blob: a6434c22fa902f515a5248dfbf72cc79c9fe64c0 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * valid.c : part of the code use to do the DTD handling and the validity
3 * checking
4 *
5 * See Copyright for the status of this software.
6 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00007 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00008 */
9
Bjorn Reese70a9da52001-04-21 16:57:29 +000010#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000011
Owen Taylor3473f882001-02-23 17:55:21 +000012#include <string.h>
13
14#ifdef HAVE_STDLIB_H
15#include <stdlib.h>
16#endif
17
18#include <libxml/xmlmemory.h>
19#include <libxml/hash.h>
20#include <libxml/valid.h>
21#include <libxml/parser.h>
22#include <libxml/parserInternals.h>
23#include <libxml/xmlerror.h>
24#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000025#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000026
Daniel Veillarde62d36c2001-05-15 08:53:16 +000027/* #define DEBUG_VALID_ALGO */
28
Owen Taylor3473f882001-02-23 17:55:21 +000029/*
30 * Generic function for accessing stacks in the Validity Context
31 */
32
33#define PUSH_AND_POP(scope, type, name) \
34scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000035 if (ctxt->name##Max <= 0) { \
36 ctxt->name##Max = 4; \
37 ctxt->name##Tab = (type *) xmlMalloc( \
38 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
39 if (ctxt->name##Tab == NULL) { \
40 xmlGenericError(xmlGenericErrorContext, \
41 "malloc failed !\n"); \
Daniel Veillarda9142e72001-06-19 11:07:54 +000042 ctxt->name##Max = 0; \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000043 return(0); \
44 } \
45 } \
Owen Taylor3473f882001-02-23 17:55:21 +000046 if (ctxt->name##Nr >= ctxt->name##Max) { \
47 ctxt->name##Max *= 2; \
48 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
49 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
50 if (ctxt->name##Tab == NULL) { \
51 xmlGenericError(xmlGenericErrorContext, \
52 "realloc failed !\n"); \
53 return(0); \
54 } \
55 } \
56 ctxt->name##Tab[ctxt->name##Nr] = value; \
57 ctxt->name = value; \
58 return(ctxt->name##Nr++); \
59} \
60scope type name##VPop(xmlValidCtxtPtr ctxt) { \
61 type ret; \
62 if (ctxt->name##Nr <= 0) return(0); \
63 ctxt->name##Nr--; \
64 if (ctxt->name##Nr > 0) \
65 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
66 else \
67 ctxt->name = NULL; \
68 ret = ctxt->name##Tab[ctxt->name##Nr]; \
69 ctxt->name##Tab[ctxt->name##Nr] = 0; \
70 return(ret); \
71} \
72
Daniel Veillarddab4cb32001-04-20 13:03:48 +000073/*
Daniel Veillardb44025c2001-10-11 22:55:55 +000074 * I use a home made algorithm less complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +000075 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000076 * only restriction is on the deepness of the tree limited by the
77 * size of the occurs bitfield
78 *
79 * this is the content of a saved state for rollbacks
80 */
81
82#define ROLLBACK_OR 0
83#define ROLLBACK_PARENT 1
84
Daniel Veillardb44025c2001-10-11 22:55:55 +000085typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +000086 xmlElementContentPtr cont; /* pointer to the content model subtree */
87 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000088 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000089 unsigned char depth; /* current depth in the overall tree */
90 unsigned char state; /* ROLLBACK_XXX */
91} _xmlValidState;
92
93#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
94#define CONT ctxt->vstate->cont
95#define NODE ctxt->vstate->node
96#define DEPTH ctxt->vstate->depth
97#define OCCURS ctxt->vstate->occurs
98#define STATE ctxt->vstate->state
99
Daniel Veillard5344c602001-12-31 16:37:34 +0000100#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
101#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000102
Daniel Veillard5344c602001-12-31 16:37:34 +0000103#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
104#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000105
106static int
107vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
108 xmlNodePtr node, unsigned char depth, long occurs,
109 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000110 int i = ctxt->vstateNr - 1;
111
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000112 if (ctxt->vstateNr >= ctxt->vstateMax) {
113 ctxt->vstateMax *= 2;
114 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
115 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
116 if (ctxt->vstateTab == NULL) {
117 xmlGenericError(xmlGenericErrorContext,
118 "realloc failed !n");
119 return(0);
120 }
Daniel Veillard06803992001-04-22 10:35:56 +0000121 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000122 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000123 /*
124 * Don't push on the stack a state already here
125 */
126 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
127 (ctxt->vstateTab[i].node == node) &&
128 (ctxt->vstateTab[i].depth == depth) &&
129 (ctxt->vstateTab[i].occurs == occurs) &&
130 (ctxt->vstateTab[i].state == state))
131 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000132 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
133 ctxt->vstateTab[ctxt->vstateNr].node = node;
134 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
135 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
136 ctxt->vstateTab[ctxt->vstateNr].state = state;
137 return(ctxt->vstateNr++);
138}
139
140static int
141vstateVPop(xmlValidCtxtPtr ctxt) {
142 if (ctxt->vstateNr <= 1) return(-1);
143 ctxt->vstateNr--;
144 ctxt->vstate = &ctxt->vstateTab[0];
145 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
146 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
147 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
148 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
149 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
150 return(ctxt->vstateNr);
151}
152
Owen Taylor3473f882001-02-23 17:55:21 +0000153PUSH_AND_POP(static, xmlNodePtr, node)
154
Owen Taylor3473f882001-02-23 17:55:21 +0000155#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000156static void
157xmlValidPrintNode(xmlNodePtr cur) {
158 if (cur == NULL) {
159 xmlGenericError(xmlGenericErrorContext, "null");
160 return;
161 }
162 switch (cur->type) {
163 case XML_ELEMENT_NODE:
164 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
165 break;
166 case XML_TEXT_NODE:
167 xmlGenericError(xmlGenericErrorContext, "text ");
168 break;
169 case XML_CDATA_SECTION_NODE:
170 xmlGenericError(xmlGenericErrorContext, "cdata ");
171 break;
172 case XML_ENTITY_REF_NODE:
173 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
174 break;
175 case XML_PI_NODE:
176 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
177 break;
178 case XML_COMMENT_NODE:
179 xmlGenericError(xmlGenericErrorContext, "comment ");
180 break;
181 case XML_ATTRIBUTE_NODE:
182 xmlGenericError(xmlGenericErrorContext, "?attr? ");
183 break;
184 case XML_ENTITY_NODE:
185 xmlGenericError(xmlGenericErrorContext, "?ent? ");
186 break;
187 case XML_DOCUMENT_NODE:
188 xmlGenericError(xmlGenericErrorContext, "?doc? ");
189 break;
190 case XML_DOCUMENT_TYPE_NODE:
191 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
192 break;
193 case XML_DOCUMENT_FRAG_NODE:
194 xmlGenericError(xmlGenericErrorContext, "?frag? ");
195 break;
196 case XML_NOTATION_NODE:
197 xmlGenericError(xmlGenericErrorContext, "?nota? ");
198 break;
199 case XML_HTML_DOCUMENT_NODE:
200 xmlGenericError(xmlGenericErrorContext, "?html? ");
201 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000202#ifdef LIBXML_DOCB_ENABLED
203 case XML_DOCB_DOCUMENT_NODE:
204 xmlGenericError(xmlGenericErrorContext, "?docb? ");
205 break;
206#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000207 case XML_DTD_NODE:
208 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
209 break;
210 case XML_ELEMENT_DECL:
211 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
212 break;
213 case XML_ATTRIBUTE_DECL:
214 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
215 break;
216 case XML_ENTITY_DECL:
217 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
218 break;
219 case XML_NAMESPACE_DECL:
220 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
221 break;
222 case XML_XINCLUDE_START:
223 xmlGenericError(xmlGenericErrorContext, "incstart ");
224 break;
225 case XML_XINCLUDE_END:
226 xmlGenericError(xmlGenericErrorContext, "incend ");
227 break;
228 }
229}
230
231static void
232xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000233 if (cur == NULL)
234 xmlGenericError(xmlGenericErrorContext, "null ");
235 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000236 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000237 cur = cur->next;
238 }
239}
240
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000241static void
242xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000243 char expr[1000];
244
245 expr[0] = 0;
246 xmlGenericError(xmlGenericErrorContext, "valid: ");
247 xmlValidPrintNodeList(cur);
248 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000249 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000250 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
251}
252
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000253static void
254xmlValidDebugState(xmlValidStatePtr state) {
255 xmlGenericError(xmlGenericErrorContext, "(");
256 if (state->cont == NULL)
257 xmlGenericError(xmlGenericErrorContext, "null,");
258 else
259 switch (state->cont->type) {
260 case XML_ELEMENT_CONTENT_PCDATA:
261 xmlGenericError(xmlGenericErrorContext, "pcdata,");
262 break;
263 case XML_ELEMENT_CONTENT_ELEMENT:
264 xmlGenericError(xmlGenericErrorContext, "%s,",
265 state->cont->name);
266 break;
267 case XML_ELEMENT_CONTENT_SEQ:
268 xmlGenericError(xmlGenericErrorContext, "seq,");
269 break;
270 case XML_ELEMENT_CONTENT_OR:
271 xmlGenericError(xmlGenericErrorContext, "or,");
272 break;
273 }
274 xmlValidPrintNode(state->node);
275 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
276 state->depth, state->occurs, state->state);
277}
278
279static void
280xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
281 int i, j;
282
283 xmlGenericError(xmlGenericErrorContext, "state: ");
284 xmlValidDebugState(ctxt->vstate);
285 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
286 ctxt->vstateNr - 1);
287 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
288 xmlValidDebugState(&ctxt->vstateTab[j]);
289 xmlGenericError(xmlGenericErrorContext, "\n");
290}
291
292/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000293#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000294 *****/
295
296#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000297#define DEBUG_VALID_MSG(m) \
298 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
299
Owen Taylor3473f882001-02-23 17:55:21 +0000300#else
301#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000302#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000303#endif
304
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000305/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000306
307#define VERROR \
308 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
309
310#define VWARNING \
311 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
312
313#define CHECK_DTD \
314 if (doc == NULL) return(0); \
315 else if ((doc->intSubset == NULL) && \
316 (doc->extSubset == NULL)) return(0)
317
318xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000319static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
320 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000321xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
322
323/************************************************************************
324 * *
325 * QName handling helper *
326 * *
327 ************************************************************************/
328
329/**
330 * xmlSplitQName2:
331 * @name: an XML parser context
332 * @prefix: a xmlChar **
333 *
334 * parse an XML qualified name string
335 *
336 * [NS 5] QName ::= (Prefix ':')? LocalPart
337 *
338 * [NS 6] Prefix ::= NCName
339 *
340 * [NS 7] LocalPart ::= NCName
341 *
342 * Returns NULL if not a QName, otherwise the local part, and prefix
343 * is updated to get the Prefix if any.
344 */
345
346xmlChar *
347xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
348 int len = 0;
349 xmlChar *ret = NULL;
350
351 *prefix = NULL;
352
Daniel Veillardf4309d72001-10-02 09:28:58 +0000353#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000354 /* xml: prefix is not really a namespace */
355 if ((name[0] == 'x') && (name[1] == 'm') &&
356 (name[2] == 'l') && (name[3] == ':'))
357 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000358#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000359
360 /* nasty but valid */
361 if (name[0] == ':')
362 return(NULL);
363
364 /*
365 * we are not trying to validate but just to cut, and yes it will
366 * work even if this is as set of UTF-8 encoded chars
367 */
368 while ((name[len] != 0) && (name[len] != ':'))
369 len++;
370
371 if (name[len] == 0)
372 return(NULL);
373
374 *prefix = xmlStrndup(name, len);
375 ret = xmlStrdup(&name[len + 1]);
376
377 return(ret);
378}
379
380/****************************************************************
381 * *
382 * Util functions for data allocation/deallocation *
383 * *
384 ****************************************************************/
385
386/**
387 * xmlNewElementContent:
388 * @name: the subelement name or NULL
389 * @type: the type of element content decl
390 *
391 * Allocate an element content structure.
392 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000393 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000394 */
395xmlElementContentPtr
396xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
397 xmlElementContentPtr ret;
398
399 switch(type) {
400 case XML_ELEMENT_CONTENT_ELEMENT:
401 if (name == NULL) {
402 xmlGenericError(xmlGenericErrorContext,
403 "xmlNewElementContent : name == NULL !\n");
404 }
405 break;
406 case XML_ELEMENT_CONTENT_PCDATA:
407 case XML_ELEMENT_CONTENT_SEQ:
408 case XML_ELEMENT_CONTENT_OR:
409 if (name != NULL) {
410 xmlGenericError(xmlGenericErrorContext,
411 "xmlNewElementContent : name != NULL !\n");
412 }
413 break;
414 default:
415 xmlGenericError(xmlGenericErrorContext,
416 "xmlNewElementContent: unknown type %d\n", type);
417 return(NULL);
418 }
419 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
420 if (ret == NULL) {
421 xmlGenericError(xmlGenericErrorContext,
422 "xmlNewElementContent : out of memory!\n");
423 return(NULL);
424 }
425 ret->type = type;
426 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000427 if (name != NULL) {
428 xmlChar *prefix = NULL;
429 ret->name = xmlSplitQName2(name, &prefix);
430 if (ret->name == NULL)
431 ret->name = xmlStrdup(name);
432 ret->prefix = prefix;
433 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000434 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000435 ret->prefix = NULL;
436 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000437 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000438 return(ret);
439}
440
441/**
442 * xmlCopyElementContent:
443 * @content: An element content pointer.
444 *
445 * Build a copy of an element content description.
446 *
447 * Returns the new xmlElementContentPtr or NULL in case of error.
448 */
449xmlElementContentPtr
450xmlCopyElementContent(xmlElementContentPtr cur) {
451 xmlElementContentPtr ret;
452
453 if (cur == NULL) return(NULL);
454 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
455 if (ret == NULL) {
456 xmlGenericError(xmlGenericErrorContext,
457 "xmlCopyElementContent : out of memory\n");
458 return(NULL);
459 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000460 if (cur->prefix != NULL)
461 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000462 ret->ocur = cur->ocur;
463 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000464 if (ret->c1 != NULL)
465 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000466 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000467 if (ret->c2 != NULL)
468 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000469 return(ret);
470}
471
472/**
473 * xmlFreeElementContent:
474 * @cur: the element content tree to free
475 *
476 * Free an element content structure. This is a recursive call !
477 */
478void
479xmlFreeElementContent(xmlElementContentPtr cur) {
480 if (cur == NULL) return;
481 switch (cur->type) {
482 case XML_ELEMENT_CONTENT_PCDATA:
483 case XML_ELEMENT_CONTENT_ELEMENT:
484 case XML_ELEMENT_CONTENT_SEQ:
485 case XML_ELEMENT_CONTENT_OR:
486 break;
487 default:
488 xmlGenericError(xmlGenericErrorContext,
489 "xmlFreeElementContent : type %d\n", cur->type);
490 return;
491 }
492 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
493 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
494 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000495 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000496 xmlFree(cur);
497}
498
499/**
500 * xmlDumpElementContent:
501 * @buf: An XML buffer
502 * @content: An element table
503 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
504 *
505 * This will dump the content of the element table as an XML DTD definition
506 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000507static void
Owen Taylor3473f882001-02-23 17:55:21 +0000508xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
509 if (content == NULL) return;
510
511 if (glob) xmlBufferWriteChar(buf, "(");
512 switch (content->type) {
513 case XML_ELEMENT_CONTENT_PCDATA:
514 xmlBufferWriteChar(buf, "#PCDATA");
515 break;
516 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000517 if (content->prefix != NULL) {
518 xmlBufferWriteCHAR(buf, content->prefix);
519 xmlBufferWriteChar(buf, ":");
520 }
Owen Taylor3473f882001-02-23 17:55:21 +0000521 xmlBufferWriteCHAR(buf, content->name);
522 break;
523 case XML_ELEMENT_CONTENT_SEQ:
524 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
525 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
526 xmlDumpElementContent(buf, content->c1, 1);
527 else
528 xmlDumpElementContent(buf, content->c1, 0);
529 xmlBufferWriteChar(buf, " , ");
530 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
531 xmlDumpElementContent(buf, content->c2, 1);
532 else
533 xmlDumpElementContent(buf, content->c2, 0);
534 break;
535 case XML_ELEMENT_CONTENT_OR:
536 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
537 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
538 xmlDumpElementContent(buf, content->c1, 1);
539 else
540 xmlDumpElementContent(buf, content->c1, 0);
541 xmlBufferWriteChar(buf, " | ");
542 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
543 xmlDumpElementContent(buf, content->c2, 1);
544 else
545 xmlDumpElementContent(buf, content->c2, 0);
546 break;
547 default:
548 xmlGenericError(xmlGenericErrorContext,
549 "xmlDumpElementContent: unknown type %d\n",
550 content->type);
551 }
552 if (glob)
553 xmlBufferWriteChar(buf, ")");
554 switch (content->ocur) {
555 case XML_ELEMENT_CONTENT_ONCE:
556 break;
557 case XML_ELEMENT_CONTENT_OPT:
558 xmlBufferWriteChar(buf, "?");
559 break;
560 case XML_ELEMENT_CONTENT_MULT:
561 xmlBufferWriteChar(buf, "*");
562 break;
563 case XML_ELEMENT_CONTENT_PLUS:
564 xmlBufferWriteChar(buf, "+");
565 break;
566 }
567}
568
569/**
570 * xmlSprintfElementContent:
571 * @buf: an output buffer
572 * @content: An element table
573 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
574 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000575 * Deprecated, unsafe, use xmlSnprintfElementContent
576 */
577void
578xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
579 xmlElementContentPtr content ATTRIBUTE_UNUSED,
580 int glob ATTRIBUTE_UNUSED) {
581}
582
583/**
584 * xmlSnprintfElementContent:
585 * @buf: an output buffer
586 * @size: the buffer size
587 * @content: An element table
588 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
589 *
Owen Taylor3473f882001-02-23 17:55:21 +0000590 * This will dump the content of the element content definition
591 * Intended just for the debug routine
592 */
593void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000594xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
595 int len;
596
Owen Taylor3473f882001-02-23 17:55:21 +0000597 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000598 len = strlen(buf);
599 if (size - len < 50) {
600 if ((size - len > 4) && (buf[len - 1] != '.'))
601 strcat(buf, " ...");
602 return;
603 }
Owen Taylor3473f882001-02-23 17:55:21 +0000604 if (glob) strcat(buf, "(");
605 switch (content->type) {
606 case XML_ELEMENT_CONTENT_PCDATA:
607 strcat(buf, "#PCDATA");
608 break;
609 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000610 if (content->prefix != NULL) {
611 if (size - len < xmlStrlen(content->prefix + 10)) {
612 strcat(buf, " ...");
613 return;
614 }
615 strcat(buf, (char *) content->prefix);
616 strcat(buf, ":");
617 }
Daniel Veillardd3d06722001-08-15 12:06:36 +0000618 if (size - len < xmlStrlen(content->name + 10)) {
619 strcat(buf, " ...");
620 return;
621 }
Owen Taylor3473f882001-02-23 17:55:21 +0000622 strcat(buf, (char *) content->name);
623 break;
624 case XML_ELEMENT_CONTENT_SEQ:
625 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
626 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000627 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000628 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000629 xmlSnprintfElementContent(buf, size, content->c1, 0);
630 len = strlen(buf);
631 if (size - len < 50) {
632 if ((size - len > 4) && (buf[len - 1] != '.'))
633 strcat(buf, " ...");
634 return;
635 }
Owen Taylor3473f882001-02-23 17:55:21 +0000636 strcat(buf, " , ");
637 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000638 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000639 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000640 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000641 break;
642 case XML_ELEMENT_CONTENT_OR:
643 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
644 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000645 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000646 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000647 xmlSnprintfElementContent(buf, size, content->c1, 0);
648 len = strlen(buf);
649 if (size - len < 50) {
650 if ((size - len > 4) && (buf[len - 1] != '.'))
651 strcat(buf, " ...");
652 return;
653 }
Owen Taylor3473f882001-02-23 17:55:21 +0000654 strcat(buf, " | ");
655 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000656 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000657 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000658 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000659 break;
660 }
661 if (glob)
662 strcat(buf, ")");
663 switch (content->ocur) {
664 case XML_ELEMENT_CONTENT_ONCE:
665 break;
666 case XML_ELEMENT_CONTENT_OPT:
667 strcat(buf, "?");
668 break;
669 case XML_ELEMENT_CONTENT_MULT:
670 strcat(buf, "*");
671 break;
672 case XML_ELEMENT_CONTENT_PLUS:
673 strcat(buf, "+");
674 break;
675 }
676}
677
678/****************************************************************
679 * *
680 * Registration of DTD declarations *
681 * *
682 ****************************************************************/
683
684/**
685 * xmlCreateElementTable:
686 *
687 * create and initialize an empty element hash table.
688 *
689 * Returns the xmlElementTablePtr just created or NULL in case of error.
690 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000691static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000692xmlCreateElementTable(void) {
693 return(xmlHashCreate(0));
694}
695
696/**
697 * xmlFreeElement:
698 * @elem: An element
699 *
700 * Deallocate the memory used by an element definition
701 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000702static void
Owen Taylor3473f882001-02-23 17:55:21 +0000703xmlFreeElement(xmlElementPtr elem) {
704 if (elem == NULL) return;
705 xmlUnlinkNode((xmlNodePtr) elem);
706 xmlFreeElementContent(elem->content);
707 if (elem->name != NULL)
708 xmlFree((xmlChar *) elem->name);
709 if (elem->prefix != NULL)
710 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000711 xmlFree(elem);
712}
713
714
715/**
716 * xmlAddElementDecl:
717 * @ctxt: the validation context
718 * @dtd: pointer to the DTD
719 * @name: the entity name
720 * @type: the element type
721 * @content: the element content tree or NULL
722 *
723 * Register a new element declaration
724 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000725 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000726 */
727xmlElementPtr
728xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
729 xmlElementTypeVal type,
730 xmlElementContentPtr content) {
731 xmlElementPtr ret;
732 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000733 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000734 xmlChar *ns, *uqname;
735
736 if (dtd == NULL) {
737 xmlGenericError(xmlGenericErrorContext,
738 "xmlAddElementDecl: dtd == NULL\n");
739 return(NULL);
740 }
741 if (name == NULL) {
742 xmlGenericError(xmlGenericErrorContext,
743 "xmlAddElementDecl: name == NULL\n");
744 return(NULL);
745 }
746 switch (type) {
747 case XML_ELEMENT_TYPE_EMPTY:
748 if (content != NULL) {
749 xmlGenericError(xmlGenericErrorContext,
750 "xmlAddElementDecl: content != NULL for EMPTY\n");
751 return(NULL);
752 }
753 break;
754 case XML_ELEMENT_TYPE_ANY:
755 if (content != NULL) {
756 xmlGenericError(xmlGenericErrorContext,
757 "xmlAddElementDecl: content != NULL for ANY\n");
758 return(NULL);
759 }
760 break;
761 case XML_ELEMENT_TYPE_MIXED:
762 if (content == NULL) {
763 xmlGenericError(xmlGenericErrorContext,
764 "xmlAddElementDecl: content == NULL for MIXED\n");
765 return(NULL);
766 }
767 break;
768 case XML_ELEMENT_TYPE_ELEMENT:
769 if (content == NULL) {
770 xmlGenericError(xmlGenericErrorContext,
771 "xmlAddElementDecl: content == NULL for ELEMENT\n");
772 return(NULL);
773 }
774 break;
775 default:
776 xmlGenericError(xmlGenericErrorContext,
777 "xmlAddElementDecl: unknown type %d\n", type);
778 return(NULL);
779 }
780
781 /*
782 * check if name is a QName
783 */
784 uqname = xmlSplitQName2(name, &ns);
785 if (uqname != NULL)
786 name = uqname;
787
788 /*
789 * Create the Element table if needed.
790 */
791 table = (xmlElementTablePtr) dtd->elements;
792 if (table == NULL) {
793 table = xmlCreateElementTable();
794 dtd->elements = (void *) table;
795 }
796 if (table == NULL) {
797 xmlGenericError(xmlGenericErrorContext,
798 "xmlAddElementDecl: Table creation failed!\n");
799 return(NULL);
800 }
801
Daniel Veillarda10efa82001-04-18 13:09:01 +0000802 /*
803 * lookup old attributes inserted on an undefined element in the
804 * internal subset.
805 */
806 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
807 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
808 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
809 oldAttributes = ret->attributes;
810 ret->attributes = NULL;
811 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
812 xmlFreeElement(ret);
813 }
Owen Taylor3473f882001-02-23 17:55:21 +0000814 }
Owen Taylor3473f882001-02-23 17:55:21 +0000815
816 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000817 * The element may already be present if one of its attribute
818 * was registered first
819 */
820 ret = xmlHashLookup2(table, name, ns);
821 if (ret != NULL) {
822 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
823 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000824 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000825 */
826 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
827 if (uqname != NULL)
828 xmlFree(uqname);
829 return(NULL);
830 }
831 } else {
832 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
833 if (ret == NULL) {
834 xmlGenericError(xmlGenericErrorContext,
835 "xmlAddElementDecl: out of memory\n");
836 return(NULL);
837 }
838 memset(ret, 0, sizeof(xmlElement));
839 ret->type = XML_ELEMENT_DECL;
840
841 /*
842 * fill the structure.
843 */
844 ret->name = xmlStrdup(name);
845 ret->prefix = ns;
846
847 /*
848 * Validity Check:
849 * Insertion must not fail
850 */
851 if (xmlHashAddEntry2(table, name, ns, ret)) {
852 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000853 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000854 */
855 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
856 xmlFreeElement(ret);
857 if (uqname != NULL)
858 xmlFree(uqname);
859 return(NULL);
860 }
861 }
862
863 /*
864 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000865 */
866 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000867 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000868 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000869
870 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000871 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +0000872 */
873 ret->parent = dtd;
874 ret->doc = dtd->doc;
875 if (dtd->last == NULL) {
876 dtd->children = dtd->last = (xmlNodePtr) ret;
877 } else {
878 dtd->last->next = (xmlNodePtr) ret;
879 ret->prev = dtd->last;
880 dtd->last = (xmlNodePtr) ret;
881 }
882 if (uqname != NULL)
883 xmlFree(uqname);
884 return(ret);
885}
886
887/**
888 * xmlFreeElementTable:
889 * @table: An element table
890 *
891 * Deallocate the memory used by an element hash table.
892 */
893void
894xmlFreeElementTable(xmlElementTablePtr table) {
895 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
896}
897
898/**
899 * xmlCopyElement:
900 * @elem: An element
901 *
902 * Build a copy of an element.
903 *
904 * Returns the new xmlElementPtr or NULL in case of error.
905 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000906static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000907xmlCopyElement(xmlElementPtr elem) {
908 xmlElementPtr cur;
909
910 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
911 if (cur == NULL) {
912 xmlGenericError(xmlGenericErrorContext,
913 "xmlCopyElement: out of memory !\n");
914 return(NULL);
915 }
916 memset(cur, 0, sizeof(xmlElement));
917 cur->type = XML_ELEMENT_DECL;
918 cur->etype = elem->etype;
919 if (elem->name != NULL)
920 cur->name = xmlStrdup(elem->name);
921 else
922 cur->name = NULL;
923 if (elem->prefix != NULL)
924 cur->prefix = xmlStrdup(elem->prefix);
925 else
926 cur->prefix = NULL;
927 cur->content = xmlCopyElementContent(elem->content);
928 /* TODO : rebuild the attribute list on the copy */
929 cur->attributes = NULL;
930 return(cur);
931}
932
933/**
934 * xmlCopyElementTable:
935 * @table: An element table
936 *
937 * Build a copy of an element table.
938 *
939 * Returns the new xmlElementTablePtr or NULL in case of error.
940 */
941xmlElementTablePtr
942xmlCopyElementTable(xmlElementTablePtr table) {
943 return((xmlElementTablePtr) xmlHashCopy(table,
944 (xmlHashCopier) xmlCopyElement));
945}
946
947/**
948 * xmlDumpElementDecl:
949 * @buf: the XML buffer output
950 * @elem: An element table
951 *
952 * This will dump the content of the element declaration as an XML
953 * DTD definition
954 */
955void
956xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
957 switch (elem->etype) {
958 case XML_ELEMENT_TYPE_EMPTY:
959 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000960 if (elem->prefix != NULL) {
961 xmlBufferWriteCHAR(buf, elem->prefix);
962 xmlBufferWriteChar(buf, ":");
963 }
Owen Taylor3473f882001-02-23 17:55:21 +0000964 xmlBufferWriteCHAR(buf, elem->name);
965 xmlBufferWriteChar(buf, " EMPTY>\n");
966 break;
967 case XML_ELEMENT_TYPE_ANY:
968 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000969 if (elem->prefix != NULL) {
970 xmlBufferWriteCHAR(buf, elem->prefix);
971 xmlBufferWriteChar(buf, ":");
972 }
Owen Taylor3473f882001-02-23 17:55:21 +0000973 xmlBufferWriteCHAR(buf, elem->name);
974 xmlBufferWriteChar(buf, " ANY>\n");
975 break;
976 case XML_ELEMENT_TYPE_MIXED:
977 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000978 if (elem->prefix != NULL) {
979 xmlBufferWriteCHAR(buf, elem->prefix);
980 xmlBufferWriteChar(buf, ":");
981 }
Owen Taylor3473f882001-02-23 17:55:21 +0000982 xmlBufferWriteCHAR(buf, elem->name);
983 xmlBufferWriteChar(buf, " ");
984 xmlDumpElementContent(buf, elem->content, 1);
985 xmlBufferWriteChar(buf, ">\n");
986 break;
987 case XML_ELEMENT_TYPE_ELEMENT:
988 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000989 if (elem->prefix != NULL) {
990 xmlBufferWriteCHAR(buf, elem->prefix);
991 xmlBufferWriteChar(buf, ":");
992 }
Owen Taylor3473f882001-02-23 17:55:21 +0000993 xmlBufferWriteCHAR(buf, elem->name);
994 xmlBufferWriteChar(buf, " ");
995 xmlDumpElementContent(buf, elem->content, 1);
996 xmlBufferWriteChar(buf, ">\n");
997 break;
998 default:
999 xmlGenericError(xmlGenericErrorContext,
1000 "xmlDumpElementDecl: internal: unknown type %d\n",
1001 elem->etype);
1002 }
1003}
1004
1005/**
1006 * xmlDumpElementTable:
1007 * @buf: the XML buffer output
1008 * @table: An element table
1009 *
1010 * This will dump the content of the element table as an XML DTD definition
1011 */
1012void
1013xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1014 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1015}
1016
1017/**
1018 * xmlCreateEnumeration:
1019 * @name: the enumeration name or NULL
1020 *
1021 * create and initialize an enumeration attribute node.
1022 *
1023 * Returns the xmlEnumerationPtr just created or NULL in case
1024 * of error.
1025 */
1026xmlEnumerationPtr
1027xmlCreateEnumeration(xmlChar *name) {
1028 xmlEnumerationPtr ret;
1029
1030 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1031 if (ret == NULL) {
1032 xmlGenericError(xmlGenericErrorContext,
1033 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1034 (long)sizeof(xmlEnumeration));
1035 return(NULL);
1036 }
1037 memset(ret, 0, sizeof(xmlEnumeration));
1038
1039 if (name != NULL)
1040 ret->name = xmlStrdup(name);
1041 return(ret);
1042}
1043
1044/**
1045 * xmlFreeEnumeration:
1046 * @cur: the tree to free.
1047 *
1048 * free an enumeration attribute node (recursive).
1049 */
1050void
1051xmlFreeEnumeration(xmlEnumerationPtr cur) {
1052 if (cur == NULL) return;
1053
1054 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1055
1056 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001057 xmlFree(cur);
1058}
1059
1060/**
1061 * xmlCopyEnumeration:
1062 * @cur: the tree to copy.
1063 *
1064 * Copy an enumeration attribute node (recursive).
1065 *
1066 * Returns the xmlEnumerationPtr just created or NULL in case
1067 * of error.
1068 */
1069xmlEnumerationPtr
1070xmlCopyEnumeration(xmlEnumerationPtr cur) {
1071 xmlEnumerationPtr ret;
1072
1073 if (cur == NULL) return(NULL);
1074 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1075
1076 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1077 else ret->next = NULL;
1078
1079 return(ret);
1080}
1081
1082/**
1083 * xmlDumpEnumeration:
1084 * @buf: the XML buffer output
1085 * @enum: An enumeration
1086 *
1087 * This will dump the content of the enumeration
1088 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001089static void
Owen Taylor3473f882001-02-23 17:55:21 +00001090xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1091 if (cur == NULL) return;
1092
1093 xmlBufferWriteCHAR(buf, cur->name);
1094 if (cur->next == NULL)
1095 xmlBufferWriteChar(buf, ")");
1096 else {
1097 xmlBufferWriteChar(buf, " | ");
1098 xmlDumpEnumeration(buf, cur->next);
1099 }
1100}
1101
1102/**
1103 * xmlCreateAttributeTable:
1104 *
1105 * create and initialize an empty attribute hash table.
1106 *
1107 * Returns the xmlAttributeTablePtr just created or NULL in case
1108 * of error.
1109 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001110static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001111xmlCreateAttributeTable(void) {
1112 return(xmlHashCreate(0));
1113}
1114
1115/**
1116 * xmlScanAttributeDeclCallback:
1117 * @attr: the attribute decl
1118 * @list: the list to update
1119 *
1120 * Callback called by xmlScanAttributeDecl when a new attribute
1121 * has to be entered in the list.
1122 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001123static void
Owen Taylor3473f882001-02-23 17:55:21 +00001124xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001125 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001126 attr->nexth = *list;
1127 *list = attr;
1128}
1129
1130/**
1131 * xmlScanAttributeDecl:
1132 * @dtd: pointer to the DTD
1133 * @elem: the element name
1134 *
1135 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001136 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001137 *
1138 * Returns the pointer to the first attribute decl in the chain,
1139 * possibly NULL.
1140 */
1141xmlAttributePtr
1142xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1143 xmlAttributePtr ret = NULL;
1144 xmlAttributeTablePtr table;
1145
1146 if (dtd == NULL) {
1147 xmlGenericError(xmlGenericErrorContext,
1148 "xmlScanAttributeDecl: dtd == NULL\n");
1149 return(NULL);
1150 }
1151 if (elem == NULL) {
1152 xmlGenericError(xmlGenericErrorContext,
1153 "xmlScanAttributeDecl: elem == NULL\n");
1154 return(NULL);
1155 }
1156 table = (xmlAttributeTablePtr) dtd->attributes;
1157 if (table == NULL)
1158 return(NULL);
1159
1160 /* WRONG !!! */
1161 xmlHashScan3(table, NULL, NULL, elem,
1162 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1163 return(ret);
1164}
1165
1166/**
1167 * xmlScanIDAttributeDecl:
1168 * @ctxt: the validation context
1169 * @elem: the element name
1170 *
1171 * Verify that the element don't have too many ID attributes
1172 * declared.
1173 *
1174 * Returns the number of ID attributes found.
1175 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001176static int
Owen Taylor3473f882001-02-23 17:55:21 +00001177xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1178 xmlAttributePtr cur;
1179 int ret = 0;
1180
1181 if (elem == NULL) return(0);
1182 cur = elem->attributes;
1183 while (cur != NULL) {
1184 if (cur->atype == XML_ATTRIBUTE_ID) {
1185 ret ++;
1186 if (ret > 1)
1187 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001188 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001189 elem->name, cur->name);
1190 }
1191 cur = cur->nexth;
1192 }
1193 return(ret);
1194}
1195
1196/**
1197 * xmlFreeAttribute:
1198 * @elem: An attribute
1199 *
1200 * Deallocate the memory used by an attribute definition
1201 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001202static void
Owen Taylor3473f882001-02-23 17:55:21 +00001203xmlFreeAttribute(xmlAttributePtr attr) {
1204 if (attr == NULL) return;
1205 xmlUnlinkNode((xmlNodePtr) attr);
1206 if (attr->tree != NULL)
1207 xmlFreeEnumeration(attr->tree);
1208 if (attr->elem != NULL)
1209 xmlFree((xmlChar *) attr->elem);
1210 if (attr->name != NULL)
1211 xmlFree((xmlChar *) attr->name);
1212 if (attr->defaultValue != NULL)
1213 xmlFree((xmlChar *) attr->defaultValue);
1214 if (attr->prefix != NULL)
1215 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001216 xmlFree(attr);
1217}
1218
1219
1220/**
1221 * xmlAddAttributeDecl:
1222 * @ctxt: the validation context
1223 * @dtd: pointer to the DTD
1224 * @elem: the element name
1225 * @name: the attribute name
1226 * @ns: the attribute namespace prefix
1227 * @type: the attribute type
1228 * @def: the attribute default type
1229 * @defaultValue: the attribute default value
1230 * @tree: if it's an enumeration, the associated list
1231 *
1232 * Register a new attribute declaration
1233 * Note that @tree becomes the ownership of the DTD
1234 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001235 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001236 */
1237xmlAttributePtr
1238xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1239 const xmlChar *name, const xmlChar *ns,
1240 xmlAttributeType type, xmlAttributeDefault def,
1241 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1242 xmlAttributePtr ret;
1243 xmlAttributeTablePtr table;
1244 xmlElementPtr elemDef;
1245
1246 if (dtd == NULL) {
1247 xmlGenericError(xmlGenericErrorContext,
1248 "xmlAddAttributeDecl: dtd == NULL\n");
1249 xmlFreeEnumeration(tree);
1250 return(NULL);
1251 }
1252 if (name == NULL) {
1253 xmlGenericError(xmlGenericErrorContext,
1254 "xmlAddAttributeDecl: name == NULL\n");
1255 xmlFreeEnumeration(tree);
1256 return(NULL);
1257 }
1258 if (elem == NULL) {
1259 xmlGenericError(xmlGenericErrorContext,
1260 "xmlAddAttributeDecl: elem == NULL\n");
1261 xmlFreeEnumeration(tree);
1262 return(NULL);
1263 }
1264 /*
1265 * Check the type and possibly the default value.
1266 */
1267 switch (type) {
1268 case XML_ATTRIBUTE_CDATA:
1269 break;
1270 case XML_ATTRIBUTE_ID:
1271 break;
1272 case XML_ATTRIBUTE_IDREF:
1273 break;
1274 case XML_ATTRIBUTE_IDREFS:
1275 break;
1276 case XML_ATTRIBUTE_ENTITY:
1277 break;
1278 case XML_ATTRIBUTE_ENTITIES:
1279 break;
1280 case XML_ATTRIBUTE_NMTOKEN:
1281 break;
1282 case XML_ATTRIBUTE_NMTOKENS:
1283 break;
1284 case XML_ATTRIBUTE_ENUMERATION:
1285 break;
1286 case XML_ATTRIBUTE_NOTATION:
1287 break;
1288 default:
1289 xmlGenericError(xmlGenericErrorContext,
1290 "xmlAddAttributeDecl: unknown type %d\n", type);
1291 xmlFreeEnumeration(tree);
1292 return(NULL);
1293 }
1294 if ((defaultValue != NULL) &&
1295 (!xmlValidateAttributeValue(type, defaultValue))) {
1296 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1297 elem, name, defaultValue);
1298 defaultValue = NULL;
1299 }
1300
1301 /*
1302 * Create the Attribute table if needed.
1303 */
1304 table = (xmlAttributeTablePtr) dtd->attributes;
1305 if (table == NULL) {
1306 table = xmlCreateAttributeTable();
1307 dtd->attributes = (void *) table;
1308 }
1309 if (table == NULL) {
1310 xmlGenericError(xmlGenericErrorContext,
1311 "xmlAddAttributeDecl: Table creation failed!\n");
1312 return(NULL);
1313 }
1314
1315
1316 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1317 if (ret == NULL) {
1318 xmlGenericError(xmlGenericErrorContext,
1319 "xmlAddAttributeDecl: out of memory\n");
1320 return(NULL);
1321 }
1322 memset(ret, 0, sizeof(xmlAttribute));
1323 ret->type = XML_ATTRIBUTE_DECL;
1324
1325 /*
1326 * fill the structure.
1327 */
1328 ret->atype = type;
1329 ret->name = xmlStrdup(name);
1330 ret->prefix = xmlStrdup(ns);
1331 ret->elem = xmlStrdup(elem);
1332 ret->def = def;
1333 ret->tree = tree;
1334 if (defaultValue != NULL)
1335 ret->defaultValue = xmlStrdup(defaultValue);
1336
1337 /*
1338 * Validity Check:
1339 * Search the DTD for previous declarations of the ATTLIST
1340 */
1341 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1342 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001343 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001344 */
1345 VWARNING(ctxt->userData,
1346 "Attribute %s on %s: already defined\n",
1347 name, elem);
1348 xmlFreeAttribute(ret);
1349 return(NULL);
1350 }
1351
1352 /*
1353 * Validity Check:
1354 * Multiple ID per element
1355 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001356 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001357 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001358
Owen Taylor3473f882001-02-23 17:55:21 +00001359 if ((type == XML_ATTRIBUTE_ID) &&
1360 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1361 VERROR(ctxt->userData,
1362 "Element %s has too may ID attributes defined : %s\n",
1363 elem, name);
Daniel Veillard48da9102001-08-07 01:10:10 +00001364 /*
1365 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001366 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001367 */
1368 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1369 ((ret->prefix != NULL &&
1370 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1371 ret->nexth = elemDef->attributes;
1372 elemDef->attributes = ret;
1373 } else {
1374 xmlAttributePtr tmp = elemDef->attributes;
1375
1376 while ((tmp != NULL) &&
1377 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1378 ((ret->prefix != NULL &&
1379 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1380 if (tmp->nexth == NULL)
1381 break;
1382 tmp = tmp->nexth;
1383 }
1384 if (tmp != NULL) {
1385 ret->nexth = tmp->nexth;
1386 tmp->nexth = ret;
1387 } else {
1388 ret->nexth = elemDef->attributes;
1389 elemDef->attributes = ret;
1390 }
1391 }
Owen Taylor3473f882001-02-23 17:55:21 +00001392 }
1393
1394 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001395 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001396 */
1397 ret->parent = dtd;
1398 ret->doc = dtd->doc;
1399 if (dtd->last == NULL) {
1400 dtd->children = dtd->last = (xmlNodePtr) ret;
1401 } else {
1402 dtd->last->next = (xmlNodePtr) ret;
1403 ret->prev = dtd->last;
1404 dtd->last = (xmlNodePtr) ret;
1405 }
1406 return(ret);
1407}
1408
1409/**
1410 * xmlFreeAttributeTable:
1411 * @table: An attribute table
1412 *
1413 * Deallocate the memory used by an entities hash table.
1414 */
1415void
1416xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1417 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1418}
1419
1420/**
1421 * xmlCopyAttribute:
1422 * @attr: An attribute
1423 *
1424 * Build a copy of an attribute.
1425 *
1426 * Returns the new xmlAttributePtr or NULL in case of error.
1427 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001428static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001429xmlCopyAttribute(xmlAttributePtr attr) {
1430 xmlAttributePtr cur;
1431
1432 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1433 if (cur == NULL) {
1434 xmlGenericError(xmlGenericErrorContext,
1435 "xmlCopyAttribute: out of memory !\n");
1436 return(NULL);
1437 }
1438 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001439 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001440 cur->atype = attr->atype;
1441 cur->def = attr->def;
1442 cur->tree = xmlCopyEnumeration(attr->tree);
1443 if (attr->elem != NULL)
1444 cur->elem = xmlStrdup(attr->elem);
1445 if (attr->name != NULL)
1446 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001447 if (attr->prefix != NULL)
1448 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001449 if (attr->defaultValue != NULL)
1450 cur->defaultValue = xmlStrdup(attr->defaultValue);
1451 return(cur);
1452}
1453
1454/**
1455 * xmlCopyAttributeTable:
1456 * @table: An attribute table
1457 *
1458 * Build a copy of an attribute table.
1459 *
1460 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1461 */
1462xmlAttributeTablePtr
1463xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1464 return((xmlAttributeTablePtr) xmlHashCopy(table,
1465 (xmlHashCopier) xmlCopyAttribute));
1466}
1467
1468/**
1469 * xmlDumpAttributeDecl:
1470 * @buf: the XML buffer output
1471 * @attr: An attribute declaration
1472 *
1473 * This will dump the content of the attribute declaration as an XML
1474 * DTD definition
1475 */
1476void
1477xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1478 xmlBufferWriteChar(buf, "<!ATTLIST ");
1479 xmlBufferWriteCHAR(buf, attr->elem);
1480 xmlBufferWriteChar(buf, " ");
1481 if (attr->prefix != NULL) {
1482 xmlBufferWriteCHAR(buf, attr->prefix);
1483 xmlBufferWriteChar(buf, ":");
1484 }
1485 xmlBufferWriteCHAR(buf, attr->name);
1486 switch (attr->atype) {
1487 case XML_ATTRIBUTE_CDATA:
1488 xmlBufferWriteChar(buf, " CDATA");
1489 break;
1490 case XML_ATTRIBUTE_ID:
1491 xmlBufferWriteChar(buf, " ID");
1492 break;
1493 case XML_ATTRIBUTE_IDREF:
1494 xmlBufferWriteChar(buf, " IDREF");
1495 break;
1496 case XML_ATTRIBUTE_IDREFS:
1497 xmlBufferWriteChar(buf, " IDREFS");
1498 break;
1499 case XML_ATTRIBUTE_ENTITY:
1500 xmlBufferWriteChar(buf, " ENTITY");
1501 break;
1502 case XML_ATTRIBUTE_ENTITIES:
1503 xmlBufferWriteChar(buf, " ENTITIES");
1504 break;
1505 case XML_ATTRIBUTE_NMTOKEN:
1506 xmlBufferWriteChar(buf, " NMTOKEN");
1507 break;
1508 case XML_ATTRIBUTE_NMTOKENS:
1509 xmlBufferWriteChar(buf, " NMTOKENS");
1510 break;
1511 case XML_ATTRIBUTE_ENUMERATION:
1512 xmlBufferWriteChar(buf, " (");
1513 xmlDumpEnumeration(buf, attr->tree);
1514 break;
1515 case XML_ATTRIBUTE_NOTATION:
1516 xmlBufferWriteChar(buf, " NOTATION (");
1517 xmlDumpEnumeration(buf, attr->tree);
1518 break;
1519 default:
1520 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001521 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001522 attr->atype);
1523 }
1524 switch (attr->def) {
1525 case XML_ATTRIBUTE_NONE:
1526 break;
1527 case XML_ATTRIBUTE_REQUIRED:
1528 xmlBufferWriteChar(buf, " #REQUIRED");
1529 break;
1530 case XML_ATTRIBUTE_IMPLIED:
1531 xmlBufferWriteChar(buf, " #IMPLIED");
1532 break;
1533 case XML_ATTRIBUTE_FIXED:
1534 xmlBufferWriteChar(buf, " #FIXED");
1535 break;
1536 default:
1537 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001538 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001539 attr->def);
1540 }
1541 if (attr->defaultValue != NULL) {
1542 xmlBufferWriteChar(buf, " ");
1543 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1544 }
1545 xmlBufferWriteChar(buf, ">\n");
1546}
1547
1548/**
1549 * xmlDumpAttributeTable:
1550 * @buf: the XML buffer output
1551 * @table: An attribute table
1552 *
1553 * This will dump the content of the attribute table as an XML DTD definition
1554 */
1555void
1556xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1557 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1558}
1559
1560/************************************************************************
1561 * *
1562 * NOTATIONs *
1563 * *
1564 ************************************************************************/
1565/**
1566 * xmlCreateNotationTable:
1567 *
1568 * create and initialize an empty notation hash table.
1569 *
1570 * Returns the xmlNotationTablePtr just created or NULL in case
1571 * of error.
1572 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001573static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001574xmlCreateNotationTable(void) {
1575 return(xmlHashCreate(0));
1576}
1577
1578/**
1579 * xmlFreeNotation:
1580 * @not: A notation
1581 *
1582 * Deallocate the memory used by an notation definition
1583 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001584static void
Owen Taylor3473f882001-02-23 17:55:21 +00001585xmlFreeNotation(xmlNotationPtr nota) {
1586 if (nota == NULL) return;
1587 if (nota->name != NULL)
1588 xmlFree((xmlChar *) nota->name);
1589 if (nota->PublicID != NULL)
1590 xmlFree((xmlChar *) nota->PublicID);
1591 if (nota->SystemID != NULL)
1592 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001593 xmlFree(nota);
1594}
1595
1596
1597/**
1598 * xmlAddNotationDecl:
1599 * @dtd: pointer to the DTD
1600 * @ctxt: the validation context
1601 * @name: the entity name
1602 * @PublicID: the public identifier or NULL
1603 * @SystemID: the system identifier or NULL
1604 *
1605 * Register a new notation declaration
1606 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001607 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001608 */
1609xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001610xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001611 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001612 const xmlChar *PublicID, const xmlChar *SystemID) {
1613 xmlNotationPtr ret;
1614 xmlNotationTablePtr table;
1615
1616 if (dtd == NULL) {
1617 xmlGenericError(xmlGenericErrorContext,
1618 "xmlAddNotationDecl: dtd == NULL\n");
1619 return(NULL);
1620 }
1621 if (name == NULL) {
1622 xmlGenericError(xmlGenericErrorContext,
1623 "xmlAddNotationDecl: name == NULL\n");
1624 return(NULL);
1625 }
1626 if ((PublicID == NULL) && (SystemID == NULL)) {
1627 xmlGenericError(xmlGenericErrorContext,
1628 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1629 }
1630
1631 /*
1632 * Create the Notation table if needed.
1633 */
1634 table = (xmlNotationTablePtr) dtd->notations;
1635 if (table == NULL)
1636 dtd->notations = table = xmlCreateNotationTable();
1637 if (table == NULL) {
1638 xmlGenericError(xmlGenericErrorContext,
1639 "xmlAddNotationDecl: Table creation failed!\n");
1640 return(NULL);
1641 }
1642
1643 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1644 if (ret == NULL) {
1645 xmlGenericError(xmlGenericErrorContext,
1646 "xmlAddNotationDecl: out of memory\n");
1647 return(NULL);
1648 }
1649 memset(ret, 0, sizeof(xmlNotation));
1650
1651 /*
1652 * fill the structure.
1653 */
1654 ret->name = xmlStrdup(name);
1655 if (SystemID != NULL)
1656 ret->SystemID = xmlStrdup(SystemID);
1657 if (PublicID != NULL)
1658 ret->PublicID = xmlStrdup(PublicID);
1659
1660 /*
1661 * Validity Check:
1662 * Check the DTD for previous declarations of the ATTLIST
1663 */
1664 if (xmlHashAddEntry(table, name, ret)) {
1665 xmlGenericError(xmlGenericErrorContext,
1666 "xmlAddNotationDecl: %s already defined\n", name);
1667 xmlFreeNotation(ret);
1668 return(NULL);
1669 }
1670 return(ret);
1671}
1672
1673/**
1674 * xmlFreeNotationTable:
1675 * @table: An notation table
1676 *
1677 * Deallocate the memory used by an entities hash table.
1678 */
1679void
1680xmlFreeNotationTable(xmlNotationTablePtr table) {
1681 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1682}
1683
1684/**
1685 * xmlCopyNotation:
1686 * @nota: A notation
1687 *
1688 * Build a copy of a notation.
1689 *
1690 * Returns the new xmlNotationPtr or NULL in case of error.
1691 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001692static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001693xmlCopyNotation(xmlNotationPtr nota) {
1694 xmlNotationPtr cur;
1695
1696 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1697 if (cur == NULL) {
1698 xmlGenericError(xmlGenericErrorContext,
1699 "xmlCopyNotation: out of memory !\n");
1700 return(NULL);
1701 }
1702 if (nota->name != NULL)
1703 cur->name = xmlStrdup(nota->name);
1704 else
1705 cur->name = NULL;
1706 if (nota->PublicID != NULL)
1707 cur->PublicID = xmlStrdup(nota->PublicID);
1708 else
1709 cur->PublicID = NULL;
1710 if (nota->SystemID != NULL)
1711 cur->SystemID = xmlStrdup(nota->SystemID);
1712 else
1713 cur->SystemID = NULL;
1714 return(cur);
1715}
1716
1717/**
1718 * xmlCopyNotationTable:
1719 * @table: A notation table
1720 *
1721 * Build a copy of a notation table.
1722 *
1723 * Returns the new xmlNotationTablePtr or NULL in case of error.
1724 */
1725xmlNotationTablePtr
1726xmlCopyNotationTable(xmlNotationTablePtr table) {
1727 return((xmlNotationTablePtr) xmlHashCopy(table,
1728 (xmlHashCopier) xmlCopyNotation));
1729}
1730
1731/**
1732 * xmlDumpNotationDecl:
1733 * @buf: the XML buffer output
1734 * @nota: A notation declaration
1735 *
1736 * This will dump the content the notation declaration as an XML DTD definition
1737 */
1738void
1739xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1740 xmlBufferWriteChar(buf, "<!NOTATION ");
1741 xmlBufferWriteCHAR(buf, nota->name);
1742 if (nota->PublicID != NULL) {
1743 xmlBufferWriteChar(buf, " PUBLIC ");
1744 xmlBufferWriteQuotedString(buf, nota->PublicID);
1745 if (nota->SystemID != NULL) {
1746 xmlBufferWriteChar(buf, " ");
1747 xmlBufferWriteCHAR(buf, nota->SystemID);
1748 }
1749 } else {
1750 xmlBufferWriteChar(buf, " SYSTEM ");
1751 xmlBufferWriteCHAR(buf, nota->SystemID);
1752 }
1753 xmlBufferWriteChar(buf, " >\n");
1754}
1755
1756/**
1757 * xmlDumpNotationTable:
1758 * @buf: the XML buffer output
1759 * @table: A notation table
1760 *
1761 * This will dump the content of the notation table as an XML DTD definition
1762 */
1763void
1764xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1765 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1766}
1767
1768/************************************************************************
1769 * *
1770 * IDs *
1771 * *
1772 ************************************************************************/
1773/**
1774 * xmlCreateIDTable:
1775 *
1776 * create and initialize an empty id hash table.
1777 *
1778 * Returns the xmlIDTablePtr just created or NULL in case
1779 * of error.
1780 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001781static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001782xmlCreateIDTable(void) {
1783 return(xmlHashCreate(0));
1784}
1785
1786/**
1787 * xmlFreeID:
1788 * @not: A id
1789 *
1790 * Deallocate the memory used by an id definition
1791 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001792static void
Owen Taylor3473f882001-02-23 17:55:21 +00001793xmlFreeID(xmlIDPtr id) {
1794 if (id == NULL) return;
1795 if (id->value != NULL)
1796 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001797 xmlFree(id);
1798}
1799
1800/**
1801 * xmlAddID:
1802 * @ctxt: the validation context
1803 * @doc: pointer to the document
1804 * @value: the value name
1805 * @attr: the attribute holding the ID
1806 *
1807 * Register a new id declaration
1808 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001809 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001810 */
1811xmlIDPtr
1812xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1813 xmlAttrPtr attr) {
1814 xmlIDPtr ret;
1815 xmlIDTablePtr table;
1816
1817 if (doc == NULL) {
1818 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001819 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001820 return(NULL);
1821 }
1822 if (value == NULL) {
1823 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001824 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001825 return(NULL);
1826 }
1827 if (attr == NULL) {
1828 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001829 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001830 return(NULL);
1831 }
1832
1833 /*
1834 * Create the ID table if needed.
1835 */
1836 table = (xmlIDTablePtr) doc->ids;
1837 if (table == NULL)
1838 doc->ids = table = xmlCreateIDTable();
1839 if (table == NULL) {
1840 xmlGenericError(xmlGenericErrorContext,
1841 "xmlAddID: Table creation failed!\n");
1842 return(NULL);
1843 }
1844
1845 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1846 if (ret == NULL) {
1847 xmlGenericError(xmlGenericErrorContext,
1848 "xmlAddID: out of memory\n");
1849 return(NULL);
1850 }
1851
1852 /*
1853 * fill the structure.
1854 */
1855 ret->value = xmlStrdup(value);
1856 ret->attr = attr;
1857
1858 if (xmlHashAddEntry(table, value, ret) < 0) {
1859 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001860 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001861 */
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001862 if (ctxt != NULL)
1863 VERROR(ctxt->userData, "ID %s already defined\n", value);
Owen Taylor3473f882001-02-23 17:55:21 +00001864 xmlFreeID(ret);
1865 return(NULL);
1866 }
1867 return(ret);
1868}
1869
1870/**
1871 * xmlFreeIDTable:
1872 * @table: An id table
1873 *
1874 * Deallocate the memory used by an ID hash table.
1875 */
1876void
1877xmlFreeIDTable(xmlIDTablePtr table) {
1878 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1879}
1880
1881/**
1882 * xmlIsID:
1883 * @doc: the document
1884 * @elem: the element carrying the attribute
1885 * @attr: the attribute
1886 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001887 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001888 * then this is simple, otherwise we use an heuristic: name ID (upper
1889 * or lowercase).
1890 *
1891 * Returns 0 or 1 depending on the lookup result
1892 */
1893int
1894xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1895 if (doc == NULL) return(0);
1896 if (attr == NULL) return(0);
1897 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1898 return(0);
1899 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1900 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1901 (xmlStrEqual(BAD_CAST "name", attr->name)))
1902 return(1);
1903 return(0);
1904 } else {
1905 xmlAttributePtr attrDecl;
1906
1907 if (elem == NULL) return(0);
1908 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1909 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1910 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1911 attr->name);
1912
1913 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1914 return(1);
1915 }
1916 return(0);
1917}
1918
1919/**
1920 * xmlRemoveID
1921 * @doc: the document
1922 * @attr: the attribute
1923 *
1924 * Remove the given attribute from the ID table maintained internally.
1925 *
1926 * Returns -1 if the lookup failed and 0 otherwise
1927 */
1928int
1929xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1930 xmlAttrPtr cur;
1931 xmlIDTablePtr table;
1932 xmlChar *ID;
1933
1934 if (doc == NULL) return(-1);
1935 if (attr == NULL) return(-1);
1936 table = (xmlIDTablePtr) doc->ids;
1937 if (table == NULL)
1938 return(-1);
1939
1940 if (attr == NULL)
1941 return(-1);
1942 ID = xmlNodeListGetString(doc, attr->children, 1);
1943 if (ID == NULL)
1944 return(-1);
1945 cur = xmlHashLookup(table, ID);
1946 if (cur != attr) {
1947 xmlFree(ID);
1948 return(-1);
1949 }
1950 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1951 xmlFree(ID);
1952 return(0);
1953}
1954
1955/**
1956 * xmlGetID:
1957 * @doc: pointer to the document
1958 * @ID: the ID value
1959 *
1960 * Search the attribute declaring the given ID
1961 *
1962 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1963 */
1964xmlAttrPtr
1965xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1966 xmlIDTablePtr table;
1967 xmlIDPtr id;
1968
1969 if (doc == NULL) {
1970 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1971 return(NULL);
1972 }
1973
1974 if (ID == NULL) {
1975 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1976 return(NULL);
1977 }
1978
1979 table = (xmlIDTablePtr) doc->ids;
1980 if (table == NULL)
1981 return(NULL);
1982
1983 id = xmlHashLookup(table, ID);
1984 if (id == NULL)
1985 return(NULL);
1986 return(id->attr);
1987}
1988
1989/************************************************************************
1990 * *
1991 * Refs *
1992 * *
1993 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001994typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001995{
1996 xmlListPtr l;
1997 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001998} xmlRemoveMemo;
1999
2000typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2001
2002typedef struct xmlValidateMemo_t
2003{
2004 xmlValidCtxtPtr ctxt;
2005 const xmlChar *name;
2006} xmlValidateMemo;
2007
2008typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002009
2010/**
2011 * xmlCreateRefTable:
2012 *
2013 * create and initialize an empty ref hash table.
2014 *
2015 * Returns the xmlRefTablePtr just created or NULL in case
2016 * of error.
2017 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002018static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002019xmlCreateRefTable(void) {
2020 return(xmlHashCreate(0));
2021}
2022
2023/**
2024 * xmlFreeRef:
2025 * @lk: A list link
2026 *
2027 * Deallocate the memory used by a ref definition
2028 */
2029static void
2030xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002031 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2032 if (ref == NULL) return;
2033 if (ref->value != NULL)
2034 xmlFree((xmlChar *)ref->value);
2035 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002036}
2037
2038/**
2039 * xmlFreeRefList:
2040 * @list_ref: A list of references.
2041 *
2042 * Deallocate the memory used by a list of references
2043 */
2044static void
2045xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002046 if (list_ref == NULL) return;
2047 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002048}
2049
2050/**
2051 * xmlWalkRemoveRef:
2052 * @data: Contents of current link
2053 * @user: Value supplied by the user
2054 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002055 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002056 */
2057static int
2058xmlWalkRemoveRef(const void *data, const void *user)
2059{
Daniel Veillard37721922001-05-04 15:21:12 +00002060 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2061 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2062 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002063
Daniel Veillard37721922001-05-04 15:21:12 +00002064 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2065 xmlListRemoveFirst(ref_list, (void *)data);
2066 return 0;
2067 }
2068 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002069}
2070
2071/**
2072 * xmlAddRef:
2073 * @ctxt: the validation context
2074 * @doc: pointer to the document
2075 * @value: the value name
2076 * @attr: the attribute holding the Ref
2077 *
2078 * Register a new ref declaration
2079 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002080 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002081 */
2082xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002083xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002084 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002085 xmlRefPtr ret;
2086 xmlRefTablePtr table;
2087 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002088
Daniel Veillard37721922001-05-04 15:21:12 +00002089 if (doc == NULL) {
2090 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002091 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002092 return(NULL);
2093 }
2094 if (value == NULL) {
2095 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002096 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002097 return(NULL);
2098 }
2099 if (attr == NULL) {
2100 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002101 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002102 return(NULL);
2103 }
Owen Taylor3473f882001-02-23 17:55:21 +00002104
Daniel Veillard37721922001-05-04 15:21:12 +00002105 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002106 * Create the Ref table if needed.
2107 */
Daniel Veillard37721922001-05-04 15:21:12 +00002108 table = (xmlRefTablePtr) doc->refs;
2109 if (table == NULL)
2110 doc->refs = table = xmlCreateRefTable();
2111 if (table == NULL) {
2112 xmlGenericError(xmlGenericErrorContext,
2113 "xmlAddRef: Table creation failed!\n");
2114 return(NULL);
2115 }
Owen Taylor3473f882001-02-23 17:55:21 +00002116
Daniel Veillard37721922001-05-04 15:21:12 +00002117 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2118 if (ret == NULL) {
2119 xmlGenericError(xmlGenericErrorContext,
2120 "xmlAddRef: out of memory\n");
2121 return(NULL);
2122 }
Owen Taylor3473f882001-02-23 17:55:21 +00002123
Daniel Veillard37721922001-05-04 15:21:12 +00002124 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002125 * fill the structure.
2126 */
Daniel Veillard37721922001-05-04 15:21:12 +00002127 ret->value = xmlStrdup(value);
2128 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002129
Daniel Veillard37721922001-05-04 15:21:12 +00002130 /* To add a reference :-
2131 * References are maintained as a list of references,
2132 * Lookup the entry, if no entry create new nodelist
2133 * Add the owning node to the NodeList
2134 * Return the ref
2135 */
Owen Taylor3473f882001-02-23 17:55:21 +00002136
Daniel Veillard37721922001-05-04 15:21:12 +00002137 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2138 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2139 xmlGenericError(xmlGenericErrorContext,
2140 "xmlAddRef: Reference list creation failed!\n");
2141 return(NULL);
2142 }
2143 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2144 xmlListDelete(ref_list);
2145 xmlGenericError(xmlGenericErrorContext,
2146 "xmlAddRef: Reference list insertion failed!\n");
2147 return(NULL);
2148 }
2149 }
2150 xmlListInsert(ref_list, ret);
2151 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002152}
2153
2154/**
2155 * xmlFreeRefTable:
2156 * @table: An ref table
2157 *
2158 * Deallocate the memory used by an Ref hash table.
2159 */
2160void
2161xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002162 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002163}
2164
2165/**
2166 * xmlIsRef:
2167 * @doc: the document
2168 * @elem: the element carrying the attribute
2169 * @attr: the attribute
2170 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002171 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002172 * then this is simple, otherwise we use an heuristic: name Ref (upper
2173 * or lowercase).
2174 *
2175 * Returns 0 or 1 depending on the lookup result
2176 */
2177int
2178xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002179 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2180 return(0);
2181 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2182 /* TODO @@@ */
2183 return(0);
2184 } else {
2185 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002186
Daniel Veillard37721922001-05-04 15:21:12 +00002187 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2188 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2189 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2190 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002191
Daniel Veillard37721922001-05-04 15:21:12 +00002192 if ((attrDecl != NULL) &&
2193 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2194 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2195 return(1);
2196 }
2197 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002198}
2199
2200/**
2201 * xmlRemoveRef
2202 * @doc: the document
2203 * @attr: the attribute
2204 *
2205 * Remove the given attribute from the Ref table maintained internally.
2206 *
2207 * Returns -1 if the lookup failed and 0 otherwise
2208 */
2209int
2210xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002211 xmlListPtr ref_list;
2212 xmlRefTablePtr table;
2213 xmlChar *ID;
2214 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002215
Daniel Veillard37721922001-05-04 15:21:12 +00002216 if (doc == NULL) return(-1);
2217 if (attr == NULL) return(-1);
2218 table = (xmlRefTablePtr) doc->refs;
2219 if (table == NULL)
2220 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002221
Daniel Veillard37721922001-05-04 15:21:12 +00002222 if (attr == NULL)
2223 return(-1);
2224 ID = xmlNodeListGetString(doc, attr->children, 1);
2225 if (ID == NULL)
2226 return(-1);
2227 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002228
Daniel Veillard37721922001-05-04 15:21:12 +00002229 if(ref_list == NULL) {
2230 xmlFree(ID);
2231 return (-1);
2232 }
2233 /* At this point, ref_list refers to a list of references which
2234 * have the same key as the supplied attr. Our list of references
2235 * is ordered by reference address and we don't have that information
2236 * here to use when removing. We'll have to walk the list and
2237 * check for a matching attribute, when we find one stop the walk
2238 * and remove the entry.
2239 * The list is ordered by reference, so that means we don't have the
2240 * key. Passing the list and the reference to the walker means we
2241 * will have enough data to be able to remove the entry.
2242 */
2243 target.l = ref_list;
2244 target.ap = attr;
2245
2246 /* Remove the supplied attr from our list */
2247 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002248
Daniel Veillard37721922001-05-04 15:21:12 +00002249 /*If the list is empty then remove the list entry in the hash */
2250 if (xmlListEmpty(ref_list))
2251 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2252 xmlFreeRefList);
2253 xmlFree(ID);
2254 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002255}
2256
2257/**
2258 * xmlGetRefs:
2259 * @doc: pointer to the document
2260 * @ID: the ID value
2261 *
2262 * Find the set of references for the supplied ID.
2263 *
2264 * Returns NULL if not found, otherwise node set for the ID.
2265 */
2266xmlListPtr
2267xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002268 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002269
Daniel Veillard37721922001-05-04 15:21:12 +00002270 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002271 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002272 return(NULL);
2273 }
Owen Taylor3473f882001-02-23 17:55:21 +00002274
Daniel Veillard37721922001-05-04 15:21:12 +00002275 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002276 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002277 return(NULL);
2278 }
Owen Taylor3473f882001-02-23 17:55:21 +00002279
Daniel Veillard37721922001-05-04 15:21:12 +00002280 table = (xmlRefTablePtr) doc->refs;
2281 if (table == NULL)
2282 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002283
Daniel Veillard37721922001-05-04 15:21:12 +00002284 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002285}
2286
2287/************************************************************************
2288 * *
2289 * Routines for validity checking *
2290 * *
2291 ************************************************************************/
2292
2293/**
2294 * xmlGetDtdElementDesc:
2295 * @dtd: a pointer to the DtD to search
2296 * @name: the element name
2297 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002298 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002299 *
2300 * returns the xmlElementPtr if found or NULL
2301 */
2302
2303xmlElementPtr
2304xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2305 xmlElementTablePtr table;
2306 xmlElementPtr cur;
2307 xmlChar *uqname = NULL, *prefix = NULL;
2308
2309 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002310 if (dtd->elements == NULL)
2311 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002312 table = (xmlElementTablePtr) dtd->elements;
2313
2314 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002315 if (uqname != NULL)
2316 name = uqname;
2317 cur = xmlHashLookup2(table, name, prefix);
2318 if (prefix != NULL) xmlFree(prefix);
2319 if (uqname != NULL) xmlFree(uqname);
2320 return(cur);
2321}
2322/**
2323 * xmlGetDtdElementDesc2:
2324 * @dtd: a pointer to the DtD to search
2325 * @name: the element name
2326 * @create: create an empty description if not found
2327 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002328 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002329 *
2330 * returns the xmlElementPtr if found or NULL
2331 */
2332
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002333static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002334xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2335 xmlElementTablePtr table;
2336 xmlElementPtr cur;
2337 xmlChar *uqname = NULL, *prefix = NULL;
2338
2339 if (dtd == NULL) return(NULL);
2340 if (dtd->elements == NULL) {
2341 if (!create)
2342 return(NULL);
2343 /*
2344 * Create the Element table if needed.
2345 */
2346 table = (xmlElementTablePtr) dtd->elements;
2347 if (table == NULL) {
2348 table = xmlCreateElementTable();
2349 dtd->elements = (void *) table;
2350 }
2351 if (table == NULL) {
2352 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002353 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002354 return(NULL);
2355 }
2356 }
2357 table = (xmlElementTablePtr) dtd->elements;
2358
2359 uqname = xmlSplitQName2(name, &prefix);
2360 if (uqname != NULL)
2361 name = uqname;
2362 cur = xmlHashLookup2(table, name, prefix);
2363 if ((cur == NULL) && (create)) {
2364 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2365 if (cur == NULL) {
2366 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002367 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002368 return(NULL);
2369 }
2370 memset(cur, 0, sizeof(xmlElement));
2371 cur->type = XML_ELEMENT_DECL;
2372
2373 /*
2374 * fill the structure.
2375 */
2376 cur->name = xmlStrdup(name);
2377 cur->prefix = xmlStrdup(prefix);
2378 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2379
2380 xmlHashAddEntry2(table, name, prefix, cur);
2381 }
2382 if (prefix != NULL) xmlFree(prefix);
2383 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002384 return(cur);
2385}
2386
2387/**
2388 * xmlGetDtdQElementDesc:
2389 * @dtd: a pointer to the DtD to search
2390 * @name: the element name
2391 * @prefix: the element namespace prefix
2392 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002393 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002394 *
2395 * returns the xmlElementPtr if found or NULL
2396 */
2397
Daniel Veillard48da9102001-08-07 01:10:10 +00002398xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002399xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2400 const xmlChar *prefix) {
2401 xmlElementTablePtr table;
2402
2403 if (dtd == NULL) return(NULL);
2404 if (dtd->elements == NULL) return(NULL);
2405 table = (xmlElementTablePtr) dtd->elements;
2406
2407 return(xmlHashLookup2(table, name, prefix));
2408}
2409
2410/**
2411 * xmlGetDtdAttrDesc:
2412 * @dtd: a pointer to the DtD to search
2413 * @elem: the element name
2414 * @name: the attribute name
2415 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002416 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002417 * this element.
2418 *
2419 * returns the xmlAttributePtr if found or NULL
2420 */
2421
2422xmlAttributePtr
2423xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2424 xmlAttributeTablePtr table;
2425 xmlAttributePtr cur;
2426 xmlChar *uqname = NULL, *prefix = NULL;
2427
2428 if (dtd == NULL) return(NULL);
2429 if (dtd->attributes == NULL) return(NULL);
2430
2431 table = (xmlAttributeTablePtr) dtd->attributes;
2432 if (table == NULL)
2433 return(NULL);
2434
2435 uqname = xmlSplitQName2(name, &prefix);
2436
2437 if (uqname != NULL) {
2438 cur = xmlHashLookup3(table, uqname, prefix, elem);
2439 if (prefix != NULL) xmlFree(prefix);
2440 if (uqname != NULL) xmlFree(uqname);
2441 } else
2442 cur = xmlHashLookup3(table, name, NULL, elem);
2443 return(cur);
2444}
2445
2446/**
2447 * xmlGetDtdQAttrDesc:
2448 * @dtd: a pointer to the DtD to search
2449 * @elem: the element name
2450 * @name: the attribute name
2451 * @prefix: the attribute namespace prefix
2452 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002453 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002454 * this element.
2455 *
2456 * returns the xmlAttributePtr if found or NULL
2457 */
2458
Daniel Veillard48da9102001-08-07 01:10:10 +00002459xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002460xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2461 const xmlChar *prefix) {
2462 xmlAttributeTablePtr table;
2463
2464 if (dtd == NULL) return(NULL);
2465 if (dtd->attributes == NULL) return(NULL);
2466 table = (xmlAttributeTablePtr) dtd->attributes;
2467
2468 return(xmlHashLookup3(table, name, prefix, elem));
2469}
2470
2471/**
2472 * xmlGetDtdNotationDesc:
2473 * @dtd: a pointer to the DtD to search
2474 * @name: the notation name
2475 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002476 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002477 *
2478 * returns the xmlNotationPtr if found or NULL
2479 */
2480
2481xmlNotationPtr
2482xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2483 xmlNotationTablePtr table;
2484
2485 if (dtd == NULL) return(NULL);
2486 if (dtd->notations == NULL) return(NULL);
2487 table = (xmlNotationTablePtr) dtd->notations;
2488
2489 return(xmlHashLookup(table, name));
2490}
2491
2492/**
2493 * xmlValidateNotationUse:
2494 * @ctxt: the validation context
2495 * @doc: the document
2496 * @notationName: the notation name to check
2497 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002498 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002499 * - [ VC: Notation Declared ]
2500 *
2501 * returns 1 if valid or 0 otherwise
2502 */
2503
2504int
2505xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2506 const xmlChar *notationName) {
2507 xmlNotationPtr notaDecl;
2508 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2509
2510 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2511 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2512 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2513
2514 if (notaDecl == NULL) {
2515 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2516 notationName);
2517 return(0);
2518 }
2519 return(1);
2520}
2521
2522/**
2523 * xmlIsMixedElement
2524 * @doc: the document
2525 * @name: the element name
2526 *
2527 * Search in the DtDs whether an element accept Mixed content (or ANY)
2528 * basically if it is supposed to accept text childs
2529 *
2530 * returns 0 if no, 1 if yes, and -1 if no element description is available
2531 */
2532
2533int
2534xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2535 xmlElementPtr elemDecl;
2536
2537 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2538
2539 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2540 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2541 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2542 if (elemDecl == NULL) return(-1);
2543 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002544 case XML_ELEMENT_TYPE_UNDEFINED:
2545 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002546 case XML_ELEMENT_TYPE_ELEMENT:
2547 return(0);
2548 case XML_ELEMENT_TYPE_EMPTY:
2549 /*
2550 * return 1 for EMPTY since we want VC error to pop up
2551 * on <empty> </empty> for example
2552 */
2553 case XML_ELEMENT_TYPE_ANY:
2554 case XML_ELEMENT_TYPE_MIXED:
2555 return(1);
2556 }
2557 return(1);
2558}
2559
2560/**
2561 * xmlValidateNameValue:
2562 * @value: an Name value
2563 *
2564 * Validate that the given value match Name production
2565 *
2566 * returns 1 if valid or 0 otherwise
2567 */
2568
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002569static int
Owen Taylor3473f882001-02-23 17:55:21 +00002570xmlValidateNameValue(const xmlChar *value) {
2571 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002572 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002573
2574 if (value == NULL) return(0);
2575 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002576 val = xmlStringCurrentChar(NULL, cur, &len);
2577 cur += len;
2578 if (!IS_LETTER(val) && (val != '_') &&
2579 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002580 return(0);
2581 }
2582
Daniel Veillardd8224e02002-01-13 15:43:22 +00002583 val = xmlStringCurrentChar(NULL, cur, &len);
2584 cur += len;
2585 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2586 (val == '.') || (val == '-') ||
2587 (val == '_') || (val == ':') ||
2588 (IS_COMBINING(val)) ||
2589 (IS_EXTENDER(val))) {
2590 val = xmlStringCurrentChar(NULL, cur, &len);
2591 cur += len;
2592 }
Owen Taylor3473f882001-02-23 17:55:21 +00002593
Daniel Veillardd8224e02002-01-13 15:43:22 +00002594 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002595
2596 return(1);
2597}
2598
2599/**
2600 * xmlValidateNamesValue:
2601 * @value: an Names value
2602 *
2603 * Validate that the given value match Names production
2604 *
2605 * returns 1 if valid or 0 otherwise
2606 */
2607
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002608static int
Owen Taylor3473f882001-02-23 17:55:21 +00002609xmlValidateNamesValue(const xmlChar *value) {
2610 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002611 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002612
2613 if (value == NULL) return(0);
2614 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002615 val = xmlStringCurrentChar(NULL, cur, &len);
2616 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002617
Daniel Veillardd8224e02002-01-13 15:43:22 +00002618 if (!IS_LETTER(val) && (val != '_') &&
2619 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002620 return(0);
2621 }
2622
Daniel Veillardd8224e02002-01-13 15:43:22 +00002623 val = xmlStringCurrentChar(NULL, cur, &len);
2624 cur += len;
2625 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2626 (val == '.') || (val == '-') ||
2627 (val == '_') || (val == ':') ||
2628 (IS_COMBINING(val)) ||
2629 (IS_EXTENDER(val))) {
2630 val = xmlStringCurrentChar(NULL, cur, &len);
2631 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002632 }
2633
Daniel Veillardd8224e02002-01-13 15:43:22 +00002634 while (IS_BLANK(val)) {
2635 while (IS_BLANK(val)) {
2636 val = xmlStringCurrentChar(NULL, cur, &len);
2637 cur += len;
2638 }
2639
2640 if (!IS_LETTER(val) && (val != '_') &&
2641 (val != ':')) {
2642 return(0);
2643 }
2644 val = xmlStringCurrentChar(NULL, cur, &len);
2645 cur += len;
2646
2647 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2648 (val == '.') || (val == '-') ||
2649 (val == '_') || (val == ':') ||
2650 (IS_COMBINING(val)) ||
2651 (IS_EXTENDER(val))) {
2652 val = xmlStringCurrentChar(NULL, cur, &len);
2653 cur += len;
2654 }
2655 }
2656
2657 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002658
2659 return(1);
2660}
2661
2662/**
2663 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002664 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002665 *
2666 * Validate that the given value match Nmtoken production
2667 *
2668 * [ VC: Name Token ]
2669 *
2670 * returns 1 if valid or 0 otherwise
2671 */
2672
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002673static int
Owen Taylor3473f882001-02-23 17:55:21 +00002674xmlValidateNmtokenValue(const xmlChar *value) {
2675 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002676 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002677
2678 if (value == NULL) return(0);
2679 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002680 val = xmlStringCurrentChar(NULL, cur, &len);
2681 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002682
Daniel Veillardd8224e02002-01-13 15:43:22 +00002683 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2684 (val != '.') && (val != '-') &&
2685 (val != '_') && (val != ':') &&
2686 (!IS_COMBINING(val)) &&
2687 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002688 return(0);
2689
Daniel Veillardd8224e02002-01-13 15:43:22 +00002690 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2691 (val == '.') || (val == '-') ||
2692 (val == '_') || (val == ':') ||
2693 (IS_COMBINING(val)) ||
2694 (IS_EXTENDER(val))) {
2695 val = xmlStringCurrentChar(NULL, cur, &len);
2696 cur += len;
2697 }
Owen Taylor3473f882001-02-23 17:55:21 +00002698
Daniel Veillardd8224e02002-01-13 15:43:22 +00002699 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002700
2701 return(1);
2702}
2703
2704/**
2705 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002706 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002707 *
2708 * Validate that the given value match Nmtokens production
2709 *
2710 * [ VC: Name Token ]
2711 *
2712 * returns 1 if valid or 0 otherwise
2713 */
2714
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002715static int
Owen Taylor3473f882001-02-23 17:55:21 +00002716xmlValidateNmtokensValue(const xmlChar *value) {
2717 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002718 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002719
2720 if (value == NULL) return(0);
2721 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002722 val = xmlStringCurrentChar(NULL, cur, &len);
2723 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002724
Daniel Veillardd8224e02002-01-13 15:43:22 +00002725 while (IS_BLANK(val)) {
2726 val = xmlStringCurrentChar(NULL, cur, &len);
2727 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002728 }
2729
Daniel Veillardd8224e02002-01-13 15:43:22 +00002730 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2731 (val != '.') && (val != '-') &&
2732 (val != '_') && (val != ':') &&
2733 (!IS_COMBINING(val)) &&
2734 (!IS_EXTENDER(val)))
2735 return(0);
2736
2737 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2738 (val == '.') || (val == '-') ||
2739 (val == '_') || (val == ':') ||
2740 (IS_COMBINING(val)) ||
2741 (IS_EXTENDER(val))) {
2742 val = xmlStringCurrentChar(NULL, cur, &len);
2743 cur += len;
2744 }
2745
2746 while (IS_BLANK(val)) {
2747 while (IS_BLANK(val)) {
2748 val = xmlStringCurrentChar(NULL, cur, &len);
2749 cur += len;
2750 }
2751 if (val == 0) return(1);
2752
2753 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2754 (val != '.') && (val != '-') &&
2755 (val != '_') && (val != ':') &&
2756 (!IS_COMBINING(val)) &&
2757 (!IS_EXTENDER(val)))
2758 return(0);
2759
2760 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2761 (val == '.') || (val == '-') ||
2762 (val == '_') || (val == ':') ||
2763 (IS_COMBINING(val)) ||
2764 (IS_EXTENDER(val))) {
2765 val = xmlStringCurrentChar(NULL, cur, &len);
2766 cur += len;
2767 }
2768 }
2769
2770 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002771
2772 return(1);
2773}
2774
2775/**
2776 * xmlValidateNotationDecl:
2777 * @ctxt: the validation context
2778 * @doc: a document instance
2779 * @nota: a notation definition
2780 *
2781 * Try to validate a single notation definition
2782 * basically it does the following checks as described by the
2783 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002784 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002785 * But this function get called anyway ...
2786 *
2787 * returns 1 if valid or 0 otherwise
2788 */
2789
2790int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002791xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2792 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002793 int ret = 1;
2794
2795 return(ret);
2796}
2797
2798/**
2799 * xmlValidateAttributeValue:
2800 * @type: an attribute type
2801 * @value: an attribute value
2802 *
2803 * Validate that the given attribute value match the proper production
2804 *
2805 * [ VC: ID ]
2806 * Values of type ID must match the Name production....
2807 *
2808 * [ VC: IDREF ]
2809 * Values of type IDREF must match the Name production, and values
2810 * of type IDREFS must match Names ...
2811 *
2812 * [ VC: Entity Name ]
2813 * Values of type ENTITY must match the Name production, values
2814 * of type ENTITIES must match Names ...
2815 *
2816 * [ VC: Name Token ]
2817 * Values of type NMTOKEN must match the Nmtoken production; values
2818 * of type NMTOKENS must match Nmtokens.
2819 *
2820 * returns 1 if valid or 0 otherwise
2821 */
2822
2823int
2824xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2825 switch (type) {
2826 case XML_ATTRIBUTE_ENTITIES:
2827 case XML_ATTRIBUTE_IDREFS:
2828 return(xmlValidateNamesValue(value));
2829 case XML_ATTRIBUTE_ENTITY:
2830 case XML_ATTRIBUTE_IDREF:
2831 case XML_ATTRIBUTE_ID:
2832 case XML_ATTRIBUTE_NOTATION:
2833 return(xmlValidateNameValue(value));
2834 case XML_ATTRIBUTE_NMTOKENS:
2835 case XML_ATTRIBUTE_ENUMERATION:
2836 return(xmlValidateNmtokensValue(value));
2837 case XML_ATTRIBUTE_NMTOKEN:
2838 return(xmlValidateNmtokenValue(value));
2839 case XML_ATTRIBUTE_CDATA:
2840 break;
2841 }
2842 return(1);
2843}
2844
2845/**
2846 * xmlValidateAttributeValue2:
2847 * @ctxt: the validation context
2848 * @doc: the document
2849 * @name: the attribute name (used for error reporting only)
2850 * @type: the attribute type
2851 * @value: the attribute value
2852 *
2853 * Validate that the given attribute value match a given type.
2854 * This typically cannot be done before having finished parsing
2855 * the subsets.
2856 *
2857 * [ VC: IDREF ]
2858 * Values of type IDREF must match one of the declared IDs
2859 * Values of type IDREFS must match a sequence of the declared IDs
2860 * each Name must match the value of an ID attribute on some element
2861 * in the XML document; i.e. IDREF values must match the value of
2862 * some ID attribute
2863 *
2864 * [ VC: Entity Name ]
2865 * Values of type ENTITY must match one declared entity
2866 * Values of type ENTITIES must match a sequence of declared entities
2867 *
2868 * [ VC: Notation Attributes ]
2869 * all notation names in the declaration must be declared.
2870 *
2871 * returns 1 if valid or 0 otherwise
2872 */
2873
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002874static int
Owen Taylor3473f882001-02-23 17:55:21 +00002875xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2876 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2877 int ret = 1;
2878 switch (type) {
2879 case XML_ATTRIBUTE_IDREFS:
2880 case XML_ATTRIBUTE_IDREF:
2881 case XML_ATTRIBUTE_ID:
2882 case XML_ATTRIBUTE_NMTOKENS:
2883 case XML_ATTRIBUTE_ENUMERATION:
2884 case XML_ATTRIBUTE_NMTOKEN:
2885 case XML_ATTRIBUTE_CDATA:
2886 break;
2887 case XML_ATTRIBUTE_ENTITY: {
2888 xmlEntityPtr ent;
2889
2890 ent = xmlGetDocEntity(doc, value);
2891 if (ent == NULL) {
2892 VERROR(ctxt->userData,
2893 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2894 name, value);
2895 ret = 0;
2896 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2897 VERROR(ctxt->userData,
2898 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2899 name, value);
2900 ret = 0;
2901 }
2902 break;
2903 }
2904 case XML_ATTRIBUTE_ENTITIES: {
2905 xmlChar *dup, *nam = NULL, *cur, save;
2906 xmlEntityPtr ent;
2907
2908 dup = xmlStrdup(value);
2909 if (dup == NULL)
2910 return(0);
2911 cur = dup;
2912 while (*cur != 0) {
2913 nam = cur;
2914 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2915 save = *cur;
2916 *cur = 0;
2917 ent = xmlGetDocEntity(doc, nam);
2918 if (ent == NULL) {
2919 VERROR(ctxt->userData,
2920 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2921 name, nam);
2922 ret = 0;
2923 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2924 VERROR(ctxt->userData,
2925 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2926 name, nam);
2927 ret = 0;
2928 }
2929 if (save == 0)
2930 break;
2931 *cur = save;
2932 while (IS_BLANK(*cur)) cur++;
2933 }
2934 xmlFree(dup);
2935 break;
2936 }
2937 case XML_ATTRIBUTE_NOTATION: {
2938 xmlNotationPtr nota;
2939
2940 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2941 if ((nota == NULL) && (doc->extSubset != NULL))
2942 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2943
2944 if (nota == NULL) {
2945 VERROR(ctxt->userData,
2946 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2947 name, value);
2948 ret = 0;
2949 }
2950 break;
2951 }
2952 }
2953 return(ret);
2954}
2955
2956/**
2957 * xmlValidNormalizeAttributeValue:
2958 * @doc: the document
2959 * @elem: the parent
2960 * @name: the attribute name
2961 * @value: the attribute value
2962 *
2963 * Does the validation related extra step of the normalization of attribute
2964 * values:
2965 *
2966 * If the declared value is not CDATA, then the XML processor must further
2967 * process the normalized attribute value by discarding any leading and
2968 * trailing space (#x20) characters, and by replacing sequences of space
2969 * (#x20) characters by single space (#x20) character.
2970 *
2971 * returns a new normalized string if normalization is needed, NULL otherwise
2972 * the caller must free the returned value.
2973 */
2974
2975xmlChar *
2976xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2977 const xmlChar *name, const xmlChar *value) {
2978 xmlChar *ret, *dst;
2979 const xmlChar *src;
2980 xmlAttributePtr attrDecl = NULL;
2981
2982 if (doc == NULL) return(NULL);
2983 if (elem == NULL) return(NULL);
2984 if (name == NULL) return(NULL);
2985 if (value == NULL) return(NULL);
2986
2987 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2988 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002989 snprintf((char *) qname, sizeof(qname), "%s:%s",
2990 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002991 qname[sizeof(qname) - 1] = 0;
2992 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2993 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2994 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2995 }
2996 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2997 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2998 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2999
3000 if (attrDecl == NULL)
3001 return(NULL);
3002 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3003 return(NULL);
3004
3005 ret = xmlStrdup(value);
3006 if (ret == NULL)
3007 return(NULL);
3008 src = value;
3009 dst = ret;
3010 while (*src == 0x20) src++;
3011 while (*src != 0) {
3012 if (*src == 0x20) {
3013 while (*src == 0x20) src++;
3014 if (*src != 0)
3015 *dst++ = 0x20;
3016 } else {
3017 *dst++ = *src++;
3018 }
3019 }
3020 *dst = 0;
3021 return(ret);
3022}
3023
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003024static void
Owen Taylor3473f882001-02-23 17:55:21 +00003025xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003026 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003027 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3028}
3029
3030/**
3031 * xmlValidateAttributeDecl:
3032 * @ctxt: the validation context
3033 * @doc: a document instance
3034 * @attr: an attribute definition
3035 *
3036 * Try to validate a single attribute definition
3037 * basically it does the following checks as described by the
3038 * XML-1.0 recommendation:
3039 * - [ VC: Attribute Default Legal ]
3040 * - [ VC: Enumeration ]
3041 * - [ VC: ID Attribute Default ]
3042 *
3043 * The ID/IDREF uniqueness and matching are done separately
3044 *
3045 * returns 1 if valid or 0 otherwise
3046 */
3047
3048int
3049xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3050 xmlAttributePtr attr) {
3051 int ret = 1;
3052 int val;
3053 CHECK_DTD;
3054 if(attr == NULL) return(1);
3055
3056 /* Attribute Default Legal */
3057 /* Enumeration */
3058 if (attr->defaultValue != NULL) {
3059 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3060 if (val == 0) {
3061 VERROR(ctxt->userData,
3062 "Syntax of default value for attribute %s on %s is not valid\n",
3063 attr->name, attr->elem);
3064 }
3065 ret &= val;
3066 }
3067
3068 /* ID Attribute Default */
3069 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3070 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3071 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3072 VERROR(ctxt->userData,
3073 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
3074 attr->name, attr->elem);
3075 ret = 0;
3076 }
3077
3078 /* One ID per Element Type */
3079 if (attr->atype == XML_ATTRIBUTE_ID) {
3080 int nbId;
3081
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003082 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003083 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3084 attr->elem);
3085 if (elem != NULL) {
3086 nbId = xmlScanIDAttributeDecl(NULL, elem);
3087 } else {
3088 xmlAttributeTablePtr table;
3089
3090 /*
3091 * The attribute may be declared in the internal subset and the
3092 * element in the external subset.
3093 */
3094 nbId = 0;
3095 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3096 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3097 xmlValidateAttributeIdCallback, &nbId);
3098 }
3099 if (nbId > 1) {
3100 VERROR(ctxt->userData,
3101 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3102 attr->elem, nbId, attr->name);
3103 } else if (doc->extSubset != NULL) {
3104 int extId = 0;
3105 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3106 if (elem != NULL) {
3107 extId = xmlScanIDAttributeDecl(NULL, elem);
3108 }
3109 if (extId > 1) {
3110 VERROR(ctxt->userData,
3111 "Element %s has %d ID attribute defined in the external subset : %s\n",
3112 attr->elem, extId, attr->name);
3113 } else if (extId + nbId > 1) {
3114 VERROR(ctxt->userData,
3115"Element %s has ID attributes defined in the internal and external subset : %s\n",
3116 attr->elem, attr->name);
3117 }
3118 }
3119 }
3120
3121 /* Validity Constraint: Enumeration */
3122 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3123 xmlEnumerationPtr tree = attr->tree;
3124 while (tree != NULL) {
3125 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3126 tree = tree->next;
3127 }
3128 if (tree == NULL) {
3129 VERROR(ctxt->userData,
3130"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3131 attr->defaultValue, attr->name, attr->elem);
3132 ret = 0;
3133 }
3134 }
3135
3136 return(ret);
3137}
3138
3139/**
3140 * xmlValidateElementDecl:
3141 * @ctxt: the validation context
3142 * @doc: a document instance
3143 * @elem: an element definition
3144 *
3145 * Try to validate a single element definition
3146 * basically it does the following checks as described by the
3147 * XML-1.0 recommendation:
3148 * - [ VC: One ID per Element Type ]
3149 * - [ VC: No Duplicate Types ]
3150 * - [ VC: Unique Element Type Declaration ]
3151 *
3152 * returns 1 if valid or 0 otherwise
3153 */
3154
3155int
3156xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3157 xmlElementPtr elem) {
3158 int ret = 1;
3159 xmlElementPtr tst;
3160
3161 CHECK_DTD;
3162
3163 if (elem == NULL) return(1);
3164
3165 /* No Duplicate Types */
3166 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3167 xmlElementContentPtr cur, next;
3168 const xmlChar *name;
3169
3170 cur = elem->content;
3171 while (cur != NULL) {
3172 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3173 if (cur->c1 == NULL) break;
3174 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3175 name = cur->c1->name;
3176 next = cur->c2;
3177 while (next != NULL) {
3178 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3179 if (xmlStrEqual(next->name, name)) {
3180 VERROR(ctxt->userData,
3181 "Definition of %s has duplicate references of %s\n",
3182 elem->name, name);
3183 ret = 0;
3184 }
3185 break;
3186 }
3187 if (next->c1 == NULL) break;
3188 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3189 if (xmlStrEqual(next->c1->name, name)) {
3190 VERROR(ctxt->userData,
3191 "Definition of %s has duplicate references of %s\n",
3192 elem->name, name);
3193 ret = 0;
3194 }
3195 next = next->c2;
3196 }
3197 }
3198 cur = cur->c2;
3199 }
3200 }
3201
3202 /* VC: Unique Element Type Declaration */
3203 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003204 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003205 ((tst->prefix == elem->prefix) ||
3206 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003207 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003208 VERROR(ctxt->userData, "Redefinition of element %s\n",
3209 elem->name);
3210 ret = 0;
3211 }
3212 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003213 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003214 ((tst->prefix == elem->prefix) ||
3215 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003216 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003217 VERROR(ctxt->userData, "Redefinition of element %s\n",
3218 elem->name);
3219 ret = 0;
3220 }
3221
Daniel Veillarda10efa82001-04-18 13:09:01 +00003222 /* One ID per Element Type
3223 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003224 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3225 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003226 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003227 return(ret);
3228}
3229
3230/**
3231 * xmlValidateOneAttribute:
3232 * @ctxt: the validation context
3233 * @doc: a document instance
3234 * @elem: an element instance
3235 * @attr: an attribute instance
3236 * @value: the attribute value (without entities processing)
3237 *
3238 * Try to validate a single attribute for an element
3239 * basically it does the following checks as described by the
3240 * XML-1.0 recommendation:
3241 * - [ VC: Attribute Value Type ]
3242 * - [ VC: Fixed Attribute Default ]
3243 * - [ VC: Entity Name ]
3244 * - [ VC: Name Token ]
3245 * - [ VC: ID ]
3246 * - [ VC: IDREF ]
3247 * - [ VC: Entity Name ]
3248 * - [ VC: Notation Attributes ]
3249 *
3250 * The ID/IDREF uniqueness and matching are done separately
3251 *
3252 * returns 1 if valid or 0 otherwise
3253 */
3254
3255int
3256xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3257 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3258 /* xmlElementPtr elemDecl; */
3259 xmlAttributePtr attrDecl = NULL;
3260 int val;
3261 int ret = 1;
3262
3263 CHECK_DTD;
3264 if ((elem == NULL) || (elem->name == NULL)) return(0);
3265 if ((attr == NULL) || (attr->name == NULL)) return(0);
3266
3267 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3268 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003269 snprintf((char *) qname, sizeof(qname), "%s:%s",
3270 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003271 qname[sizeof(qname) - 1] = 0;
3272 if (attr->ns != NULL) {
3273 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3274 attr->name, attr->ns->prefix);
3275 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3276 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3277 attr->name, attr->ns->prefix);
3278 } else {
3279 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3280 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3281 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3282 qname, attr->name);
3283 }
3284 }
3285 if (attrDecl == NULL) {
3286 if (attr->ns != NULL) {
3287 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3288 attr->name, attr->ns->prefix);
3289 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3290 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3291 attr->name, attr->ns->prefix);
3292 } else {
3293 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3294 elem->name, attr->name);
3295 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3296 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3297 elem->name, attr->name);
3298 }
3299 }
3300
3301
3302 /* Validity Constraint: Attribute Value Type */
3303 if (attrDecl == NULL) {
3304 VERROR(ctxt->userData,
3305 "No declaration for attribute %s on element %s\n",
3306 attr->name, elem->name);
3307 return(0);
3308 }
3309 attr->atype = attrDecl->atype;
3310
3311 val = xmlValidateAttributeValue(attrDecl->atype, value);
3312 if (val == 0) {
3313 VERROR(ctxt->userData,
3314 "Syntax of value for attribute %s on %s is not valid\n",
3315 attr->name, elem->name);
3316 ret = 0;
3317 }
3318
3319 /* Validity constraint: Fixed Attribute Default */
3320 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3321 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3322 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003323 "Value for attribute %s on %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003324 attr->name, elem->name, attrDecl->defaultValue);
3325 ret = 0;
3326 }
3327 }
3328
3329 /* Validity Constraint: ID uniqueness */
3330 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3331 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3332 ret = 0;
3333 }
3334
3335 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3336 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3337 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3338 ret = 0;
3339 }
3340
3341 /* Validity Constraint: Notation Attributes */
3342 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3343 xmlEnumerationPtr tree = attrDecl->tree;
3344 xmlNotationPtr nota;
3345
3346 /* First check that the given NOTATION was declared */
3347 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3348 if (nota == NULL)
3349 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3350
3351 if (nota == NULL) {
3352 VERROR(ctxt->userData,
3353 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3354 value, attr->name, elem->name);
3355 ret = 0;
3356 }
3357
3358 /* Second, verify that it's among the list */
3359 while (tree != NULL) {
3360 if (xmlStrEqual(tree->name, value)) break;
3361 tree = tree->next;
3362 }
3363 if (tree == NULL) {
3364 VERROR(ctxt->userData,
3365"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3366 value, attr->name, elem->name);
3367 ret = 0;
3368 }
3369 }
3370
3371 /* Validity Constraint: Enumeration */
3372 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3373 xmlEnumerationPtr tree = attrDecl->tree;
3374 while (tree != NULL) {
3375 if (xmlStrEqual(tree->name, value)) break;
3376 tree = tree->next;
3377 }
3378 if (tree == NULL) {
3379 VERROR(ctxt->userData,
3380 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3381 value, attr->name, elem->name);
3382 ret = 0;
3383 }
3384 }
3385
3386 /* Fixed Attribute Default */
3387 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3388 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3389 VERROR(ctxt->userData,
3390 "Value for attribute %s on %s must be \"%s\"\n",
3391 attr->name, elem->name, attrDecl->defaultValue);
3392 ret = 0;
3393 }
3394
3395 /* Extra check for the attribute value */
3396 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3397 attrDecl->atype, value);
3398
3399 return(ret);
3400}
3401
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003402/**
3403 * xmlValidateSkipIgnorable:
3404 * @ctxt: the validation context
3405 * @child: the child list
3406 *
3407 * Skip ignorable elements w.r.t. the validation process
3408 *
3409 * returns the first element to consider for validation of the content model
3410 */
3411
3412static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003413xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003414 while (child != NULL) {
3415 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003416 /* These things are ignored (skipped) during validation. */
3417 case XML_PI_NODE:
3418 case XML_COMMENT_NODE:
3419 case XML_XINCLUDE_START:
3420 case XML_XINCLUDE_END:
3421 child = child->next;
3422 break;
3423 case XML_TEXT_NODE:
3424 if (xmlIsBlankNode(child))
3425 child = child->next;
3426 else
3427 return(child);
3428 break;
3429 /* keep current node */
3430 default:
3431 return(child);
3432 }
3433 }
3434 return(child);
3435}
3436
3437/**
3438 * xmlValidateElementType:
3439 * @ctxt: the validation context
3440 *
3441 * Try to validate the content model of an element internal function
3442 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003443 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3444 * reference is found and -3 if the validation succeeded but
3445 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003446 */
3447
3448static int
3449xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003450 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003451 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003452
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003453 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003454 if ((NODE == NULL) && (CONT == NULL))
3455 return(1);
3456 if ((NODE == NULL) &&
3457 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3458 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3459 return(1);
3460 }
3461 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003462 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003463 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003464
3465 /*
3466 * We arrive here when more states need to be examined
3467 */
3468cont:
3469
3470 /*
3471 * We just recovered from a rollback generated by a possible
3472 * epsilon transition, go directly to the analysis phase
3473 */
3474 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003475 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003476 DEBUG_VALID_STATE(NODE, CONT)
3477 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003478 goto analyze;
3479 }
3480
3481 DEBUG_VALID_STATE(NODE, CONT)
3482 /*
3483 * we may have to save a backup state here. This is the equivalent
3484 * of handling epsilon transition in NFAs.
3485 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003486 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003487 ((CONT->parent == NULL) ||
3488 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003489 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003490 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003491 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003492 DEBUG_VALID_MSG("saving parent branch");
3493 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3494 }
3495
3496
3497 /*
3498 * Check first if the content matches
3499 */
3500 switch (CONT->type) {
3501 case XML_ELEMENT_CONTENT_PCDATA:
3502 if (NODE == NULL) {
3503 DEBUG_VALID_MSG("pcdata failed no node");
3504 ret = 0;
3505 break;
3506 }
3507 if (NODE->type == XML_TEXT_NODE) {
3508 DEBUG_VALID_MSG("pcdata found, skip to next");
3509 /*
3510 * go to next element in the content model
3511 * skipping ignorable elems
3512 */
3513 do {
3514 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003515 NODE = xmlValidateSkipIgnorable(NODE);
3516 if ((NODE != NULL) &&
3517 (NODE->type == XML_ENTITY_REF_NODE))
3518 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003519 } while ((NODE != NULL) &&
3520 ((NODE->type != XML_ELEMENT_NODE) &&
3521 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003522 ret = 1;
3523 break;
3524 } else {
3525 DEBUG_VALID_MSG("pcdata failed");
3526 ret = 0;
3527 break;
3528 }
3529 break;
3530 case XML_ELEMENT_CONTENT_ELEMENT:
3531 if (NODE == NULL) {
3532 DEBUG_VALID_MSG("element failed no node");
3533 ret = 0;
3534 break;
3535 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003536 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3537 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003538 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003539 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3540 ret = (CONT->prefix == NULL);
3541 } else if (CONT->prefix == NULL) {
3542 ret = 0;
3543 } else {
3544 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3545 }
3546 }
3547 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003548 DEBUG_VALID_MSG("element found, skip to next");
3549 /*
3550 * go to next element in the content model
3551 * skipping ignorable elems
3552 */
3553 do {
3554 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003555 NODE = xmlValidateSkipIgnorable(NODE);
3556 if ((NODE != NULL) &&
3557 (NODE->type == XML_ENTITY_REF_NODE))
3558 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003559 } while ((NODE != NULL) &&
3560 ((NODE->type != XML_ELEMENT_NODE) &&
3561 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003562 } else {
3563 DEBUG_VALID_MSG("element failed");
3564 ret = 0;
3565 break;
3566 }
3567 break;
3568 case XML_ELEMENT_CONTENT_OR:
3569 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003570 * Small optimization.
3571 */
3572 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3573 if ((NODE == NULL) ||
3574 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3575 DEPTH++;
3576 CONT = CONT->c2;
3577 goto cont;
3578 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003579 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3580 ret = (CONT->c1->prefix == NULL);
3581 } else if (CONT->c1->prefix == NULL) {
3582 ret = 0;
3583 } else {
3584 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3585 }
3586 if (ret == 0) {
3587 DEPTH++;
3588 CONT = CONT->c2;
3589 goto cont;
3590 }
Daniel Veillard85349052001-04-20 13:48:21 +00003591 }
3592
3593 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003594 * save the second branch 'or' branch
3595 */
3596 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard268fd1b2001-08-26 18:46:36 +00003597 vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3598 OCCURS, ROLLBACK_OR);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003599
3600 DEPTH++;
3601 CONT = CONT->c1;
3602 goto cont;
3603 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003604 /*
3605 * Small optimization.
3606 */
3607 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3608 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3609 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3610 if ((NODE == NULL) ||
3611 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3612 DEPTH++;
3613 CONT = CONT->c2;
3614 goto cont;
3615 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003616 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3617 ret = (CONT->c1->prefix == NULL);
3618 } else if (CONT->c1->prefix == NULL) {
3619 ret = 0;
3620 } else {
3621 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3622 }
3623 if (ret == 0) {
3624 DEPTH++;
3625 CONT = CONT->c2;
3626 goto cont;
3627 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003628 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003629 DEPTH++;
3630 CONT = CONT->c1;
3631 goto cont;
3632 }
3633
3634 /*
3635 * At this point handle going up in the tree
3636 */
3637 if (ret == -1) {
3638 DEBUG_VALID_MSG("error found returning");
3639 return(ret);
3640 }
3641analyze:
3642 while (CONT != NULL) {
3643 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003644 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003645 * this level.
3646 */
3647 if (ret == 0) {
3648 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003649 xmlNodePtr cur;
3650
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003651 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003652 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003653 DEBUG_VALID_MSG("Once branch failed, rollback");
3654 if (vstateVPop(ctxt) < 0 ) {
3655 DEBUG_VALID_MSG("exhaustion, failed");
3656 return(0);
3657 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003658 if (cur != ctxt->vstate->node)
3659 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003660 goto cont;
3661 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003662 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003663 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003664 DEBUG_VALID_MSG("Plus branch failed, rollback");
3665 if (vstateVPop(ctxt) < 0 ) {
3666 DEBUG_VALID_MSG("exhaustion, failed");
3667 return(0);
3668 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003669 if (cur != ctxt->vstate->node)
3670 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003671 goto cont;
3672 }
3673 DEBUG_VALID_MSG("Plus branch found");
3674 ret = 1;
3675 break;
3676 case XML_ELEMENT_CONTENT_MULT:
3677#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003678 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003679 DEBUG_VALID_MSG("Mult branch failed");
3680 } else {
3681 DEBUG_VALID_MSG("Mult branch found");
3682 }
3683#endif
3684 ret = 1;
3685 break;
3686 case XML_ELEMENT_CONTENT_OPT:
3687 DEBUG_VALID_MSG("Option branch failed");
3688 ret = 1;
3689 break;
3690 }
3691 } else {
3692 switch (CONT->ocur) {
3693 case XML_ELEMENT_CONTENT_OPT:
3694 DEBUG_VALID_MSG("Option branch succeeded");
3695 ret = 1;
3696 break;
3697 case XML_ELEMENT_CONTENT_ONCE:
3698 DEBUG_VALID_MSG("Once branch succeeded");
3699 ret = 1;
3700 break;
3701 case XML_ELEMENT_CONTENT_PLUS:
3702 if (STATE == ROLLBACK_PARENT) {
3703 DEBUG_VALID_MSG("Plus branch rollback");
3704 ret = 1;
3705 break;
3706 }
3707 if (NODE == NULL) {
3708 DEBUG_VALID_MSG("Plus branch exhausted");
3709 ret = 1;
3710 break;
3711 }
3712 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003713 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003714 goto cont;
3715 case XML_ELEMENT_CONTENT_MULT:
3716 if (STATE == ROLLBACK_PARENT) {
3717 DEBUG_VALID_MSG("Mult branch rollback");
3718 ret = 1;
3719 break;
3720 }
3721 if (NODE == NULL) {
3722 DEBUG_VALID_MSG("Mult branch exhausted");
3723 ret = 1;
3724 break;
3725 }
3726 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003727 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003728 goto cont;
3729 }
3730 }
3731 STATE = 0;
3732
3733 /*
3734 * Then act accordingly at the parent level
3735 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003736 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003737 if (CONT->parent == NULL)
3738 break;
3739
3740 switch (CONT->parent->type) {
3741 case XML_ELEMENT_CONTENT_PCDATA:
3742 DEBUG_VALID_MSG("Error: parent pcdata");
3743 return(-1);
3744 case XML_ELEMENT_CONTENT_ELEMENT:
3745 DEBUG_VALID_MSG("Error: parent element");
3746 return(-1);
3747 case XML_ELEMENT_CONTENT_OR:
3748 if (ret == 1) {
3749 DEBUG_VALID_MSG("Or succeeded");
3750 CONT = CONT->parent;
3751 DEPTH--;
3752 } else {
3753 DEBUG_VALID_MSG("Or failed");
3754 CONT = CONT->parent;
3755 DEPTH--;
3756 }
3757 break;
3758 case XML_ELEMENT_CONTENT_SEQ:
3759 if (ret == 0) {
3760 DEBUG_VALID_MSG("Sequence failed");
3761 CONT = CONT->parent;
3762 DEPTH--;
3763 } else if (CONT == CONT->parent->c1) {
3764 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3765 CONT = CONT->parent->c2;
3766 goto cont;
3767 } else {
3768 DEBUG_VALID_MSG("Sequence succeeded");
3769 CONT = CONT->parent;
3770 DEPTH--;
3771 }
3772 }
3773 }
3774 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003775 xmlNodePtr cur;
3776
3777 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003778 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3779 if (vstateVPop(ctxt) < 0 ) {
3780 DEBUG_VALID_MSG("exhaustion, failed");
3781 return(0);
3782 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003783 if (cur != ctxt->vstate->node)
3784 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003785 goto cont;
3786 }
3787 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003788 xmlNodePtr cur;
3789
3790 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003791 DEBUG_VALID_MSG("Failure, rollback");
3792 if (vstateVPop(ctxt) < 0 ) {
3793 DEBUG_VALID_MSG("exhaustion, failed");
3794 return(0);
3795 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003796 if (cur != ctxt->vstate->node)
3797 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003798 goto cont;
3799 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003800 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003801}
3802
3803/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003804 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003805 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003806 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003807 * @content: An element
3808 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3809 *
3810 * This will dump the list of elements to the buffer
3811 * Intended just for the debug routine
3812 */
3813static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003814xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003815 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003816 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003817
3818 if (node == NULL) return;
3819 if (glob) strcat(buf, "(");
3820 cur = node;
3821 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003822 len = strlen(buf);
3823 if (size - len < 50) {
3824 if ((size - len > 4) && (buf[len - 1] != '.'))
3825 strcat(buf, " ...");
3826 return;
3827 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003828 switch (cur->type) {
3829 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003830 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
3831 if (size - len < xmlStrlen(cur->ns->prefix + 10)) {
3832 if ((size - len > 4) && (buf[len - 1] != '.'))
3833 strcat(buf, " ...");
3834 return;
3835 }
3836 strcat(buf, (char *) cur->ns->prefix);
3837 strcat(buf, ":");
3838 }
Daniel Veillardd3d06722001-08-15 12:06:36 +00003839 if (size - len < xmlStrlen(cur->name + 10)) {
3840 if ((size - len > 4) && (buf[len - 1] != '.'))
3841 strcat(buf, " ...");
3842 return;
3843 }
3844 strcat(buf, (char *) cur->name);
3845 if (cur->next != NULL)
3846 strcat(buf, " ");
3847 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003848 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003849 if (xmlIsBlankNode(cur))
3850 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003851 case XML_CDATA_SECTION_NODE:
3852 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003853 strcat(buf, "CDATA");
3854 if (cur->next != NULL)
3855 strcat(buf, " ");
3856 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003857 case XML_ATTRIBUTE_NODE:
3858 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003859#ifdef LIBXML_DOCB_ENABLED
3860 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003861#endif
3862 case XML_HTML_DOCUMENT_NODE:
3863 case XML_DOCUMENT_TYPE_NODE:
3864 case XML_DOCUMENT_FRAG_NODE:
3865 case XML_NOTATION_NODE:
3866 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003867 strcat(buf, "???");
3868 if (cur->next != NULL)
3869 strcat(buf, " ");
3870 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003871 case XML_ENTITY_NODE:
3872 case XML_PI_NODE:
3873 case XML_DTD_NODE:
3874 case XML_COMMENT_NODE:
3875 case XML_ELEMENT_DECL:
3876 case XML_ATTRIBUTE_DECL:
3877 case XML_ENTITY_DECL:
3878 case XML_XINCLUDE_START:
3879 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003880 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003881 }
3882 cur = cur->next;
3883 }
3884 if (glob) strcat(buf, ")");
3885}
3886
3887/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003888 * xmlValidateElementContent:
3889 * @ctxt: the validation context
3890 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003891 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003892 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003893 *
3894 * Try to validate the content model of an element
3895 *
3896 * returns 1 if valid or 0 if not and -1 in case of error
3897 */
3898
3899static int
3900xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003901 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003902 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003903 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003904 xmlElementContentPtr cont;
3905 const xmlChar *name;
3906
3907 if (elemDecl == NULL)
3908 return(-1);
3909 cont = elemDecl->content;
3910 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003911
3912 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003913 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003914 */
3915 ctxt->vstateMax = 8;
3916 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3917 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3918 if (ctxt->vstateTab == NULL) {
3919 xmlGenericError(xmlGenericErrorContext,
3920 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003921 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003922 }
3923 /*
3924 * The first entry in the stack is reserved to the current state
3925 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00003926 ctxt->nodeMax = 0;
3927 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00003928 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003929 ctxt->vstate = &ctxt->vstateTab[0];
3930 ctxt->vstateNr = 1;
3931 CONT = cont;
3932 NODE = child;
3933 DEPTH = 0;
3934 OCCURS = 0;
3935 STATE = 0;
3936 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003937 if ((ret == -3) && (warn)) {
3938 VWARNING(ctxt->userData,
3939 "Element %s content model is ambiguous\n", name);
3940 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003941 /*
3942 * An entities reference appeared at this level.
3943 * Buid a minimal representation of this node content
3944 * sufficient to run the validation process on it
3945 */
3946 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003947 cur = child;
3948 while (cur != NULL) {
3949 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003950 case XML_ENTITY_REF_NODE:
3951 /*
3952 * Push the current node to be able to roll back
3953 * and process within the entity
3954 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003955 if ((cur->children != NULL) &&
3956 (cur->children->children != NULL)) {
3957 nodeVPush(ctxt, cur);
3958 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003959 continue;
3960 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00003961 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003962 case XML_TEXT_NODE:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003963 case XML_CDATA_SECTION_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003964 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003965 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003966 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003967 case XML_ELEMENT_NODE:
3968 /*
3969 * Allocate a new node and minimally fills in
3970 * what's required
3971 */
3972 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3973 if (tmp == NULL) {
3974 xmlGenericError(xmlGenericErrorContext,
3975 "xmlValidateElementContent : malloc failed\n");
3976 xmlFreeNodeList(repl);
3977 ret = -1;
3978 goto done;
3979 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003980 tmp->type = cur->type;
3981 tmp->name = cur->name;
3982 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003983 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00003984 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003985 if (repl == NULL)
3986 repl = last = tmp;
3987 else {
3988 last->next = tmp;
3989 last = tmp;
3990 }
3991 break;
3992 default:
3993 break;
3994 }
3995 /*
3996 * Switch to next element
3997 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003998 cur = cur->next;
3999 while (cur == NULL) {
4000 cur = nodeVPop(ctxt);
4001 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004002 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004003 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004004 }
4005 }
4006
4007 /*
4008 * Relaunch the validation
4009 */
4010 ctxt->vstate = &ctxt->vstateTab[0];
4011 ctxt->vstateNr = 1;
4012 CONT = cont;
4013 NODE = repl;
4014 DEPTH = 0;
4015 OCCURS = 0;
4016 STATE = 0;
4017 ret = xmlValidateElementType(ctxt);
4018 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004019 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004020 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4021 char expr[5000];
4022 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004023
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004024 expr[0] = 0;
4025 xmlSnprintfElementContent(expr, 5000, cont, 1);
4026 list[0] = 0;
4027 if (repl != NULL)
4028 xmlSnprintfElements(list, 5000, repl, 1);
4029 else
4030 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004031
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004032 if (name != NULL) {
4033 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004034 "Element %s content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004035 name, expr, list);
4036 } else {
4037 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004038 "Element content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004039 expr, list);
4040 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004041 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004042 if (name != NULL) {
4043 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004044 "Element %s content doesn't follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004045 name);
4046 } else {
4047 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004048 "Element content doesn't follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004049 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004050 }
4051 ret = 0;
4052 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004053 if (ret == -3)
4054 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004055
4056
4057done:
4058 /*
4059 * Deallocate the copy if done, and free up the validation stack
4060 */
4061 while (repl != NULL) {
4062 tmp = repl->next;
4063 xmlFree(repl);
4064 repl = tmp;
4065 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004066 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004067 if (ctxt->vstateTab != NULL) {
4068 xmlFree(ctxt->vstateTab);
4069 ctxt->vstateTab = NULL;
4070 }
4071 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004072 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004073 if (ctxt->nodeTab != NULL) {
4074 xmlFree(ctxt->nodeTab);
4075 ctxt->nodeTab = NULL;
4076 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004077 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004078
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004079}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004080
Owen Taylor3473f882001-02-23 17:55:21 +00004081/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004082 * xmlValidateCdataElement:
4083 * @ctxt: the validation context
4084 * @doc: a document instance
4085 * @elem: an element instance
4086 *
4087 * Check that an element follows #CDATA
4088 *
4089 * returns 1 if valid or 0 otherwise
4090 */
4091static int
4092xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4093 xmlNodePtr elem) {
4094 int ret = 1;
4095 xmlNodePtr cur, child;
4096
4097 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4098 return(0);
4099
4100 child = elem->children;
4101
4102 cur = child;
4103 while (cur != NULL) {
4104 switch (cur->type) {
4105 case XML_ENTITY_REF_NODE:
4106 /*
4107 * Push the current node to be able to roll back
4108 * and process within the entity
4109 */
4110 if ((cur->children != NULL) &&
4111 (cur->children->children != NULL)) {
4112 nodeVPush(ctxt, cur);
4113 cur = cur->children->children;
4114 continue;
4115 }
4116 break;
4117 case XML_COMMENT_NODE:
4118 case XML_PI_NODE:
4119 case XML_TEXT_NODE:
4120 case XML_CDATA_SECTION_NODE:
4121 break;
4122 default:
4123 ret = 0;
4124 goto done;
4125 }
4126 /*
4127 * Switch to next element
4128 */
4129 cur = cur->next;
4130 while (cur == NULL) {
4131 cur = nodeVPop(ctxt);
4132 if (cur == NULL)
4133 break;
4134 cur = cur->next;
4135 }
4136 }
4137done:
4138 ctxt->nodeMax = 0;
4139 ctxt->nodeNr = 0;
4140 if (ctxt->nodeTab != NULL) {
4141 xmlFree(ctxt->nodeTab);
4142 ctxt->nodeTab = NULL;
4143 }
4144 return(ret);
4145}
4146
4147/**
Owen Taylor3473f882001-02-23 17:55:21 +00004148 * xmlValidateOneElement:
4149 * @ctxt: the validation context
4150 * @doc: a document instance
4151 * @elem: an element instance
4152 *
4153 * Try to validate a single element and it's attributes,
4154 * basically it does the following checks as described by the
4155 * XML-1.0 recommendation:
4156 * - [ VC: Element Valid ]
4157 * - [ VC: Required Attribute ]
4158 * Then call xmlValidateOneAttribute() for each attribute present.
4159 *
4160 * The ID/IDREF checkings are done separately
4161 *
4162 * returns 1 if valid or 0 otherwise
4163 */
4164
4165int
4166xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4167 xmlNodePtr elem) {
4168 xmlElementPtr elemDecl = NULL;
4169 xmlElementContentPtr cont;
4170 xmlAttributePtr attr;
4171 xmlNodePtr child;
4172 int ret = 1;
4173 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004174 const xmlChar *prefix = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00004175
4176 CHECK_DTD;
4177
4178 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004179 switch (elem->type) {
4180 case XML_ATTRIBUTE_NODE:
4181 VERROR(ctxt->userData,
4182 "Attribute element not expected here\n");
4183 return(0);
4184 case XML_TEXT_NODE:
4185 if (elem->children != NULL) {
4186 VERROR(ctxt->userData, "Text element has childs !\n");
4187 return(0);
4188 }
4189 if (elem->properties != NULL) {
4190 VERROR(ctxt->userData, "Text element has attributes !\n");
4191 return(0);
4192 }
4193 if (elem->ns != NULL) {
4194 VERROR(ctxt->userData, "Text element has namespace !\n");
4195 return(0);
4196 }
4197 if (elem->nsDef != NULL) {
4198 VERROR(ctxt->userData,
4199 "Text element carries namespace definitions !\n");
4200 return(0);
4201 }
4202 if (elem->content == NULL) {
4203 VERROR(ctxt->userData,
4204 "Text element has no content !\n");
4205 return(0);
4206 }
4207 return(1);
4208 case XML_XINCLUDE_START:
4209 case XML_XINCLUDE_END:
4210 return(1);
4211 case XML_CDATA_SECTION_NODE:
4212 case XML_ENTITY_REF_NODE:
4213 case XML_PI_NODE:
4214 case XML_COMMENT_NODE:
4215 return(1);
4216 case XML_ENTITY_NODE:
4217 VERROR(ctxt->userData,
4218 "Entity element not expected here\n");
4219 return(0);
4220 case XML_NOTATION_NODE:
4221 VERROR(ctxt->userData,
4222 "Notation element not expected here\n");
4223 return(0);
4224 case XML_DOCUMENT_NODE:
4225 case XML_DOCUMENT_TYPE_NODE:
4226 case XML_DOCUMENT_FRAG_NODE:
4227 VERROR(ctxt->userData,
4228 "Document element not expected here\n");
4229 return(0);
4230 case XML_HTML_DOCUMENT_NODE:
4231 VERROR(ctxt->userData,
4232 "\n");
4233 return(0);
4234 case XML_ELEMENT_NODE:
4235 break;
4236 default:
4237 VERROR(ctxt->userData,
4238 "unknown element type %d\n", elem->type);
4239 return(0);
4240 }
4241 if (elem->name == NULL) return(0);
4242
4243 /*
4244 * Fetch the declaration for the qualified name
4245 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004246 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4247 prefix = elem->ns->prefix;
4248
4249 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004250 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004251 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004252 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4253 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004254 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004255 }
4256
4257 /*
4258 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004259 * This is "non-strict" validation should be done on the
4260 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004261 */
4262 if (elemDecl == NULL) {
4263 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
4264 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4265 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
4266 }
4267 if (elemDecl == NULL) {
4268 VERROR(ctxt->userData, "No declaration for element %s\n",
4269 elem->name);
4270 return(0);
4271 }
4272
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004273 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004274 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004275 case XML_ELEMENT_TYPE_UNDEFINED:
4276 VERROR(ctxt->userData, "No declaration for element %s\n",
4277 elem->name);
4278 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004279 case XML_ELEMENT_TYPE_EMPTY:
4280 if (elem->children != NULL) {
4281 VERROR(ctxt->userData,
4282 "Element %s was declared EMPTY this one has content\n",
4283 elem->name);
4284 ret = 0;
4285 }
4286 break;
4287 case XML_ELEMENT_TYPE_ANY:
4288 /* I don't think anything is required then */
4289 break;
4290 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004291 /* simple case of declared as #PCDATA */
4292 if ((elemDecl->content != NULL) &&
4293 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4294 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4295 if (!ret) {
4296 VERROR(ctxt->userData,
4297 "Element %s was declared #PCDATA but contains non text nodes\n",
4298 elem->name);
4299 }
4300 break;
4301 }
Owen Taylor3473f882001-02-23 17:55:21 +00004302 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004303 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004304 while (child != NULL) {
4305 if (child->type == XML_ELEMENT_NODE) {
4306 name = child->name;
4307 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4308 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004309 snprintf((char *) qname, sizeof(qname), "%s:%s",
4310 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004311 qname[sizeof(qname) - 1] = 0;
4312 cont = elemDecl->content;
4313 while (cont != NULL) {
4314 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4315 if (xmlStrEqual(cont->name, qname)) break;
4316 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4317 (cont->c1 != NULL) &&
4318 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4319 if (xmlStrEqual(cont->c1->name, qname)) break;
4320 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4321 (cont->c1 == NULL) ||
4322 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4323 /* Internal error !!! */
4324 xmlGenericError(xmlGenericErrorContext,
4325 "Internal: MIXED struct bad\n");
4326 break;
4327 }
4328 cont = cont->c2;
4329 }
4330 if (cont != NULL)
4331 goto child_ok;
4332 }
4333 cont = elemDecl->content;
4334 while (cont != NULL) {
4335 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4336 if (xmlStrEqual(cont->name, name)) break;
4337 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4338 (cont->c1 != NULL) &&
4339 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4340 if (xmlStrEqual(cont->c1->name, name)) break;
4341 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4342 (cont->c1 == NULL) ||
4343 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4344 /* Internal error !!! */
4345 xmlGenericError(xmlGenericErrorContext,
4346 "Internal: MIXED struct bad\n");
4347 break;
4348 }
4349 cont = cont->c2;
4350 }
4351 if (cont == NULL) {
4352 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004353 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004354 name, elem->name);
4355 ret = 0;
4356 }
4357 }
4358child_ok:
4359 child = child->next;
4360 }
4361 break;
4362 case XML_ELEMENT_TYPE_ELEMENT:
4363 child = elem->children;
4364 cont = elemDecl->content;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004365 ret = xmlValidateElementContent(ctxt, child, elemDecl, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00004366 break;
4367 }
4368
4369 /* [ VC: Required Attribute ] */
4370 attr = elemDecl->attributes;
4371 while (attr != NULL) {
4372 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4373 xmlAttrPtr attrib;
4374 int qualified = -1;
4375
4376 attrib = elem->properties;
4377 while (attrib != NULL) {
4378 if (xmlStrEqual(attrib->name, attr->name)) {
4379 if (attr->prefix != NULL) {
4380 xmlNsPtr nameSpace = attrib->ns;
4381
4382 if (nameSpace == NULL)
4383 nameSpace = elem->ns;
4384 /*
4385 * qualified names handling is problematic, having a
4386 * different prefix should be possible but DTDs don't
4387 * allow to define the URI instead of the prefix :-(
4388 */
4389 if (nameSpace == NULL) {
4390 if (qualified < 0)
4391 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004392 } else if (!xmlStrEqual(nameSpace->prefix,
4393 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004394 if (qualified < 1)
4395 qualified = 1;
4396 } else
4397 goto found;
4398 } else {
4399 /*
4400 * We should allow applications to define namespaces
4401 * for their application even if the DTD doesn't
4402 * carry one, otherwise, basically we would always
4403 * break.
4404 */
4405 goto found;
4406 }
4407 }
4408 attrib = attrib->next;
4409 }
4410 if (qualified == -1) {
4411 if (attr->prefix == NULL) {
4412 VERROR(ctxt->userData,
4413 "Element %s doesn't carry attribute %s\n",
4414 elem->name, attr->name);
4415 ret = 0;
4416 } else {
4417 VERROR(ctxt->userData,
4418 "Element %s doesn't carry attribute %s:%s\n",
4419 elem->name, attr->prefix,attr->name);
4420 ret = 0;
4421 }
4422 } else if (qualified == 0) {
4423 VWARNING(ctxt->userData,
4424 "Element %s required attribute %s:%s has no prefix\n",
4425 elem->name, attr->prefix,attr->name);
4426 } else if (qualified == 1) {
4427 VWARNING(ctxt->userData,
4428 "Element %s required attribute %s:%s has different prefix\n",
4429 elem->name, attr->prefix,attr->name);
4430 }
4431 }
4432found:
4433 attr = attr->nexth;
4434 }
4435 return(ret);
4436}
4437
4438/**
4439 * xmlValidateRoot:
4440 * @ctxt: the validation context
4441 * @doc: a document instance
4442 *
4443 * Try to validate a the root element
4444 * basically it does the following check as described by the
4445 * XML-1.0 recommendation:
4446 * - [ VC: Root Element Type ]
4447 * it doesn't try to recurse or apply other check to the element
4448 *
4449 * returns 1 if valid or 0 otherwise
4450 */
4451
4452int
4453xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4454 xmlNodePtr root;
4455 if (doc == NULL) return(0);
4456
4457 root = xmlDocGetRootElement(doc);
4458 if ((root == NULL) || (root->name == NULL)) {
4459 VERROR(ctxt->userData, "Not valid: no root element\n");
4460 return(0);
4461 }
4462
4463 /*
4464 * When doing post validation against a separate DTD, those may
4465 * no internal subset has been generated
4466 */
4467 if ((doc->intSubset != NULL) &&
4468 (doc->intSubset->name != NULL)) {
4469 /*
4470 * Check first the document root against the NQName
4471 */
4472 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4473 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4474 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004475 snprintf((char *) qname, sizeof(qname), "%s:%s",
4476 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004477 qname[sizeof(qname) - 1] = 0;
4478 if (xmlStrEqual(doc->intSubset->name, qname))
4479 goto name_ok;
4480 }
4481 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4482 (xmlStrEqual(root->name, BAD_CAST "html")))
4483 goto name_ok;
4484 VERROR(ctxt->userData,
4485 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4486 root->name, doc->intSubset->name);
4487 return(0);
4488
4489 }
4490 }
4491name_ok:
4492 return(1);
4493}
4494
4495
4496/**
4497 * xmlValidateElement:
4498 * @ctxt: the validation context
4499 * @doc: a document instance
4500 * @elem: an element instance
4501 *
4502 * Try to validate the subtree under an element
4503 *
4504 * returns 1 if valid or 0 otherwise
4505 */
4506
4507int
4508xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4509 xmlNodePtr child;
4510 xmlAttrPtr attr;
4511 xmlChar *value;
4512 int ret = 1;
4513
4514 if (elem == NULL) return(0);
4515
4516 /*
4517 * XInclude elements were added after parsing in the infoset,
4518 * they don't really mean anything validation wise.
4519 */
4520 if ((elem->type == XML_XINCLUDE_START) ||
4521 (elem->type == XML_XINCLUDE_END))
4522 return(1);
4523
4524 CHECK_DTD;
4525
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004526 /*
4527 * Entities references have to be handled separately
4528 */
4529 if (elem->type == XML_ENTITY_REF_NODE) {
4530 return(1);
4531 }
4532
Owen Taylor3473f882001-02-23 17:55:21 +00004533 ret &= xmlValidateOneElement(ctxt, doc, elem);
4534 attr = elem->properties;
4535 while(attr != NULL) {
4536 value = xmlNodeListGetString(doc, attr->children, 0);
4537 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4538 if (value != NULL)
4539 xmlFree(value);
4540 attr= attr->next;
4541 }
4542 child = elem->children;
4543 while (child != NULL) {
4544 ret &= xmlValidateElement(ctxt, doc, child);
4545 child = child->next;
4546 }
4547
4548 return(ret);
4549}
4550
Daniel Veillard8730c562001-02-26 10:49:57 +00004551/**
4552 * xmlValidateRef:
4553 * @ref: A reference to be validated
4554 * @ctxt: Validation context
4555 * @name: Name of ID we are searching for
4556 *
4557 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004558static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004559xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004560 const xmlChar *name) {
4561 xmlAttrPtr id;
4562 xmlAttrPtr attr;
4563
4564 if (ref == NULL)
4565 return;
4566 attr = ref->attr;
4567 if (attr == NULL)
4568 return;
4569 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4570 id = xmlGetID(ctxt->doc, name);
4571 if (id == NULL) {
4572 VERROR(ctxt->userData,
4573 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4574 attr->name, name);
4575 ctxt->valid = 0;
4576 }
4577 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4578 xmlChar *dup, *str = NULL, *cur, save;
4579
4580 dup = xmlStrdup(name);
4581 if (dup == NULL) {
4582 ctxt->valid = 0;
4583 return;
4584 }
4585 cur = dup;
4586 while (*cur != 0) {
4587 str = cur;
4588 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4589 save = *cur;
4590 *cur = 0;
4591 id = xmlGetID(ctxt->doc, str);
4592 if (id == NULL) {
4593 VERROR(ctxt->userData,
4594 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4595 attr->name, str);
4596 ctxt->valid = 0;
4597 }
4598 if (save == 0)
4599 break;
4600 *cur = save;
4601 while (IS_BLANK(*cur)) cur++;
4602 }
4603 xmlFree(dup);
4604 }
4605}
4606
4607/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004608 * xmlWalkValidateList:
4609 * @data: Contents of current link
4610 * @user: Value supplied by the user
4611 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004612 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004613 */
4614static int
4615xmlWalkValidateList(const void *data, const void *user)
4616{
4617 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4618 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4619 return 1;
4620}
4621
4622/**
4623 * xmlValidateCheckRefCallback:
4624 * @ref_list: List of references
4625 * @ctxt: Validation context
4626 * @name: Name of ID we are searching for
4627 *
4628 */
4629static void
4630xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4631 const xmlChar *name) {
4632 xmlValidateMemo memo;
4633
4634 if (ref_list == NULL)
4635 return;
4636 memo.ctxt = ctxt;
4637 memo.name = name;
4638
4639 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4640
4641}
4642
4643/**
Owen Taylor3473f882001-02-23 17:55:21 +00004644 * xmlValidateDocumentFinal:
4645 * @ctxt: the validation context
4646 * @doc: a document instance
4647 *
4648 * Does the final step for the document validation once all the
4649 * incremental validation steps have been completed
4650 *
4651 * basically it does the following checks described by the XML Rec
4652 *
4653 *
4654 * returns 1 if valid or 0 otherwise
4655 */
4656
4657int
4658xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4659 xmlRefTablePtr table;
4660
4661 if (doc == NULL) {
4662 xmlGenericError(xmlGenericErrorContext,
4663 "xmlValidateDocumentFinal: doc == NULL\n");
4664 return(0);
4665 }
4666
4667 /*
4668 * Check all the NOTATION/NOTATIONS attributes
4669 */
4670 /*
4671 * Check all the ENTITY/ENTITIES attributes definition for validity
4672 */
4673 /*
4674 * Check all the IDREF/IDREFS attributes definition for validity
4675 */
4676 table = (xmlRefTablePtr) doc->refs;
4677 ctxt->doc = doc;
4678 ctxt->valid = 1;
4679 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4680 return(ctxt->valid);
4681}
4682
4683/**
4684 * xmlValidateDtd:
4685 * @ctxt: the validation context
4686 * @doc: a document instance
4687 * @dtd: a dtd instance
4688 *
4689 * Try to validate the document against the dtd instance
4690 *
4691 * basically it does check all the definitions in the DtD.
4692 *
4693 * returns 1 if valid or 0 otherwise
4694 */
4695
4696int
4697xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4698 int ret;
4699 xmlDtdPtr oldExt;
4700 xmlNodePtr root;
4701
4702 if (dtd == NULL) return(0);
4703 if (doc == NULL) return(0);
4704 oldExt = doc->extSubset;
4705 doc->extSubset = dtd;
4706 ret = xmlValidateRoot(ctxt, doc);
4707 if (ret == 0) {
4708 doc->extSubset = oldExt;
4709 return(ret);
4710 }
4711 if (doc->ids != NULL) {
4712 xmlFreeIDTable(doc->ids);
4713 doc->ids = NULL;
4714 }
4715 if (doc->refs != NULL) {
4716 xmlFreeRefTable(doc->refs);
4717 doc->refs = NULL;
4718 }
4719 root = xmlDocGetRootElement(doc);
4720 ret = xmlValidateElement(ctxt, doc, root);
4721 ret &= xmlValidateDocumentFinal(ctxt, doc);
4722 doc->extSubset = oldExt;
4723 return(ret);
4724}
4725
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004726static void
Owen Taylor3473f882001-02-23 17:55:21 +00004727xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004728 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004729 if (cur == NULL)
4730 return;
4731 switch (cur->atype) {
4732 case XML_ATTRIBUTE_CDATA:
4733 case XML_ATTRIBUTE_ID:
4734 case XML_ATTRIBUTE_IDREF :
4735 case XML_ATTRIBUTE_IDREFS:
4736 case XML_ATTRIBUTE_NMTOKEN:
4737 case XML_ATTRIBUTE_NMTOKENS:
4738 case XML_ATTRIBUTE_ENUMERATION:
4739 break;
4740 case XML_ATTRIBUTE_ENTITY:
4741 case XML_ATTRIBUTE_ENTITIES:
4742 case XML_ATTRIBUTE_NOTATION:
4743 if (cur->defaultValue != NULL) {
4744 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4745 cur->name, cur->atype, cur->defaultValue);
4746 }
4747 if (cur->tree != NULL) {
4748 xmlEnumerationPtr tree = cur->tree;
4749 while (tree != NULL) {
4750 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4751 cur->name, cur->atype, tree->name);
4752 tree = tree->next;
4753 }
4754 }
4755 }
4756}
4757
4758/**
4759 * xmlValidateDtdFinal:
4760 * @ctxt: the validation context
4761 * @doc: a document instance
4762 *
4763 * Does the final step for the dtds validation once all the
4764 * subsets have been parsed
4765 *
4766 * basically it does the following checks described by the XML Rec
4767 * - check that ENTITY and ENTITIES type attributes default or
4768 * possible values matches one of the defined entities.
4769 * - check that NOTATION type attributes default or
4770 * possible values matches one of the defined notations.
4771 *
4772 * returns 1 if valid or 0 otherwise
4773 */
4774
4775int
4776xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4777 int ret = 1;
4778 xmlDtdPtr dtd;
4779 xmlAttributeTablePtr table;
4780
4781 if (doc == NULL) return(0);
4782 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4783 return(0);
4784 ctxt->doc = doc;
4785 ctxt->valid = ret;
4786 dtd = doc->intSubset;
4787 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4788 table = (xmlAttributeTablePtr) dtd->attributes;
4789 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4790 }
4791 dtd = doc->extSubset;
4792 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4793 table = (xmlAttributeTablePtr) dtd->attributes;
4794 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4795 }
4796 return(ctxt->valid);
4797}
4798
4799/**
4800 * xmlValidateDocument:
4801 * @ctxt: the validation context
4802 * @doc: a document instance
4803 *
4804 * Try to validate the document instance
4805 *
4806 * basically it does the all the checks described by the XML Rec
4807 * i.e. validates the internal and external subset (if present)
4808 * and validate the document tree.
4809 *
4810 * returns 1 if valid or 0 otherwise
4811 */
4812
4813int
4814xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4815 int ret;
4816 xmlNodePtr root;
4817
4818 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4819 return(0);
4820 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4821 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4822 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4823 doc->intSubset->SystemID);
4824 if (doc->extSubset == NULL) {
4825 if (doc->intSubset->SystemID != NULL) {
4826 VERROR(ctxt->userData,
4827 "Could not load the external subset \"%s\"\n",
4828 doc->intSubset->SystemID);
4829 } else {
4830 VERROR(ctxt->userData,
4831 "Could not load the external subset \"%s\"\n",
4832 doc->intSubset->ExternalID);
4833 }
4834 return(0);
4835 }
4836 }
4837
4838 if (doc->ids != NULL) {
4839 xmlFreeIDTable(doc->ids);
4840 doc->ids = NULL;
4841 }
4842 if (doc->refs != NULL) {
4843 xmlFreeRefTable(doc->refs);
4844 doc->refs = NULL;
4845 }
4846 ret = xmlValidateDtdFinal(ctxt, doc);
4847 if (!xmlValidateRoot(ctxt, doc)) return(0);
4848
4849 root = xmlDocGetRootElement(doc);
4850 ret &= xmlValidateElement(ctxt, doc, root);
4851 ret &= xmlValidateDocumentFinal(ctxt, doc);
4852 return(ret);
4853}
4854
4855
4856/************************************************************************
4857 * *
4858 * Routines for dynamic validation editing *
4859 * *
4860 ************************************************************************/
4861
4862/**
4863 * xmlValidGetPotentialChildren:
4864 * @ctree: an element content tree
4865 * @list: an array to store the list of child names
4866 * @len: a pointer to the number of element in the list
4867 * @max: the size of the array
4868 *
4869 * Build/extend a list of potential children allowed by the content tree
4870 *
4871 * returns the number of element in the list, or -1 in case of error.
4872 */
4873
4874int
4875xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4876 int *len, int max) {
4877 int i;
4878
4879 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4880 return(-1);
4881 if (*len >= max) return(*len);
4882
4883 switch (ctree->type) {
4884 case XML_ELEMENT_CONTENT_PCDATA:
4885 for (i = 0; i < *len;i++)
4886 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4887 list[(*len)++] = BAD_CAST "#PCDATA";
4888 break;
4889 case XML_ELEMENT_CONTENT_ELEMENT:
4890 for (i = 0; i < *len;i++)
4891 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4892 list[(*len)++] = ctree->name;
4893 break;
4894 case XML_ELEMENT_CONTENT_SEQ:
4895 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4896 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4897 break;
4898 case XML_ELEMENT_CONTENT_OR:
4899 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4900 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4901 break;
4902 }
4903
4904 return(*len);
4905}
4906
4907/**
4908 * xmlValidGetValidElements:
4909 * @prev: an element to insert after
4910 * @next: an element to insert next
4911 * @list: an array to store the list of child names
4912 * @max: the size of the array
4913 *
4914 * This function returns the list of authorized children to insert
4915 * within an existing tree while respecting the validity constraints
4916 * forced by the Dtd. The insertion point is defined using @prev and
4917 * @next in the following ways:
4918 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4919 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4920 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4921 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4922 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4923 *
4924 * pointers to the element names are inserted at the beginning of the array
4925 * and do not need to be freed.
4926 *
4927 * returns the number of element in the list, or -1 in case of error. If
4928 * the function returns the value @max the caller is invited to grow the
4929 * receiving array and retry.
4930 */
4931
4932int
4933xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4934 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004935 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00004936 int nb_valid_elements = 0;
4937 const xmlChar *elements[256];
4938 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004939 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00004940
4941 xmlNode *ref_node;
4942 xmlNode *parent;
4943 xmlNode *test_node;
4944
4945 xmlNode *prev_next;
4946 xmlNode *next_prev;
4947 xmlNode *parent_childs;
4948 xmlNode *parent_last;
4949
4950 xmlElement *element_desc;
4951
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004952 vctxt.userData = NULL;
4953 vctxt.error = NULL;
4954 vctxt.warning = NULL;
4955
Owen Taylor3473f882001-02-23 17:55:21 +00004956 if (prev == NULL && next == NULL)
4957 return(-1);
4958
4959 if (list == NULL) return(-1);
4960 if (max <= 0) return(-1);
4961
4962 nb_valid_elements = 0;
4963 ref_node = prev ? prev : next;
4964 parent = ref_node->parent;
4965
4966 /*
4967 * Retrieves the parent element declaration
4968 */
4969 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4970 parent->name);
4971 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4972 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4973 parent->name);
4974 if (element_desc == NULL) return(-1);
4975
4976 /*
4977 * Do a backup of the current tree structure
4978 */
4979 prev_next = prev ? prev->next : NULL;
4980 next_prev = next ? next->prev : NULL;
4981 parent_childs = parent->children;
4982 parent_last = parent->last;
4983
4984 /*
4985 * Creates a dummy node and insert it into the tree
4986 */
4987 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4988 test_node->doc = ref_node->doc;
4989 test_node->parent = parent;
4990 test_node->prev = prev;
4991 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00004992 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00004993
4994 if (prev) prev->next = test_node;
4995 else parent->children = test_node;
4996
4997 if (next) next->prev = test_node;
4998 else parent->last = test_node;
4999
5000 /*
5001 * Insert each potential child node and check if the parent is
5002 * still valid
5003 */
5004 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5005 elements, &nb_elements, 256);
5006
5007 for (i = 0;i < nb_elements;i++) {
5008 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005009 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005010 int j;
5011
5012 for (j = 0; j < nb_valid_elements;j++)
5013 if (xmlStrEqual(elements[i], list[j])) break;
5014 list[nb_valid_elements++] = elements[i];
5015 if (nb_valid_elements >= max) break;
5016 }
5017 }
5018
5019 /*
5020 * Restore the tree structure
5021 */
5022 if (prev) prev->next = prev_next;
5023 if (next) next->prev = next_prev;
5024 parent->children = parent_childs;
5025 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005026
5027 /*
5028 * Free up the dummy node
5029 */
5030 test_node->name = name;
5031 xmlFreeNode(test_node);
5032
Owen Taylor3473f882001-02-23 17:55:21 +00005033 return(nb_valid_elements);
5034}