blob: 55c7b9e2c395d8e3c62f246b3a8ef3e3f777bf88 [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));
1439 cur->atype = attr->atype;
1440 cur->def = attr->def;
1441 cur->tree = xmlCopyEnumeration(attr->tree);
1442 if (attr->elem != NULL)
1443 cur->elem = xmlStrdup(attr->elem);
1444 if (attr->name != NULL)
1445 cur->name = xmlStrdup(attr->name);
1446 if (attr->defaultValue != NULL)
1447 cur->defaultValue = xmlStrdup(attr->defaultValue);
1448 return(cur);
1449}
1450
1451/**
1452 * xmlCopyAttributeTable:
1453 * @table: An attribute table
1454 *
1455 * Build a copy of an attribute table.
1456 *
1457 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1458 */
1459xmlAttributeTablePtr
1460xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1461 return((xmlAttributeTablePtr) xmlHashCopy(table,
1462 (xmlHashCopier) xmlCopyAttribute));
1463}
1464
1465/**
1466 * xmlDumpAttributeDecl:
1467 * @buf: the XML buffer output
1468 * @attr: An attribute declaration
1469 *
1470 * This will dump the content of the attribute declaration as an XML
1471 * DTD definition
1472 */
1473void
1474xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1475 xmlBufferWriteChar(buf, "<!ATTLIST ");
1476 xmlBufferWriteCHAR(buf, attr->elem);
1477 xmlBufferWriteChar(buf, " ");
1478 if (attr->prefix != NULL) {
1479 xmlBufferWriteCHAR(buf, attr->prefix);
1480 xmlBufferWriteChar(buf, ":");
1481 }
1482 xmlBufferWriteCHAR(buf, attr->name);
1483 switch (attr->atype) {
1484 case XML_ATTRIBUTE_CDATA:
1485 xmlBufferWriteChar(buf, " CDATA");
1486 break;
1487 case XML_ATTRIBUTE_ID:
1488 xmlBufferWriteChar(buf, " ID");
1489 break;
1490 case XML_ATTRIBUTE_IDREF:
1491 xmlBufferWriteChar(buf, " IDREF");
1492 break;
1493 case XML_ATTRIBUTE_IDREFS:
1494 xmlBufferWriteChar(buf, " IDREFS");
1495 break;
1496 case XML_ATTRIBUTE_ENTITY:
1497 xmlBufferWriteChar(buf, " ENTITY");
1498 break;
1499 case XML_ATTRIBUTE_ENTITIES:
1500 xmlBufferWriteChar(buf, " ENTITIES");
1501 break;
1502 case XML_ATTRIBUTE_NMTOKEN:
1503 xmlBufferWriteChar(buf, " NMTOKEN");
1504 break;
1505 case XML_ATTRIBUTE_NMTOKENS:
1506 xmlBufferWriteChar(buf, " NMTOKENS");
1507 break;
1508 case XML_ATTRIBUTE_ENUMERATION:
1509 xmlBufferWriteChar(buf, " (");
1510 xmlDumpEnumeration(buf, attr->tree);
1511 break;
1512 case XML_ATTRIBUTE_NOTATION:
1513 xmlBufferWriteChar(buf, " NOTATION (");
1514 xmlDumpEnumeration(buf, attr->tree);
1515 break;
1516 default:
1517 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001518 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001519 attr->atype);
1520 }
1521 switch (attr->def) {
1522 case XML_ATTRIBUTE_NONE:
1523 break;
1524 case XML_ATTRIBUTE_REQUIRED:
1525 xmlBufferWriteChar(buf, " #REQUIRED");
1526 break;
1527 case XML_ATTRIBUTE_IMPLIED:
1528 xmlBufferWriteChar(buf, " #IMPLIED");
1529 break;
1530 case XML_ATTRIBUTE_FIXED:
1531 xmlBufferWriteChar(buf, " #FIXED");
1532 break;
1533 default:
1534 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001535 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001536 attr->def);
1537 }
1538 if (attr->defaultValue != NULL) {
1539 xmlBufferWriteChar(buf, " ");
1540 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1541 }
1542 xmlBufferWriteChar(buf, ">\n");
1543}
1544
1545/**
1546 * xmlDumpAttributeTable:
1547 * @buf: the XML buffer output
1548 * @table: An attribute table
1549 *
1550 * This will dump the content of the attribute table as an XML DTD definition
1551 */
1552void
1553xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1554 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1555}
1556
1557/************************************************************************
1558 * *
1559 * NOTATIONs *
1560 * *
1561 ************************************************************************/
1562/**
1563 * xmlCreateNotationTable:
1564 *
1565 * create and initialize an empty notation hash table.
1566 *
1567 * Returns the xmlNotationTablePtr just created or NULL in case
1568 * of error.
1569 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001570static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001571xmlCreateNotationTable(void) {
1572 return(xmlHashCreate(0));
1573}
1574
1575/**
1576 * xmlFreeNotation:
1577 * @not: A notation
1578 *
1579 * Deallocate the memory used by an notation definition
1580 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001581static void
Owen Taylor3473f882001-02-23 17:55:21 +00001582xmlFreeNotation(xmlNotationPtr nota) {
1583 if (nota == NULL) return;
1584 if (nota->name != NULL)
1585 xmlFree((xmlChar *) nota->name);
1586 if (nota->PublicID != NULL)
1587 xmlFree((xmlChar *) nota->PublicID);
1588 if (nota->SystemID != NULL)
1589 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001590 xmlFree(nota);
1591}
1592
1593
1594/**
1595 * xmlAddNotationDecl:
1596 * @dtd: pointer to the DTD
1597 * @ctxt: the validation context
1598 * @name: the entity name
1599 * @PublicID: the public identifier or NULL
1600 * @SystemID: the system identifier or NULL
1601 *
1602 * Register a new notation declaration
1603 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001604 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001605 */
1606xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001607xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001608 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001609 const xmlChar *PublicID, const xmlChar *SystemID) {
1610 xmlNotationPtr ret;
1611 xmlNotationTablePtr table;
1612
1613 if (dtd == NULL) {
1614 xmlGenericError(xmlGenericErrorContext,
1615 "xmlAddNotationDecl: dtd == NULL\n");
1616 return(NULL);
1617 }
1618 if (name == NULL) {
1619 xmlGenericError(xmlGenericErrorContext,
1620 "xmlAddNotationDecl: name == NULL\n");
1621 return(NULL);
1622 }
1623 if ((PublicID == NULL) && (SystemID == NULL)) {
1624 xmlGenericError(xmlGenericErrorContext,
1625 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1626 }
1627
1628 /*
1629 * Create the Notation table if needed.
1630 */
1631 table = (xmlNotationTablePtr) dtd->notations;
1632 if (table == NULL)
1633 dtd->notations = table = xmlCreateNotationTable();
1634 if (table == NULL) {
1635 xmlGenericError(xmlGenericErrorContext,
1636 "xmlAddNotationDecl: Table creation failed!\n");
1637 return(NULL);
1638 }
1639
1640 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1641 if (ret == NULL) {
1642 xmlGenericError(xmlGenericErrorContext,
1643 "xmlAddNotationDecl: out of memory\n");
1644 return(NULL);
1645 }
1646 memset(ret, 0, sizeof(xmlNotation));
1647
1648 /*
1649 * fill the structure.
1650 */
1651 ret->name = xmlStrdup(name);
1652 if (SystemID != NULL)
1653 ret->SystemID = xmlStrdup(SystemID);
1654 if (PublicID != NULL)
1655 ret->PublicID = xmlStrdup(PublicID);
1656
1657 /*
1658 * Validity Check:
1659 * Check the DTD for previous declarations of the ATTLIST
1660 */
1661 if (xmlHashAddEntry(table, name, ret)) {
1662 xmlGenericError(xmlGenericErrorContext,
1663 "xmlAddNotationDecl: %s already defined\n", name);
1664 xmlFreeNotation(ret);
1665 return(NULL);
1666 }
1667 return(ret);
1668}
1669
1670/**
1671 * xmlFreeNotationTable:
1672 * @table: An notation table
1673 *
1674 * Deallocate the memory used by an entities hash table.
1675 */
1676void
1677xmlFreeNotationTable(xmlNotationTablePtr table) {
1678 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1679}
1680
1681/**
1682 * xmlCopyNotation:
1683 * @nota: A notation
1684 *
1685 * Build a copy of a notation.
1686 *
1687 * Returns the new xmlNotationPtr or NULL in case of error.
1688 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001689static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001690xmlCopyNotation(xmlNotationPtr nota) {
1691 xmlNotationPtr cur;
1692
1693 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1694 if (cur == NULL) {
1695 xmlGenericError(xmlGenericErrorContext,
1696 "xmlCopyNotation: out of memory !\n");
1697 return(NULL);
1698 }
1699 if (nota->name != NULL)
1700 cur->name = xmlStrdup(nota->name);
1701 else
1702 cur->name = NULL;
1703 if (nota->PublicID != NULL)
1704 cur->PublicID = xmlStrdup(nota->PublicID);
1705 else
1706 cur->PublicID = NULL;
1707 if (nota->SystemID != NULL)
1708 cur->SystemID = xmlStrdup(nota->SystemID);
1709 else
1710 cur->SystemID = NULL;
1711 return(cur);
1712}
1713
1714/**
1715 * xmlCopyNotationTable:
1716 * @table: A notation table
1717 *
1718 * Build a copy of a notation table.
1719 *
1720 * Returns the new xmlNotationTablePtr or NULL in case of error.
1721 */
1722xmlNotationTablePtr
1723xmlCopyNotationTable(xmlNotationTablePtr table) {
1724 return((xmlNotationTablePtr) xmlHashCopy(table,
1725 (xmlHashCopier) xmlCopyNotation));
1726}
1727
1728/**
1729 * xmlDumpNotationDecl:
1730 * @buf: the XML buffer output
1731 * @nota: A notation declaration
1732 *
1733 * This will dump the content the notation declaration as an XML DTD definition
1734 */
1735void
1736xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1737 xmlBufferWriteChar(buf, "<!NOTATION ");
1738 xmlBufferWriteCHAR(buf, nota->name);
1739 if (nota->PublicID != NULL) {
1740 xmlBufferWriteChar(buf, " PUBLIC ");
1741 xmlBufferWriteQuotedString(buf, nota->PublicID);
1742 if (nota->SystemID != NULL) {
1743 xmlBufferWriteChar(buf, " ");
1744 xmlBufferWriteCHAR(buf, nota->SystemID);
1745 }
1746 } else {
1747 xmlBufferWriteChar(buf, " SYSTEM ");
1748 xmlBufferWriteCHAR(buf, nota->SystemID);
1749 }
1750 xmlBufferWriteChar(buf, " >\n");
1751}
1752
1753/**
1754 * xmlDumpNotationTable:
1755 * @buf: the XML buffer output
1756 * @table: A notation table
1757 *
1758 * This will dump the content of the notation table as an XML DTD definition
1759 */
1760void
1761xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1762 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1763}
1764
1765/************************************************************************
1766 * *
1767 * IDs *
1768 * *
1769 ************************************************************************/
1770/**
1771 * xmlCreateIDTable:
1772 *
1773 * create and initialize an empty id hash table.
1774 *
1775 * Returns the xmlIDTablePtr just created or NULL in case
1776 * of error.
1777 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001778static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001779xmlCreateIDTable(void) {
1780 return(xmlHashCreate(0));
1781}
1782
1783/**
1784 * xmlFreeID:
1785 * @not: A id
1786 *
1787 * Deallocate the memory used by an id definition
1788 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001789static void
Owen Taylor3473f882001-02-23 17:55:21 +00001790xmlFreeID(xmlIDPtr id) {
1791 if (id == NULL) return;
1792 if (id->value != NULL)
1793 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001794 xmlFree(id);
1795}
1796
1797/**
1798 * xmlAddID:
1799 * @ctxt: the validation context
1800 * @doc: pointer to the document
1801 * @value: the value name
1802 * @attr: the attribute holding the ID
1803 *
1804 * Register a new id declaration
1805 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001806 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001807 */
1808xmlIDPtr
1809xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1810 xmlAttrPtr attr) {
1811 xmlIDPtr ret;
1812 xmlIDTablePtr table;
1813
1814 if (doc == NULL) {
1815 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001816 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001817 return(NULL);
1818 }
1819 if (value == NULL) {
1820 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001821 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001822 return(NULL);
1823 }
1824 if (attr == NULL) {
1825 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001826 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001827 return(NULL);
1828 }
1829
1830 /*
1831 * Create the ID table if needed.
1832 */
1833 table = (xmlIDTablePtr) doc->ids;
1834 if (table == NULL)
1835 doc->ids = table = xmlCreateIDTable();
1836 if (table == NULL) {
1837 xmlGenericError(xmlGenericErrorContext,
1838 "xmlAddID: Table creation failed!\n");
1839 return(NULL);
1840 }
1841
1842 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1843 if (ret == NULL) {
1844 xmlGenericError(xmlGenericErrorContext,
1845 "xmlAddID: out of memory\n");
1846 return(NULL);
1847 }
1848
1849 /*
1850 * fill the structure.
1851 */
1852 ret->value = xmlStrdup(value);
1853 ret->attr = attr;
1854
1855 if (xmlHashAddEntry(table, value, ret) < 0) {
1856 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001857 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001858 */
1859 VERROR(ctxt->userData, "ID %s already defined\n", value);
1860 xmlFreeID(ret);
1861 return(NULL);
1862 }
1863 return(ret);
1864}
1865
1866/**
1867 * xmlFreeIDTable:
1868 * @table: An id table
1869 *
1870 * Deallocate the memory used by an ID hash table.
1871 */
1872void
1873xmlFreeIDTable(xmlIDTablePtr table) {
1874 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1875}
1876
1877/**
1878 * xmlIsID:
1879 * @doc: the document
1880 * @elem: the element carrying the attribute
1881 * @attr: the attribute
1882 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001883 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001884 * then this is simple, otherwise we use an heuristic: name ID (upper
1885 * or lowercase).
1886 *
1887 * Returns 0 or 1 depending on the lookup result
1888 */
1889int
1890xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1891 if (doc == NULL) return(0);
1892 if (attr == NULL) return(0);
1893 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1894 return(0);
1895 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1896 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1897 (xmlStrEqual(BAD_CAST "name", attr->name)))
1898 return(1);
1899 return(0);
1900 } else {
1901 xmlAttributePtr attrDecl;
1902
1903 if (elem == NULL) return(0);
1904 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1905 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1906 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1907 attr->name);
1908
1909 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1910 return(1);
1911 }
1912 return(0);
1913}
1914
1915/**
1916 * xmlRemoveID
1917 * @doc: the document
1918 * @attr: the attribute
1919 *
1920 * Remove the given attribute from the ID table maintained internally.
1921 *
1922 * Returns -1 if the lookup failed and 0 otherwise
1923 */
1924int
1925xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1926 xmlAttrPtr cur;
1927 xmlIDTablePtr table;
1928 xmlChar *ID;
1929
1930 if (doc == NULL) return(-1);
1931 if (attr == NULL) return(-1);
1932 table = (xmlIDTablePtr) doc->ids;
1933 if (table == NULL)
1934 return(-1);
1935
1936 if (attr == NULL)
1937 return(-1);
1938 ID = xmlNodeListGetString(doc, attr->children, 1);
1939 if (ID == NULL)
1940 return(-1);
1941 cur = xmlHashLookup(table, ID);
1942 if (cur != attr) {
1943 xmlFree(ID);
1944 return(-1);
1945 }
1946 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1947 xmlFree(ID);
1948 return(0);
1949}
1950
1951/**
1952 * xmlGetID:
1953 * @doc: pointer to the document
1954 * @ID: the ID value
1955 *
1956 * Search the attribute declaring the given ID
1957 *
1958 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1959 */
1960xmlAttrPtr
1961xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1962 xmlIDTablePtr table;
1963 xmlIDPtr id;
1964
1965 if (doc == NULL) {
1966 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1967 return(NULL);
1968 }
1969
1970 if (ID == NULL) {
1971 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1972 return(NULL);
1973 }
1974
1975 table = (xmlIDTablePtr) doc->ids;
1976 if (table == NULL)
1977 return(NULL);
1978
1979 id = xmlHashLookup(table, ID);
1980 if (id == NULL)
1981 return(NULL);
1982 return(id->attr);
1983}
1984
1985/************************************************************************
1986 * *
1987 * Refs *
1988 * *
1989 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001990typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001991{
1992 xmlListPtr l;
1993 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001994} xmlRemoveMemo;
1995
1996typedef xmlRemoveMemo *xmlRemoveMemoPtr;
1997
1998typedef struct xmlValidateMemo_t
1999{
2000 xmlValidCtxtPtr ctxt;
2001 const xmlChar *name;
2002} xmlValidateMemo;
2003
2004typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002005
2006/**
2007 * xmlCreateRefTable:
2008 *
2009 * create and initialize an empty ref hash table.
2010 *
2011 * Returns the xmlRefTablePtr just created or NULL in case
2012 * of error.
2013 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002014static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002015xmlCreateRefTable(void) {
2016 return(xmlHashCreate(0));
2017}
2018
2019/**
2020 * xmlFreeRef:
2021 * @lk: A list link
2022 *
2023 * Deallocate the memory used by a ref definition
2024 */
2025static void
2026xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002027 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2028 if (ref == NULL) return;
2029 if (ref->value != NULL)
2030 xmlFree((xmlChar *)ref->value);
2031 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002032}
2033
2034/**
2035 * xmlFreeRefList:
2036 * @list_ref: A list of references.
2037 *
2038 * Deallocate the memory used by a list of references
2039 */
2040static void
2041xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002042 if (list_ref == NULL) return;
2043 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002044}
2045
2046/**
2047 * xmlWalkRemoveRef:
2048 * @data: Contents of current link
2049 * @user: Value supplied by the user
2050 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002051 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002052 */
2053static int
2054xmlWalkRemoveRef(const void *data, const void *user)
2055{
Daniel Veillard37721922001-05-04 15:21:12 +00002056 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2057 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2058 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002059
Daniel Veillard37721922001-05-04 15:21:12 +00002060 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2061 xmlListRemoveFirst(ref_list, (void *)data);
2062 return 0;
2063 }
2064 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002065}
2066
2067/**
2068 * xmlAddRef:
2069 * @ctxt: the validation context
2070 * @doc: pointer to the document
2071 * @value: the value name
2072 * @attr: the attribute holding the Ref
2073 *
2074 * Register a new ref declaration
2075 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002076 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002077 */
2078xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002079xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002080 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002081 xmlRefPtr ret;
2082 xmlRefTablePtr table;
2083 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002084
Daniel Veillard37721922001-05-04 15:21:12 +00002085 if (doc == NULL) {
2086 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002087 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002088 return(NULL);
2089 }
2090 if (value == NULL) {
2091 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002092 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002093 return(NULL);
2094 }
2095 if (attr == NULL) {
2096 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002097 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002098 return(NULL);
2099 }
Owen Taylor3473f882001-02-23 17:55:21 +00002100
Daniel Veillard37721922001-05-04 15:21:12 +00002101 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002102 * Create the Ref table if needed.
2103 */
Daniel Veillard37721922001-05-04 15:21:12 +00002104 table = (xmlRefTablePtr) doc->refs;
2105 if (table == NULL)
2106 doc->refs = table = xmlCreateRefTable();
2107 if (table == NULL) {
2108 xmlGenericError(xmlGenericErrorContext,
2109 "xmlAddRef: Table creation failed!\n");
2110 return(NULL);
2111 }
Owen Taylor3473f882001-02-23 17:55:21 +00002112
Daniel Veillard37721922001-05-04 15:21:12 +00002113 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2114 if (ret == NULL) {
2115 xmlGenericError(xmlGenericErrorContext,
2116 "xmlAddRef: out of memory\n");
2117 return(NULL);
2118 }
Owen Taylor3473f882001-02-23 17:55:21 +00002119
Daniel Veillard37721922001-05-04 15:21:12 +00002120 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002121 * fill the structure.
2122 */
Daniel Veillard37721922001-05-04 15:21:12 +00002123 ret->value = xmlStrdup(value);
2124 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002125
Daniel Veillard37721922001-05-04 15:21:12 +00002126 /* To add a reference :-
2127 * References are maintained as a list of references,
2128 * Lookup the entry, if no entry create new nodelist
2129 * Add the owning node to the NodeList
2130 * Return the ref
2131 */
Owen Taylor3473f882001-02-23 17:55:21 +00002132
Daniel Veillard37721922001-05-04 15:21:12 +00002133 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2134 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2135 xmlGenericError(xmlGenericErrorContext,
2136 "xmlAddRef: Reference list creation failed!\n");
2137 return(NULL);
2138 }
2139 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2140 xmlListDelete(ref_list);
2141 xmlGenericError(xmlGenericErrorContext,
2142 "xmlAddRef: Reference list insertion failed!\n");
2143 return(NULL);
2144 }
2145 }
2146 xmlListInsert(ref_list, ret);
2147 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002148}
2149
2150/**
2151 * xmlFreeRefTable:
2152 * @table: An ref table
2153 *
2154 * Deallocate the memory used by an Ref hash table.
2155 */
2156void
2157xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002158 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002159}
2160
2161/**
2162 * xmlIsRef:
2163 * @doc: the document
2164 * @elem: the element carrying the attribute
2165 * @attr: the attribute
2166 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002167 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002168 * then this is simple, otherwise we use an heuristic: name Ref (upper
2169 * or lowercase).
2170 *
2171 * Returns 0 or 1 depending on the lookup result
2172 */
2173int
2174xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002175 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2176 return(0);
2177 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2178 /* TODO @@@ */
2179 return(0);
2180 } else {
2181 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002182
Daniel Veillard37721922001-05-04 15:21:12 +00002183 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2184 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2185 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2186 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002187
Daniel Veillard37721922001-05-04 15:21:12 +00002188 if ((attrDecl != NULL) &&
2189 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2190 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2191 return(1);
2192 }
2193 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002194}
2195
2196/**
2197 * xmlRemoveRef
2198 * @doc: the document
2199 * @attr: the attribute
2200 *
2201 * Remove the given attribute from the Ref table maintained internally.
2202 *
2203 * Returns -1 if the lookup failed and 0 otherwise
2204 */
2205int
2206xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002207 xmlListPtr ref_list;
2208 xmlRefTablePtr table;
2209 xmlChar *ID;
2210 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002211
Daniel Veillard37721922001-05-04 15:21:12 +00002212 if (doc == NULL) return(-1);
2213 if (attr == NULL) return(-1);
2214 table = (xmlRefTablePtr) doc->refs;
2215 if (table == NULL)
2216 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002217
Daniel Veillard37721922001-05-04 15:21:12 +00002218 if (attr == NULL)
2219 return(-1);
2220 ID = xmlNodeListGetString(doc, attr->children, 1);
2221 if (ID == NULL)
2222 return(-1);
2223 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002224
Daniel Veillard37721922001-05-04 15:21:12 +00002225 if(ref_list == NULL) {
2226 xmlFree(ID);
2227 return (-1);
2228 }
2229 /* At this point, ref_list refers to a list of references which
2230 * have the same key as the supplied attr. Our list of references
2231 * is ordered by reference address and we don't have that information
2232 * here to use when removing. We'll have to walk the list and
2233 * check for a matching attribute, when we find one stop the walk
2234 * and remove the entry.
2235 * The list is ordered by reference, so that means we don't have the
2236 * key. Passing the list and the reference to the walker means we
2237 * will have enough data to be able to remove the entry.
2238 */
2239 target.l = ref_list;
2240 target.ap = attr;
2241
2242 /* Remove the supplied attr from our list */
2243 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002244
Daniel Veillard37721922001-05-04 15:21:12 +00002245 /*If the list is empty then remove the list entry in the hash */
2246 if (xmlListEmpty(ref_list))
2247 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2248 xmlFreeRefList);
2249 xmlFree(ID);
2250 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002251}
2252
2253/**
2254 * xmlGetRefs:
2255 * @doc: pointer to the document
2256 * @ID: the ID value
2257 *
2258 * Find the set of references for the supplied ID.
2259 *
2260 * Returns NULL if not found, otherwise node set for the ID.
2261 */
2262xmlListPtr
2263xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002264 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002265
Daniel Veillard37721922001-05-04 15:21:12 +00002266 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002267 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002268 return(NULL);
2269 }
Owen Taylor3473f882001-02-23 17:55:21 +00002270
Daniel Veillard37721922001-05-04 15:21:12 +00002271 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002272 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002273 return(NULL);
2274 }
Owen Taylor3473f882001-02-23 17:55:21 +00002275
Daniel Veillard37721922001-05-04 15:21:12 +00002276 table = (xmlRefTablePtr) doc->refs;
2277 if (table == NULL)
2278 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002279
Daniel Veillard37721922001-05-04 15:21:12 +00002280 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002281}
2282
2283/************************************************************************
2284 * *
2285 * Routines for validity checking *
2286 * *
2287 ************************************************************************/
2288
2289/**
2290 * xmlGetDtdElementDesc:
2291 * @dtd: a pointer to the DtD to search
2292 * @name: the element name
2293 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002294 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002295 *
2296 * returns the xmlElementPtr if found or NULL
2297 */
2298
2299xmlElementPtr
2300xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2301 xmlElementTablePtr table;
2302 xmlElementPtr cur;
2303 xmlChar *uqname = NULL, *prefix = NULL;
2304
2305 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002306 if (dtd->elements == NULL)
2307 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002308 table = (xmlElementTablePtr) dtd->elements;
2309
2310 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002311 if (uqname != NULL)
2312 name = uqname;
2313 cur = xmlHashLookup2(table, name, prefix);
2314 if (prefix != NULL) xmlFree(prefix);
2315 if (uqname != NULL) xmlFree(uqname);
2316 return(cur);
2317}
2318/**
2319 * xmlGetDtdElementDesc2:
2320 * @dtd: a pointer to the DtD to search
2321 * @name: the element name
2322 * @create: create an empty description if not found
2323 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002324 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002325 *
2326 * returns the xmlElementPtr if found or NULL
2327 */
2328
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002329static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002330xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2331 xmlElementTablePtr table;
2332 xmlElementPtr cur;
2333 xmlChar *uqname = NULL, *prefix = NULL;
2334
2335 if (dtd == NULL) return(NULL);
2336 if (dtd->elements == NULL) {
2337 if (!create)
2338 return(NULL);
2339 /*
2340 * Create the Element table if needed.
2341 */
2342 table = (xmlElementTablePtr) dtd->elements;
2343 if (table == NULL) {
2344 table = xmlCreateElementTable();
2345 dtd->elements = (void *) table;
2346 }
2347 if (table == NULL) {
2348 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002349 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002350 return(NULL);
2351 }
2352 }
2353 table = (xmlElementTablePtr) dtd->elements;
2354
2355 uqname = xmlSplitQName2(name, &prefix);
2356 if (uqname != NULL)
2357 name = uqname;
2358 cur = xmlHashLookup2(table, name, prefix);
2359 if ((cur == NULL) && (create)) {
2360 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2361 if (cur == NULL) {
2362 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002363 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002364 return(NULL);
2365 }
2366 memset(cur, 0, sizeof(xmlElement));
2367 cur->type = XML_ELEMENT_DECL;
2368
2369 /*
2370 * fill the structure.
2371 */
2372 cur->name = xmlStrdup(name);
2373 cur->prefix = xmlStrdup(prefix);
2374 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2375
2376 xmlHashAddEntry2(table, name, prefix, cur);
2377 }
2378 if (prefix != NULL) xmlFree(prefix);
2379 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002380 return(cur);
2381}
2382
2383/**
2384 * xmlGetDtdQElementDesc:
2385 * @dtd: a pointer to the DtD to search
2386 * @name: the element name
2387 * @prefix: the element namespace prefix
2388 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002389 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002390 *
2391 * returns the xmlElementPtr if found or NULL
2392 */
2393
Daniel Veillard48da9102001-08-07 01:10:10 +00002394xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002395xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2396 const xmlChar *prefix) {
2397 xmlElementTablePtr table;
2398
2399 if (dtd == NULL) return(NULL);
2400 if (dtd->elements == NULL) return(NULL);
2401 table = (xmlElementTablePtr) dtd->elements;
2402
2403 return(xmlHashLookup2(table, name, prefix));
2404}
2405
2406/**
2407 * xmlGetDtdAttrDesc:
2408 * @dtd: a pointer to the DtD to search
2409 * @elem: the element name
2410 * @name: the attribute name
2411 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002412 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002413 * this element.
2414 *
2415 * returns the xmlAttributePtr if found or NULL
2416 */
2417
2418xmlAttributePtr
2419xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2420 xmlAttributeTablePtr table;
2421 xmlAttributePtr cur;
2422 xmlChar *uqname = NULL, *prefix = NULL;
2423
2424 if (dtd == NULL) return(NULL);
2425 if (dtd->attributes == NULL) return(NULL);
2426
2427 table = (xmlAttributeTablePtr) dtd->attributes;
2428 if (table == NULL)
2429 return(NULL);
2430
2431 uqname = xmlSplitQName2(name, &prefix);
2432
2433 if (uqname != NULL) {
2434 cur = xmlHashLookup3(table, uqname, prefix, elem);
2435 if (prefix != NULL) xmlFree(prefix);
2436 if (uqname != NULL) xmlFree(uqname);
2437 } else
2438 cur = xmlHashLookup3(table, name, NULL, elem);
2439 return(cur);
2440}
2441
2442/**
2443 * xmlGetDtdQAttrDesc:
2444 * @dtd: a pointer to the DtD to search
2445 * @elem: the element name
2446 * @name: the attribute name
2447 * @prefix: the attribute namespace prefix
2448 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002449 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002450 * this element.
2451 *
2452 * returns the xmlAttributePtr if found or NULL
2453 */
2454
Daniel Veillard48da9102001-08-07 01:10:10 +00002455xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002456xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2457 const xmlChar *prefix) {
2458 xmlAttributeTablePtr table;
2459
2460 if (dtd == NULL) return(NULL);
2461 if (dtd->attributes == NULL) return(NULL);
2462 table = (xmlAttributeTablePtr) dtd->attributes;
2463
2464 return(xmlHashLookup3(table, name, prefix, elem));
2465}
2466
2467/**
2468 * xmlGetDtdNotationDesc:
2469 * @dtd: a pointer to the DtD to search
2470 * @name: the notation name
2471 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002472 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002473 *
2474 * returns the xmlNotationPtr if found or NULL
2475 */
2476
2477xmlNotationPtr
2478xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2479 xmlNotationTablePtr table;
2480
2481 if (dtd == NULL) return(NULL);
2482 if (dtd->notations == NULL) return(NULL);
2483 table = (xmlNotationTablePtr) dtd->notations;
2484
2485 return(xmlHashLookup(table, name));
2486}
2487
2488/**
2489 * xmlValidateNotationUse:
2490 * @ctxt: the validation context
2491 * @doc: the document
2492 * @notationName: the notation name to check
2493 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002494 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002495 * - [ VC: Notation Declared ]
2496 *
2497 * returns 1 if valid or 0 otherwise
2498 */
2499
2500int
2501xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2502 const xmlChar *notationName) {
2503 xmlNotationPtr notaDecl;
2504 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2505
2506 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2507 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2508 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2509
2510 if (notaDecl == NULL) {
2511 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2512 notationName);
2513 return(0);
2514 }
2515 return(1);
2516}
2517
2518/**
2519 * xmlIsMixedElement
2520 * @doc: the document
2521 * @name: the element name
2522 *
2523 * Search in the DtDs whether an element accept Mixed content (or ANY)
2524 * basically if it is supposed to accept text childs
2525 *
2526 * returns 0 if no, 1 if yes, and -1 if no element description is available
2527 */
2528
2529int
2530xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2531 xmlElementPtr elemDecl;
2532
2533 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2534
2535 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2536 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2537 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2538 if (elemDecl == NULL) return(-1);
2539 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002540 case XML_ELEMENT_TYPE_UNDEFINED:
2541 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002542 case XML_ELEMENT_TYPE_ELEMENT:
2543 return(0);
2544 case XML_ELEMENT_TYPE_EMPTY:
2545 /*
2546 * return 1 for EMPTY since we want VC error to pop up
2547 * on <empty> </empty> for example
2548 */
2549 case XML_ELEMENT_TYPE_ANY:
2550 case XML_ELEMENT_TYPE_MIXED:
2551 return(1);
2552 }
2553 return(1);
2554}
2555
2556/**
2557 * xmlValidateNameValue:
2558 * @value: an Name value
2559 *
2560 * Validate that the given value match Name production
2561 *
2562 * returns 1 if valid or 0 otherwise
2563 */
2564
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002565static int
Owen Taylor3473f882001-02-23 17:55:21 +00002566xmlValidateNameValue(const xmlChar *value) {
2567 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002568 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002569
2570 if (value == NULL) return(0);
2571 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002572 val = xmlStringCurrentChar(NULL, cur, &len);
2573 cur += len;
2574 if (!IS_LETTER(val) && (val != '_') &&
2575 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002576 return(0);
2577 }
2578
Daniel Veillardd8224e02002-01-13 15:43:22 +00002579 val = xmlStringCurrentChar(NULL, cur, &len);
2580 cur += len;
2581 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2582 (val == '.') || (val == '-') ||
2583 (val == '_') || (val == ':') ||
2584 (IS_COMBINING(val)) ||
2585 (IS_EXTENDER(val))) {
2586 val = xmlStringCurrentChar(NULL, cur, &len);
2587 cur += len;
2588 }
Owen Taylor3473f882001-02-23 17:55:21 +00002589
Daniel Veillardd8224e02002-01-13 15:43:22 +00002590 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002591
2592 return(1);
2593}
2594
2595/**
2596 * xmlValidateNamesValue:
2597 * @value: an Names value
2598 *
2599 * Validate that the given value match Names production
2600 *
2601 * returns 1 if valid or 0 otherwise
2602 */
2603
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002604static int
Owen Taylor3473f882001-02-23 17:55:21 +00002605xmlValidateNamesValue(const xmlChar *value) {
2606 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002607 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002608
2609 if (value == NULL) return(0);
2610 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002611 val = xmlStringCurrentChar(NULL, cur, &len);
2612 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002613
Daniel Veillardd8224e02002-01-13 15:43:22 +00002614 if (!IS_LETTER(val) && (val != '_') &&
2615 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002616 return(0);
2617 }
2618
Daniel Veillardd8224e02002-01-13 15:43:22 +00002619 val = xmlStringCurrentChar(NULL, cur, &len);
2620 cur += len;
2621 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2622 (val == '.') || (val == '-') ||
2623 (val == '_') || (val == ':') ||
2624 (IS_COMBINING(val)) ||
2625 (IS_EXTENDER(val))) {
2626 val = xmlStringCurrentChar(NULL, cur, &len);
2627 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002628 }
2629
Daniel Veillardd8224e02002-01-13 15:43:22 +00002630 while (IS_BLANK(val)) {
2631 while (IS_BLANK(val)) {
2632 val = xmlStringCurrentChar(NULL, cur, &len);
2633 cur += len;
2634 }
2635
2636 if (!IS_LETTER(val) && (val != '_') &&
2637 (val != ':')) {
2638 return(0);
2639 }
2640 val = xmlStringCurrentChar(NULL, cur, &len);
2641 cur += len;
2642
2643 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2644 (val == '.') || (val == '-') ||
2645 (val == '_') || (val == ':') ||
2646 (IS_COMBINING(val)) ||
2647 (IS_EXTENDER(val))) {
2648 val = xmlStringCurrentChar(NULL, cur, &len);
2649 cur += len;
2650 }
2651 }
2652
2653 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002654
2655 return(1);
2656}
2657
2658/**
2659 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002660 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002661 *
2662 * Validate that the given value match Nmtoken production
2663 *
2664 * [ VC: Name Token ]
2665 *
2666 * returns 1 if valid or 0 otherwise
2667 */
2668
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002669static int
Owen Taylor3473f882001-02-23 17:55:21 +00002670xmlValidateNmtokenValue(const xmlChar *value) {
2671 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002672 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002673
2674 if (value == NULL) return(0);
2675 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002676 val = xmlStringCurrentChar(NULL, cur, &len);
2677 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002678
Daniel Veillardd8224e02002-01-13 15:43:22 +00002679 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2680 (val != '.') && (val != '-') &&
2681 (val != '_') && (val != ':') &&
2682 (!IS_COMBINING(val)) &&
2683 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002684 return(0);
2685
Daniel Veillardd8224e02002-01-13 15:43:22 +00002686 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2687 (val == '.') || (val == '-') ||
2688 (val == '_') || (val == ':') ||
2689 (IS_COMBINING(val)) ||
2690 (IS_EXTENDER(val))) {
2691 val = xmlStringCurrentChar(NULL, cur, &len);
2692 cur += len;
2693 }
Owen Taylor3473f882001-02-23 17:55:21 +00002694
Daniel Veillardd8224e02002-01-13 15:43:22 +00002695 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002696
2697 return(1);
2698}
2699
2700/**
2701 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002702 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002703 *
2704 * Validate that the given value match Nmtokens production
2705 *
2706 * [ VC: Name Token ]
2707 *
2708 * returns 1 if valid or 0 otherwise
2709 */
2710
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002711static int
Owen Taylor3473f882001-02-23 17:55:21 +00002712xmlValidateNmtokensValue(const xmlChar *value) {
2713 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002714 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002715
2716 if (value == NULL) return(0);
2717 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002718 val = xmlStringCurrentChar(NULL, cur, &len);
2719 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002720
Daniel Veillardd8224e02002-01-13 15:43:22 +00002721 while (IS_BLANK(val)) {
2722 val = xmlStringCurrentChar(NULL, cur, &len);
2723 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002724 }
2725
Daniel Veillardd8224e02002-01-13 15:43:22 +00002726 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2727 (val != '.') && (val != '-') &&
2728 (val != '_') && (val != ':') &&
2729 (!IS_COMBINING(val)) &&
2730 (!IS_EXTENDER(val)))
2731 return(0);
2732
2733 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2734 (val == '.') || (val == '-') ||
2735 (val == '_') || (val == ':') ||
2736 (IS_COMBINING(val)) ||
2737 (IS_EXTENDER(val))) {
2738 val = xmlStringCurrentChar(NULL, cur, &len);
2739 cur += len;
2740 }
2741
2742 while (IS_BLANK(val)) {
2743 while (IS_BLANK(val)) {
2744 val = xmlStringCurrentChar(NULL, cur, &len);
2745 cur += len;
2746 }
2747 if (val == 0) return(1);
2748
2749 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2750 (val != '.') && (val != '-') &&
2751 (val != '_') && (val != ':') &&
2752 (!IS_COMBINING(val)) &&
2753 (!IS_EXTENDER(val)))
2754 return(0);
2755
2756 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2757 (val == '.') || (val == '-') ||
2758 (val == '_') || (val == ':') ||
2759 (IS_COMBINING(val)) ||
2760 (IS_EXTENDER(val))) {
2761 val = xmlStringCurrentChar(NULL, cur, &len);
2762 cur += len;
2763 }
2764 }
2765
2766 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002767
2768 return(1);
2769}
2770
2771/**
2772 * xmlValidateNotationDecl:
2773 * @ctxt: the validation context
2774 * @doc: a document instance
2775 * @nota: a notation definition
2776 *
2777 * Try to validate a single notation definition
2778 * basically it does the following checks as described by the
2779 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002780 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002781 * But this function get called anyway ...
2782 *
2783 * returns 1 if valid or 0 otherwise
2784 */
2785
2786int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002787xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2788 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002789 int ret = 1;
2790
2791 return(ret);
2792}
2793
2794/**
2795 * xmlValidateAttributeValue:
2796 * @type: an attribute type
2797 * @value: an attribute value
2798 *
2799 * Validate that the given attribute value match the proper production
2800 *
2801 * [ VC: ID ]
2802 * Values of type ID must match the Name production....
2803 *
2804 * [ VC: IDREF ]
2805 * Values of type IDREF must match the Name production, and values
2806 * of type IDREFS must match Names ...
2807 *
2808 * [ VC: Entity Name ]
2809 * Values of type ENTITY must match the Name production, values
2810 * of type ENTITIES must match Names ...
2811 *
2812 * [ VC: Name Token ]
2813 * Values of type NMTOKEN must match the Nmtoken production; values
2814 * of type NMTOKENS must match Nmtokens.
2815 *
2816 * returns 1 if valid or 0 otherwise
2817 */
2818
2819int
2820xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2821 switch (type) {
2822 case XML_ATTRIBUTE_ENTITIES:
2823 case XML_ATTRIBUTE_IDREFS:
2824 return(xmlValidateNamesValue(value));
2825 case XML_ATTRIBUTE_ENTITY:
2826 case XML_ATTRIBUTE_IDREF:
2827 case XML_ATTRIBUTE_ID:
2828 case XML_ATTRIBUTE_NOTATION:
2829 return(xmlValidateNameValue(value));
2830 case XML_ATTRIBUTE_NMTOKENS:
2831 case XML_ATTRIBUTE_ENUMERATION:
2832 return(xmlValidateNmtokensValue(value));
2833 case XML_ATTRIBUTE_NMTOKEN:
2834 return(xmlValidateNmtokenValue(value));
2835 case XML_ATTRIBUTE_CDATA:
2836 break;
2837 }
2838 return(1);
2839}
2840
2841/**
2842 * xmlValidateAttributeValue2:
2843 * @ctxt: the validation context
2844 * @doc: the document
2845 * @name: the attribute name (used for error reporting only)
2846 * @type: the attribute type
2847 * @value: the attribute value
2848 *
2849 * Validate that the given attribute value match a given type.
2850 * This typically cannot be done before having finished parsing
2851 * the subsets.
2852 *
2853 * [ VC: IDREF ]
2854 * Values of type IDREF must match one of the declared IDs
2855 * Values of type IDREFS must match a sequence of the declared IDs
2856 * each Name must match the value of an ID attribute on some element
2857 * in the XML document; i.e. IDREF values must match the value of
2858 * some ID attribute
2859 *
2860 * [ VC: Entity Name ]
2861 * Values of type ENTITY must match one declared entity
2862 * Values of type ENTITIES must match a sequence of declared entities
2863 *
2864 * [ VC: Notation Attributes ]
2865 * all notation names in the declaration must be declared.
2866 *
2867 * returns 1 if valid or 0 otherwise
2868 */
2869
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002870static int
Owen Taylor3473f882001-02-23 17:55:21 +00002871xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2872 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2873 int ret = 1;
2874 switch (type) {
2875 case XML_ATTRIBUTE_IDREFS:
2876 case XML_ATTRIBUTE_IDREF:
2877 case XML_ATTRIBUTE_ID:
2878 case XML_ATTRIBUTE_NMTOKENS:
2879 case XML_ATTRIBUTE_ENUMERATION:
2880 case XML_ATTRIBUTE_NMTOKEN:
2881 case XML_ATTRIBUTE_CDATA:
2882 break;
2883 case XML_ATTRIBUTE_ENTITY: {
2884 xmlEntityPtr ent;
2885
2886 ent = xmlGetDocEntity(doc, value);
2887 if (ent == NULL) {
2888 VERROR(ctxt->userData,
2889 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2890 name, value);
2891 ret = 0;
2892 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2893 VERROR(ctxt->userData,
2894 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2895 name, value);
2896 ret = 0;
2897 }
2898 break;
2899 }
2900 case XML_ATTRIBUTE_ENTITIES: {
2901 xmlChar *dup, *nam = NULL, *cur, save;
2902 xmlEntityPtr ent;
2903
2904 dup = xmlStrdup(value);
2905 if (dup == NULL)
2906 return(0);
2907 cur = dup;
2908 while (*cur != 0) {
2909 nam = cur;
2910 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2911 save = *cur;
2912 *cur = 0;
2913 ent = xmlGetDocEntity(doc, nam);
2914 if (ent == NULL) {
2915 VERROR(ctxt->userData,
2916 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2917 name, nam);
2918 ret = 0;
2919 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2920 VERROR(ctxt->userData,
2921 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2922 name, nam);
2923 ret = 0;
2924 }
2925 if (save == 0)
2926 break;
2927 *cur = save;
2928 while (IS_BLANK(*cur)) cur++;
2929 }
2930 xmlFree(dup);
2931 break;
2932 }
2933 case XML_ATTRIBUTE_NOTATION: {
2934 xmlNotationPtr nota;
2935
2936 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2937 if ((nota == NULL) && (doc->extSubset != NULL))
2938 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2939
2940 if (nota == NULL) {
2941 VERROR(ctxt->userData,
2942 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2943 name, value);
2944 ret = 0;
2945 }
2946 break;
2947 }
2948 }
2949 return(ret);
2950}
2951
2952/**
2953 * xmlValidNormalizeAttributeValue:
2954 * @doc: the document
2955 * @elem: the parent
2956 * @name: the attribute name
2957 * @value: the attribute value
2958 *
2959 * Does the validation related extra step of the normalization of attribute
2960 * values:
2961 *
2962 * If the declared value is not CDATA, then the XML processor must further
2963 * process the normalized attribute value by discarding any leading and
2964 * trailing space (#x20) characters, and by replacing sequences of space
2965 * (#x20) characters by single space (#x20) character.
2966 *
2967 * returns a new normalized string if normalization is needed, NULL otherwise
2968 * the caller must free the returned value.
2969 */
2970
2971xmlChar *
2972xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2973 const xmlChar *name, const xmlChar *value) {
2974 xmlChar *ret, *dst;
2975 const xmlChar *src;
2976 xmlAttributePtr attrDecl = NULL;
2977
2978 if (doc == NULL) return(NULL);
2979 if (elem == NULL) return(NULL);
2980 if (name == NULL) return(NULL);
2981 if (value == NULL) return(NULL);
2982
2983 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2984 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002985 snprintf((char *) qname, sizeof(qname), "%s:%s",
2986 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002987 qname[sizeof(qname) - 1] = 0;
2988 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2989 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2990 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2991 }
2992 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2993 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2994 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2995
2996 if (attrDecl == NULL)
2997 return(NULL);
2998 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2999 return(NULL);
3000
3001 ret = xmlStrdup(value);
3002 if (ret == NULL)
3003 return(NULL);
3004 src = value;
3005 dst = ret;
3006 while (*src == 0x20) src++;
3007 while (*src != 0) {
3008 if (*src == 0x20) {
3009 while (*src == 0x20) src++;
3010 if (*src != 0)
3011 *dst++ = 0x20;
3012 } else {
3013 *dst++ = *src++;
3014 }
3015 }
3016 *dst = 0;
3017 return(ret);
3018}
3019
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003020static void
Owen Taylor3473f882001-02-23 17:55:21 +00003021xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003022 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003023 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3024}
3025
3026/**
3027 * xmlValidateAttributeDecl:
3028 * @ctxt: the validation context
3029 * @doc: a document instance
3030 * @attr: an attribute definition
3031 *
3032 * Try to validate a single attribute definition
3033 * basically it does the following checks as described by the
3034 * XML-1.0 recommendation:
3035 * - [ VC: Attribute Default Legal ]
3036 * - [ VC: Enumeration ]
3037 * - [ VC: ID Attribute Default ]
3038 *
3039 * The ID/IDREF uniqueness and matching are done separately
3040 *
3041 * returns 1 if valid or 0 otherwise
3042 */
3043
3044int
3045xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3046 xmlAttributePtr attr) {
3047 int ret = 1;
3048 int val;
3049 CHECK_DTD;
3050 if(attr == NULL) return(1);
3051
3052 /* Attribute Default Legal */
3053 /* Enumeration */
3054 if (attr->defaultValue != NULL) {
3055 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3056 if (val == 0) {
3057 VERROR(ctxt->userData,
3058 "Syntax of default value for attribute %s on %s is not valid\n",
3059 attr->name, attr->elem);
3060 }
3061 ret &= val;
3062 }
3063
3064 /* ID Attribute Default */
3065 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3066 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3067 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3068 VERROR(ctxt->userData,
3069 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
3070 attr->name, attr->elem);
3071 ret = 0;
3072 }
3073
3074 /* One ID per Element Type */
3075 if (attr->atype == XML_ATTRIBUTE_ID) {
3076 int nbId;
3077
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003078 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003079 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3080 attr->elem);
3081 if (elem != NULL) {
3082 nbId = xmlScanIDAttributeDecl(NULL, elem);
3083 } else {
3084 xmlAttributeTablePtr table;
3085
3086 /*
3087 * The attribute may be declared in the internal subset and the
3088 * element in the external subset.
3089 */
3090 nbId = 0;
3091 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3092 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3093 xmlValidateAttributeIdCallback, &nbId);
3094 }
3095 if (nbId > 1) {
3096 VERROR(ctxt->userData,
3097 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3098 attr->elem, nbId, attr->name);
3099 } else if (doc->extSubset != NULL) {
3100 int extId = 0;
3101 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3102 if (elem != NULL) {
3103 extId = xmlScanIDAttributeDecl(NULL, elem);
3104 }
3105 if (extId > 1) {
3106 VERROR(ctxt->userData,
3107 "Element %s has %d ID attribute defined in the external subset : %s\n",
3108 attr->elem, extId, attr->name);
3109 } else if (extId + nbId > 1) {
3110 VERROR(ctxt->userData,
3111"Element %s has ID attributes defined in the internal and external subset : %s\n",
3112 attr->elem, attr->name);
3113 }
3114 }
3115 }
3116
3117 /* Validity Constraint: Enumeration */
3118 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3119 xmlEnumerationPtr tree = attr->tree;
3120 while (tree != NULL) {
3121 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3122 tree = tree->next;
3123 }
3124 if (tree == NULL) {
3125 VERROR(ctxt->userData,
3126"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3127 attr->defaultValue, attr->name, attr->elem);
3128 ret = 0;
3129 }
3130 }
3131
3132 return(ret);
3133}
3134
3135/**
3136 * xmlValidateElementDecl:
3137 * @ctxt: the validation context
3138 * @doc: a document instance
3139 * @elem: an element definition
3140 *
3141 * Try to validate a single element definition
3142 * basically it does the following checks as described by the
3143 * XML-1.0 recommendation:
3144 * - [ VC: One ID per Element Type ]
3145 * - [ VC: No Duplicate Types ]
3146 * - [ VC: Unique Element Type Declaration ]
3147 *
3148 * returns 1 if valid or 0 otherwise
3149 */
3150
3151int
3152xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3153 xmlElementPtr elem) {
3154 int ret = 1;
3155 xmlElementPtr tst;
3156
3157 CHECK_DTD;
3158
3159 if (elem == NULL) return(1);
3160
3161 /* No Duplicate Types */
3162 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3163 xmlElementContentPtr cur, next;
3164 const xmlChar *name;
3165
3166 cur = elem->content;
3167 while (cur != NULL) {
3168 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3169 if (cur->c1 == NULL) break;
3170 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3171 name = cur->c1->name;
3172 next = cur->c2;
3173 while (next != NULL) {
3174 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3175 if (xmlStrEqual(next->name, name)) {
3176 VERROR(ctxt->userData,
3177 "Definition of %s has duplicate references of %s\n",
3178 elem->name, name);
3179 ret = 0;
3180 }
3181 break;
3182 }
3183 if (next->c1 == NULL) break;
3184 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3185 if (xmlStrEqual(next->c1->name, name)) {
3186 VERROR(ctxt->userData,
3187 "Definition of %s has duplicate references of %s\n",
3188 elem->name, name);
3189 ret = 0;
3190 }
3191 next = next->c2;
3192 }
3193 }
3194 cur = cur->c2;
3195 }
3196 }
3197
3198 /* VC: Unique Element Type Declaration */
3199 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003200 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003201 ((tst->prefix == elem->prefix) ||
3202 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003203 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003204 VERROR(ctxt->userData, "Redefinition of element %s\n",
3205 elem->name);
3206 ret = 0;
3207 }
3208 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003209 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003210 ((tst->prefix == elem->prefix) ||
3211 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003212 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003213 VERROR(ctxt->userData, "Redefinition of element %s\n",
3214 elem->name);
3215 ret = 0;
3216 }
3217
Daniel Veillarda10efa82001-04-18 13:09:01 +00003218 /* One ID per Element Type
3219 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003220 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3221 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003222 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003223 return(ret);
3224}
3225
3226/**
3227 * xmlValidateOneAttribute:
3228 * @ctxt: the validation context
3229 * @doc: a document instance
3230 * @elem: an element instance
3231 * @attr: an attribute instance
3232 * @value: the attribute value (without entities processing)
3233 *
3234 * Try to validate a single attribute for an element
3235 * basically it does the following checks as described by the
3236 * XML-1.0 recommendation:
3237 * - [ VC: Attribute Value Type ]
3238 * - [ VC: Fixed Attribute Default ]
3239 * - [ VC: Entity Name ]
3240 * - [ VC: Name Token ]
3241 * - [ VC: ID ]
3242 * - [ VC: IDREF ]
3243 * - [ VC: Entity Name ]
3244 * - [ VC: Notation Attributes ]
3245 *
3246 * The ID/IDREF uniqueness and matching are done separately
3247 *
3248 * returns 1 if valid or 0 otherwise
3249 */
3250
3251int
3252xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3253 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3254 /* xmlElementPtr elemDecl; */
3255 xmlAttributePtr attrDecl = NULL;
3256 int val;
3257 int ret = 1;
3258
3259 CHECK_DTD;
3260 if ((elem == NULL) || (elem->name == NULL)) return(0);
3261 if ((attr == NULL) || (attr->name == NULL)) return(0);
3262
3263 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3264 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003265 snprintf((char *) qname, sizeof(qname), "%s:%s",
3266 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003267 qname[sizeof(qname) - 1] = 0;
3268 if (attr->ns != NULL) {
3269 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3270 attr->name, attr->ns->prefix);
3271 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3272 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3273 attr->name, attr->ns->prefix);
3274 } else {
3275 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3276 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3277 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3278 qname, attr->name);
3279 }
3280 }
3281 if (attrDecl == NULL) {
3282 if (attr->ns != NULL) {
3283 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3284 attr->name, attr->ns->prefix);
3285 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3286 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3287 attr->name, attr->ns->prefix);
3288 } else {
3289 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3290 elem->name, attr->name);
3291 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3292 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3293 elem->name, attr->name);
3294 }
3295 }
3296
3297
3298 /* Validity Constraint: Attribute Value Type */
3299 if (attrDecl == NULL) {
3300 VERROR(ctxt->userData,
3301 "No declaration for attribute %s on element %s\n",
3302 attr->name, elem->name);
3303 return(0);
3304 }
3305 attr->atype = attrDecl->atype;
3306
3307 val = xmlValidateAttributeValue(attrDecl->atype, value);
3308 if (val == 0) {
3309 VERROR(ctxt->userData,
3310 "Syntax of value for attribute %s on %s is not valid\n",
3311 attr->name, elem->name);
3312 ret = 0;
3313 }
3314
3315 /* Validity constraint: Fixed Attribute Default */
3316 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3317 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3318 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003319 "Value for attribute %s on %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003320 attr->name, elem->name, attrDecl->defaultValue);
3321 ret = 0;
3322 }
3323 }
3324
3325 /* Validity Constraint: ID uniqueness */
3326 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3327 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3328 ret = 0;
3329 }
3330
3331 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3332 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3333 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3334 ret = 0;
3335 }
3336
3337 /* Validity Constraint: Notation Attributes */
3338 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3339 xmlEnumerationPtr tree = attrDecl->tree;
3340 xmlNotationPtr nota;
3341
3342 /* First check that the given NOTATION was declared */
3343 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3344 if (nota == NULL)
3345 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3346
3347 if (nota == NULL) {
3348 VERROR(ctxt->userData,
3349 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3350 value, attr->name, elem->name);
3351 ret = 0;
3352 }
3353
3354 /* Second, verify that it's among the list */
3355 while (tree != NULL) {
3356 if (xmlStrEqual(tree->name, value)) break;
3357 tree = tree->next;
3358 }
3359 if (tree == NULL) {
3360 VERROR(ctxt->userData,
3361"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3362 value, attr->name, elem->name);
3363 ret = 0;
3364 }
3365 }
3366
3367 /* Validity Constraint: Enumeration */
3368 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3369 xmlEnumerationPtr tree = attrDecl->tree;
3370 while (tree != NULL) {
3371 if (xmlStrEqual(tree->name, value)) break;
3372 tree = tree->next;
3373 }
3374 if (tree == NULL) {
3375 VERROR(ctxt->userData,
3376 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3377 value, attr->name, elem->name);
3378 ret = 0;
3379 }
3380 }
3381
3382 /* Fixed Attribute Default */
3383 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3384 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3385 VERROR(ctxt->userData,
3386 "Value for attribute %s on %s must be \"%s\"\n",
3387 attr->name, elem->name, attrDecl->defaultValue);
3388 ret = 0;
3389 }
3390
3391 /* Extra check for the attribute value */
3392 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3393 attrDecl->atype, value);
3394
3395 return(ret);
3396}
3397
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003398/**
3399 * xmlValidateSkipIgnorable:
3400 * @ctxt: the validation context
3401 * @child: the child list
3402 *
3403 * Skip ignorable elements w.r.t. the validation process
3404 *
3405 * returns the first element to consider for validation of the content model
3406 */
3407
3408static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003409xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003410 while (child != NULL) {
3411 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003412 /* These things are ignored (skipped) during validation. */
3413 case XML_PI_NODE:
3414 case XML_COMMENT_NODE:
3415 case XML_XINCLUDE_START:
3416 case XML_XINCLUDE_END:
3417 child = child->next;
3418 break;
3419 case XML_TEXT_NODE:
3420 if (xmlIsBlankNode(child))
3421 child = child->next;
3422 else
3423 return(child);
3424 break;
3425 /* keep current node */
3426 default:
3427 return(child);
3428 }
3429 }
3430 return(child);
3431}
3432
3433/**
3434 * xmlValidateElementType:
3435 * @ctxt: the validation context
3436 *
3437 * Try to validate the content model of an element internal function
3438 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003439 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3440 * reference is found and -3 if the validation succeeded but
3441 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003442 */
3443
3444static int
3445xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003446 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003447 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003448
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003449 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003450 if ((NODE == NULL) && (CONT == NULL))
3451 return(1);
3452 if ((NODE == NULL) &&
3453 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3454 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3455 return(1);
3456 }
3457 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003458 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003459 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003460
3461 /*
3462 * We arrive here when more states need to be examined
3463 */
3464cont:
3465
3466 /*
3467 * We just recovered from a rollback generated by a possible
3468 * epsilon transition, go directly to the analysis phase
3469 */
3470 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003471 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003472 DEBUG_VALID_STATE(NODE, CONT)
3473 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003474 goto analyze;
3475 }
3476
3477 DEBUG_VALID_STATE(NODE, CONT)
3478 /*
3479 * we may have to save a backup state here. This is the equivalent
3480 * of handling epsilon transition in NFAs.
3481 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003482 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003483 ((CONT->parent == NULL) ||
3484 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003485 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003486 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003487 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003488 DEBUG_VALID_MSG("saving parent branch");
3489 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3490 }
3491
3492
3493 /*
3494 * Check first if the content matches
3495 */
3496 switch (CONT->type) {
3497 case XML_ELEMENT_CONTENT_PCDATA:
3498 if (NODE == NULL) {
3499 DEBUG_VALID_MSG("pcdata failed no node");
3500 ret = 0;
3501 break;
3502 }
3503 if (NODE->type == XML_TEXT_NODE) {
3504 DEBUG_VALID_MSG("pcdata found, skip to next");
3505 /*
3506 * go to next element in the content model
3507 * skipping ignorable elems
3508 */
3509 do {
3510 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003511 NODE = xmlValidateSkipIgnorable(NODE);
3512 if ((NODE != NULL) &&
3513 (NODE->type == XML_ENTITY_REF_NODE))
3514 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003515 } while ((NODE != NULL) &&
3516 ((NODE->type != XML_ELEMENT_NODE) &&
3517 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003518 ret = 1;
3519 break;
3520 } else {
3521 DEBUG_VALID_MSG("pcdata failed");
3522 ret = 0;
3523 break;
3524 }
3525 break;
3526 case XML_ELEMENT_CONTENT_ELEMENT:
3527 if (NODE == NULL) {
3528 DEBUG_VALID_MSG("element failed no node");
3529 ret = 0;
3530 break;
3531 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003532 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3533 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003534 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003535 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3536 ret = (CONT->prefix == NULL);
3537 } else if (CONT->prefix == NULL) {
3538 ret = 0;
3539 } else {
3540 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3541 }
3542 }
3543 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003544 DEBUG_VALID_MSG("element found, skip to next");
3545 /*
3546 * go to next element in the content model
3547 * skipping ignorable elems
3548 */
3549 do {
3550 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003551 NODE = xmlValidateSkipIgnorable(NODE);
3552 if ((NODE != NULL) &&
3553 (NODE->type == XML_ENTITY_REF_NODE))
3554 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003555 } while ((NODE != NULL) &&
3556 ((NODE->type != XML_ELEMENT_NODE) &&
3557 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003558 } else {
3559 DEBUG_VALID_MSG("element failed");
3560 ret = 0;
3561 break;
3562 }
3563 break;
3564 case XML_ELEMENT_CONTENT_OR:
3565 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003566 * Small optimization.
3567 */
3568 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3569 if ((NODE == NULL) ||
3570 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3571 DEPTH++;
3572 CONT = CONT->c2;
3573 goto cont;
3574 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003575 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3576 ret = (CONT->c1->prefix == NULL);
3577 } else if (CONT->c1->prefix == NULL) {
3578 ret = 0;
3579 } else {
3580 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3581 }
3582 if (ret == 0) {
3583 DEPTH++;
3584 CONT = CONT->c2;
3585 goto cont;
3586 }
Daniel Veillard85349052001-04-20 13:48:21 +00003587 }
3588
3589 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003590 * save the second branch 'or' branch
3591 */
3592 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard268fd1b2001-08-26 18:46:36 +00003593 vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3594 OCCURS, ROLLBACK_OR);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003595
3596 DEPTH++;
3597 CONT = CONT->c1;
3598 goto cont;
3599 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003600 /*
3601 * Small optimization.
3602 */
3603 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3604 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3605 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3606 if ((NODE == NULL) ||
3607 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3608 DEPTH++;
3609 CONT = CONT->c2;
3610 goto cont;
3611 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003612 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3613 ret = (CONT->c1->prefix == NULL);
3614 } else if (CONT->c1->prefix == NULL) {
3615 ret = 0;
3616 } else {
3617 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3618 }
3619 if (ret == 0) {
3620 DEPTH++;
3621 CONT = CONT->c2;
3622 goto cont;
3623 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003624 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003625 DEPTH++;
3626 CONT = CONT->c1;
3627 goto cont;
3628 }
3629
3630 /*
3631 * At this point handle going up in the tree
3632 */
3633 if (ret == -1) {
3634 DEBUG_VALID_MSG("error found returning");
3635 return(ret);
3636 }
3637analyze:
3638 while (CONT != NULL) {
3639 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003640 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003641 * this level.
3642 */
3643 if (ret == 0) {
3644 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003645 xmlNodePtr cur;
3646
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003647 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003648 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003649 DEBUG_VALID_MSG("Once branch failed, rollback");
3650 if (vstateVPop(ctxt) < 0 ) {
3651 DEBUG_VALID_MSG("exhaustion, failed");
3652 return(0);
3653 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003654 if (cur != ctxt->vstate->node)
3655 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003656 goto cont;
3657 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003658 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003659 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003660 DEBUG_VALID_MSG("Plus branch failed, rollback");
3661 if (vstateVPop(ctxt) < 0 ) {
3662 DEBUG_VALID_MSG("exhaustion, failed");
3663 return(0);
3664 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003665 if (cur != ctxt->vstate->node)
3666 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003667 goto cont;
3668 }
3669 DEBUG_VALID_MSG("Plus branch found");
3670 ret = 1;
3671 break;
3672 case XML_ELEMENT_CONTENT_MULT:
3673#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003674 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003675 DEBUG_VALID_MSG("Mult branch failed");
3676 } else {
3677 DEBUG_VALID_MSG("Mult branch found");
3678 }
3679#endif
3680 ret = 1;
3681 break;
3682 case XML_ELEMENT_CONTENT_OPT:
3683 DEBUG_VALID_MSG("Option branch failed");
3684 ret = 1;
3685 break;
3686 }
3687 } else {
3688 switch (CONT->ocur) {
3689 case XML_ELEMENT_CONTENT_OPT:
3690 DEBUG_VALID_MSG("Option branch succeeded");
3691 ret = 1;
3692 break;
3693 case XML_ELEMENT_CONTENT_ONCE:
3694 DEBUG_VALID_MSG("Once branch succeeded");
3695 ret = 1;
3696 break;
3697 case XML_ELEMENT_CONTENT_PLUS:
3698 if (STATE == ROLLBACK_PARENT) {
3699 DEBUG_VALID_MSG("Plus branch rollback");
3700 ret = 1;
3701 break;
3702 }
3703 if (NODE == NULL) {
3704 DEBUG_VALID_MSG("Plus branch exhausted");
3705 ret = 1;
3706 break;
3707 }
3708 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003709 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003710 goto cont;
3711 case XML_ELEMENT_CONTENT_MULT:
3712 if (STATE == ROLLBACK_PARENT) {
3713 DEBUG_VALID_MSG("Mult branch rollback");
3714 ret = 1;
3715 break;
3716 }
3717 if (NODE == NULL) {
3718 DEBUG_VALID_MSG("Mult branch exhausted");
3719 ret = 1;
3720 break;
3721 }
3722 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003723 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003724 goto cont;
3725 }
3726 }
3727 STATE = 0;
3728
3729 /*
3730 * Then act accordingly at the parent level
3731 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003732 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003733 if (CONT->parent == NULL)
3734 break;
3735
3736 switch (CONT->parent->type) {
3737 case XML_ELEMENT_CONTENT_PCDATA:
3738 DEBUG_VALID_MSG("Error: parent pcdata");
3739 return(-1);
3740 case XML_ELEMENT_CONTENT_ELEMENT:
3741 DEBUG_VALID_MSG("Error: parent element");
3742 return(-1);
3743 case XML_ELEMENT_CONTENT_OR:
3744 if (ret == 1) {
3745 DEBUG_VALID_MSG("Or succeeded");
3746 CONT = CONT->parent;
3747 DEPTH--;
3748 } else {
3749 DEBUG_VALID_MSG("Or failed");
3750 CONT = CONT->parent;
3751 DEPTH--;
3752 }
3753 break;
3754 case XML_ELEMENT_CONTENT_SEQ:
3755 if (ret == 0) {
3756 DEBUG_VALID_MSG("Sequence failed");
3757 CONT = CONT->parent;
3758 DEPTH--;
3759 } else if (CONT == CONT->parent->c1) {
3760 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3761 CONT = CONT->parent->c2;
3762 goto cont;
3763 } else {
3764 DEBUG_VALID_MSG("Sequence succeeded");
3765 CONT = CONT->parent;
3766 DEPTH--;
3767 }
3768 }
3769 }
3770 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003771 xmlNodePtr cur;
3772
3773 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003774 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3775 if (vstateVPop(ctxt) < 0 ) {
3776 DEBUG_VALID_MSG("exhaustion, failed");
3777 return(0);
3778 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003779 if (cur != ctxt->vstate->node)
3780 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003781 goto cont;
3782 }
3783 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003784 xmlNodePtr cur;
3785
3786 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003787 DEBUG_VALID_MSG("Failure, rollback");
3788 if (vstateVPop(ctxt) < 0 ) {
3789 DEBUG_VALID_MSG("exhaustion, failed");
3790 return(0);
3791 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003792 if (cur != ctxt->vstate->node)
3793 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003794 goto cont;
3795 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003796 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003797}
3798
3799/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003800 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003801 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003802 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003803 * @content: An element
3804 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3805 *
3806 * This will dump the list of elements to the buffer
3807 * Intended just for the debug routine
3808 */
3809static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003810xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003811 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003812 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003813
3814 if (node == NULL) return;
3815 if (glob) strcat(buf, "(");
3816 cur = node;
3817 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003818 len = strlen(buf);
3819 if (size - len < 50) {
3820 if ((size - len > 4) && (buf[len - 1] != '.'))
3821 strcat(buf, " ...");
3822 return;
3823 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003824 switch (cur->type) {
3825 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003826 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
3827 if (size - len < xmlStrlen(cur->ns->prefix + 10)) {
3828 if ((size - len > 4) && (buf[len - 1] != '.'))
3829 strcat(buf, " ...");
3830 return;
3831 }
3832 strcat(buf, (char *) cur->ns->prefix);
3833 strcat(buf, ":");
3834 }
Daniel Veillardd3d06722001-08-15 12:06:36 +00003835 if (size - len < xmlStrlen(cur->name + 10)) {
3836 if ((size - len > 4) && (buf[len - 1] != '.'))
3837 strcat(buf, " ...");
3838 return;
3839 }
3840 strcat(buf, (char *) cur->name);
3841 if (cur->next != NULL)
3842 strcat(buf, " ");
3843 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003844 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003845 if (xmlIsBlankNode(cur))
3846 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003847 case XML_CDATA_SECTION_NODE:
3848 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003849 strcat(buf, "CDATA");
3850 if (cur->next != NULL)
3851 strcat(buf, " ");
3852 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003853 case XML_ATTRIBUTE_NODE:
3854 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003855#ifdef LIBXML_DOCB_ENABLED
3856 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003857#endif
3858 case XML_HTML_DOCUMENT_NODE:
3859 case XML_DOCUMENT_TYPE_NODE:
3860 case XML_DOCUMENT_FRAG_NODE:
3861 case XML_NOTATION_NODE:
3862 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003863 strcat(buf, "???");
3864 if (cur->next != NULL)
3865 strcat(buf, " ");
3866 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003867 case XML_ENTITY_NODE:
3868 case XML_PI_NODE:
3869 case XML_DTD_NODE:
3870 case XML_COMMENT_NODE:
3871 case XML_ELEMENT_DECL:
3872 case XML_ATTRIBUTE_DECL:
3873 case XML_ENTITY_DECL:
3874 case XML_XINCLUDE_START:
3875 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003876 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003877 }
3878 cur = cur->next;
3879 }
3880 if (glob) strcat(buf, ")");
3881}
3882
3883/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003884 * xmlValidateElementContent:
3885 * @ctxt: the validation context
3886 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003887 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003888 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003889 *
3890 * Try to validate the content model of an element
3891 *
3892 * returns 1 if valid or 0 if not and -1 in case of error
3893 */
3894
3895static int
3896xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003897 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003898 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003899 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003900 xmlElementContentPtr cont;
3901 const xmlChar *name;
3902
3903 if (elemDecl == NULL)
3904 return(-1);
3905 cont = elemDecl->content;
3906 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003907
3908 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003909 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003910 */
3911 ctxt->vstateMax = 8;
3912 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3913 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3914 if (ctxt->vstateTab == NULL) {
3915 xmlGenericError(xmlGenericErrorContext,
3916 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003917 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003918 }
3919 /*
3920 * The first entry in the stack is reserved to the current state
3921 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00003922 ctxt->nodeMax = 0;
3923 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00003924 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003925 ctxt->vstate = &ctxt->vstateTab[0];
3926 ctxt->vstateNr = 1;
3927 CONT = cont;
3928 NODE = child;
3929 DEPTH = 0;
3930 OCCURS = 0;
3931 STATE = 0;
3932 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003933 if ((ret == -3) && (warn)) {
3934 VWARNING(ctxt->userData,
3935 "Element %s content model is ambiguous\n", name);
3936 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003937 /*
3938 * An entities reference appeared at this level.
3939 * Buid a minimal representation of this node content
3940 * sufficient to run the validation process on it
3941 */
3942 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003943 cur = child;
3944 while (cur != NULL) {
3945 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003946 case XML_ENTITY_REF_NODE:
3947 /*
3948 * Push the current node to be able to roll back
3949 * and process within the entity
3950 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003951 if ((cur->children != NULL) &&
3952 (cur->children->children != NULL)) {
3953 nodeVPush(ctxt, cur);
3954 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003955 continue;
3956 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00003957 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003958 case XML_TEXT_NODE:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003959 case XML_CDATA_SECTION_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003960 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003961 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003962 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003963 case XML_ELEMENT_NODE:
3964 /*
3965 * Allocate a new node and minimally fills in
3966 * what's required
3967 */
3968 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3969 if (tmp == NULL) {
3970 xmlGenericError(xmlGenericErrorContext,
3971 "xmlValidateElementContent : malloc failed\n");
3972 xmlFreeNodeList(repl);
3973 ret = -1;
3974 goto done;
3975 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003976 tmp->type = cur->type;
3977 tmp->name = cur->name;
3978 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003979 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00003980 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003981 if (repl == NULL)
3982 repl = last = tmp;
3983 else {
3984 last->next = tmp;
3985 last = tmp;
3986 }
3987 break;
3988 default:
3989 break;
3990 }
3991 /*
3992 * Switch to next element
3993 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003994 cur = cur->next;
3995 while (cur == NULL) {
3996 cur = nodeVPop(ctxt);
3997 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003998 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003999 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004000 }
4001 }
4002
4003 /*
4004 * Relaunch the validation
4005 */
4006 ctxt->vstate = &ctxt->vstateTab[0];
4007 ctxt->vstateNr = 1;
4008 CONT = cont;
4009 NODE = repl;
4010 DEPTH = 0;
4011 OCCURS = 0;
4012 STATE = 0;
4013 ret = xmlValidateElementType(ctxt);
4014 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004015 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004016 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4017 char expr[5000];
4018 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004019
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004020 expr[0] = 0;
4021 xmlSnprintfElementContent(expr, 5000, cont, 1);
4022 list[0] = 0;
4023 if (repl != NULL)
4024 xmlSnprintfElements(list, 5000, repl, 1);
4025 else
4026 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004027
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004028 if (name != NULL) {
4029 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004030 "Element %s content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004031 name, expr, list);
4032 } else {
4033 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004034 "Element content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004035 expr, list);
4036 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004037 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004038 if (name != NULL) {
4039 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004040 "Element %s content doesn't follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004041 name);
4042 } else {
4043 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004044 "Element content doesn't follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004045 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004046 }
4047 ret = 0;
4048 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004049 if (ret == -3)
4050 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004051
4052
4053done:
4054 /*
4055 * Deallocate the copy if done, and free up the validation stack
4056 */
4057 while (repl != NULL) {
4058 tmp = repl->next;
4059 xmlFree(repl);
4060 repl = tmp;
4061 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004062 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004063 if (ctxt->vstateTab != NULL) {
4064 xmlFree(ctxt->vstateTab);
4065 ctxt->vstateTab = NULL;
4066 }
4067 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004068 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004069 if (ctxt->nodeTab != NULL) {
4070 xmlFree(ctxt->nodeTab);
4071 ctxt->nodeTab = NULL;
4072 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004073 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004074
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004075}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004076
Owen Taylor3473f882001-02-23 17:55:21 +00004077/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004078 * xmlValidateCdataElement:
4079 * @ctxt: the validation context
4080 * @doc: a document instance
4081 * @elem: an element instance
4082 *
4083 * Check that an element follows #CDATA
4084 *
4085 * returns 1 if valid or 0 otherwise
4086 */
4087static int
4088xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4089 xmlNodePtr elem) {
4090 int ret = 1;
4091 xmlNodePtr cur, child;
4092
4093 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4094 return(0);
4095
4096 child = elem->children;
4097
4098 cur = child;
4099 while (cur != NULL) {
4100 switch (cur->type) {
4101 case XML_ENTITY_REF_NODE:
4102 /*
4103 * Push the current node to be able to roll back
4104 * and process within the entity
4105 */
4106 if ((cur->children != NULL) &&
4107 (cur->children->children != NULL)) {
4108 nodeVPush(ctxt, cur);
4109 cur = cur->children->children;
4110 continue;
4111 }
4112 break;
4113 case XML_COMMENT_NODE:
4114 case XML_PI_NODE:
4115 case XML_TEXT_NODE:
4116 case XML_CDATA_SECTION_NODE:
4117 break;
4118 default:
4119 ret = 0;
4120 goto done;
4121 }
4122 /*
4123 * Switch to next element
4124 */
4125 cur = cur->next;
4126 while (cur == NULL) {
4127 cur = nodeVPop(ctxt);
4128 if (cur == NULL)
4129 break;
4130 cur = cur->next;
4131 }
4132 }
4133done:
4134 ctxt->nodeMax = 0;
4135 ctxt->nodeNr = 0;
4136 if (ctxt->nodeTab != NULL) {
4137 xmlFree(ctxt->nodeTab);
4138 ctxt->nodeTab = NULL;
4139 }
4140 return(ret);
4141}
4142
4143/**
Owen Taylor3473f882001-02-23 17:55:21 +00004144 * xmlValidateOneElement:
4145 * @ctxt: the validation context
4146 * @doc: a document instance
4147 * @elem: an element instance
4148 *
4149 * Try to validate a single element and it's attributes,
4150 * basically it does the following checks as described by the
4151 * XML-1.0 recommendation:
4152 * - [ VC: Element Valid ]
4153 * - [ VC: Required Attribute ]
4154 * Then call xmlValidateOneAttribute() for each attribute present.
4155 *
4156 * The ID/IDREF checkings are done separately
4157 *
4158 * returns 1 if valid or 0 otherwise
4159 */
4160
4161int
4162xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4163 xmlNodePtr elem) {
4164 xmlElementPtr elemDecl = NULL;
4165 xmlElementContentPtr cont;
4166 xmlAttributePtr attr;
4167 xmlNodePtr child;
4168 int ret = 1;
4169 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004170 const xmlChar *prefix = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00004171
4172 CHECK_DTD;
4173
4174 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004175 switch (elem->type) {
4176 case XML_ATTRIBUTE_NODE:
4177 VERROR(ctxt->userData,
4178 "Attribute element not expected here\n");
4179 return(0);
4180 case XML_TEXT_NODE:
4181 if (elem->children != NULL) {
4182 VERROR(ctxt->userData, "Text element has childs !\n");
4183 return(0);
4184 }
4185 if (elem->properties != NULL) {
4186 VERROR(ctxt->userData, "Text element has attributes !\n");
4187 return(0);
4188 }
4189 if (elem->ns != NULL) {
4190 VERROR(ctxt->userData, "Text element has namespace !\n");
4191 return(0);
4192 }
4193 if (elem->nsDef != NULL) {
4194 VERROR(ctxt->userData,
4195 "Text element carries namespace definitions !\n");
4196 return(0);
4197 }
4198 if (elem->content == NULL) {
4199 VERROR(ctxt->userData,
4200 "Text element has no content !\n");
4201 return(0);
4202 }
4203 return(1);
4204 case XML_XINCLUDE_START:
4205 case XML_XINCLUDE_END:
4206 return(1);
4207 case XML_CDATA_SECTION_NODE:
4208 case XML_ENTITY_REF_NODE:
4209 case XML_PI_NODE:
4210 case XML_COMMENT_NODE:
4211 return(1);
4212 case XML_ENTITY_NODE:
4213 VERROR(ctxt->userData,
4214 "Entity element not expected here\n");
4215 return(0);
4216 case XML_NOTATION_NODE:
4217 VERROR(ctxt->userData,
4218 "Notation element not expected here\n");
4219 return(0);
4220 case XML_DOCUMENT_NODE:
4221 case XML_DOCUMENT_TYPE_NODE:
4222 case XML_DOCUMENT_FRAG_NODE:
4223 VERROR(ctxt->userData,
4224 "Document element not expected here\n");
4225 return(0);
4226 case XML_HTML_DOCUMENT_NODE:
4227 VERROR(ctxt->userData,
4228 "\n");
4229 return(0);
4230 case XML_ELEMENT_NODE:
4231 break;
4232 default:
4233 VERROR(ctxt->userData,
4234 "unknown element type %d\n", elem->type);
4235 return(0);
4236 }
4237 if (elem->name == NULL) return(0);
4238
4239 /*
4240 * Fetch the declaration for the qualified name
4241 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004242 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4243 prefix = elem->ns->prefix;
4244
4245 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004246 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004247 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004248 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4249 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004250 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004251 }
4252
4253 /*
4254 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004255 * This is "non-strict" validation should be done on the
4256 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004257 */
4258 if (elemDecl == NULL) {
4259 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
4260 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4261 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
4262 }
4263 if (elemDecl == NULL) {
4264 VERROR(ctxt->userData, "No declaration for element %s\n",
4265 elem->name);
4266 return(0);
4267 }
4268
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004269 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004270 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004271 case XML_ELEMENT_TYPE_UNDEFINED:
4272 VERROR(ctxt->userData, "No declaration for element %s\n",
4273 elem->name);
4274 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004275 case XML_ELEMENT_TYPE_EMPTY:
4276 if (elem->children != NULL) {
4277 VERROR(ctxt->userData,
4278 "Element %s was declared EMPTY this one has content\n",
4279 elem->name);
4280 ret = 0;
4281 }
4282 break;
4283 case XML_ELEMENT_TYPE_ANY:
4284 /* I don't think anything is required then */
4285 break;
4286 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004287 /* simple case of declared as #PCDATA */
4288 if ((elemDecl->content != NULL) &&
4289 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4290 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4291 if (!ret) {
4292 VERROR(ctxt->userData,
4293 "Element %s was declared #PCDATA but contains non text nodes\n",
4294 elem->name);
4295 }
4296 break;
4297 }
Owen Taylor3473f882001-02-23 17:55:21 +00004298 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004299 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004300 while (child != NULL) {
4301 if (child->type == XML_ELEMENT_NODE) {
4302 name = child->name;
4303 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4304 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004305 snprintf((char *) qname, sizeof(qname), "%s:%s",
4306 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004307 qname[sizeof(qname) - 1] = 0;
4308 cont = elemDecl->content;
4309 while (cont != NULL) {
4310 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4311 if (xmlStrEqual(cont->name, qname)) break;
4312 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4313 (cont->c1 != NULL) &&
4314 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4315 if (xmlStrEqual(cont->c1->name, qname)) break;
4316 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4317 (cont->c1 == NULL) ||
4318 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4319 /* Internal error !!! */
4320 xmlGenericError(xmlGenericErrorContext,
4321 "Internal: MIXED struct bad\n");
4322 break;
4323 }
4324 cont = cont->c2;
4325 }
4326 if (cont != NULL)
4327 goto child_ok;
4328 }
4329 cont = elemDecl->content;
4330 while (cont != NULL) {
4331 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4332 if (xmlStrEqual(cont->name, name)) break;
4333 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4334 (cont->c1 != NULL) &&
4335 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4336 if (xmlStrEqual(cont->c1->name, name)) break;
4337 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4338 (cont->c1 == NULL) ||
4339 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4340 /* Internal error !!! */
4341 xmlGenericError(xmlGenericErrorContext,
4342 "Internal: MIXED struct bad\n");
4343 break;
4344 }
4345 cont = cont->c2;
4346 }
4347 if (cont == NULL) {
4348 VERROR(ctxt->userData,
4349 "Element %s is not declared in %s list of possible childs\n",
4350 name, elem->name);
4351 ret = 0;
4352 }
4353 }
4354child_ok:
4355 child = child->next;
4356 }
4357 break;
4358 case XML_ELEMENT_TYPE_ELEMENT:
4359 child = elem->children;
4360 cont = elemDecl->content;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004361 ret = xmlValidateElementContent(ctxt, child, elemDecl, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00004362 break;
4363 }
4364
4365 /* [ VC: Required Attribute ] */
4366 attr = elemDecl->attributes;
4367 while (attr != NULL) {
4368 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4369 xmlAttrPtr attrib;
4370 int qualified = -1;
4371
4372 attrib = elem->properties;
4373 while (attrib != NULL) {
4374 if (xmlStrEqual(attrib->name, attr->name)) {
4375 if (attr->prefix != NULL) {
4376 xmlNsPtr nameSpace = attrib->ns;
4377
4378 if (nameSpace == NULL)
4379 nameSpace = elem->ns;
4380 /*
4381 * qualified names handling is problematic, having a
4382 * different prefix should be possible but DTDs don't
4383 * allow to define the URI instead of the prefix :-(
4384 */
4385 if (nameSpace == NULL) {
4386 if (qualified < 0)
4387 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004388 } else if (!xmlStrEqual(nameSpace->prefix,
4389 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004390 if (qualified < 1)
4391 qualified = 1;
4392 } else
4393 goto found;
4394 } else {
4395 /*
4396 * We should allow applications to define namespaces
4397 * for their application even if the DTD doesn't
4398 * carry one, otherwise, basically we would always
4399 * break.
4400 */
4401 goto found;
4402 }
4403 }
4404 attrib = attrib->next;
4405 }
4406 if (qualified == -1) {
4407 if (attr->prefix == NULL) {
4408 VERROR(ctxt->userData,
4409 "Element %s doesn't carry attribute %s\n",
4410 elem->name, attr->name);
4411 ret = 0;
4412 } else {
4413 VERROR(ctxt->userData,
4414 "Element %s doesn't carry attribute %s:%s\n",
4415 elem->name, attr->prefix,attr->name);
4416 ret = 0;
4417 }
4418 } else if (qualified == 0) {
4419 VWARNING(ctxt->userData,
4420 "Element %s required attribute %s:%s has no prefix\n",
4421 elem->name, attr->prefix,attr->name);
4422 } else if (qualified == 1) {
4423 VWARNING(ctxt->userData,
4424 "Element %s required attribute %s:%s has different prefix\n",
4425 elem->name, attr->prefix,attr->name);
4426 }
4427 }
4428found:
4429 attr = attr->nexth;
4430 }
4431 return(ret);
4432}
4433
4434/**
4435 * xmlValidateRoot:
4436 * @ctxt: the validation context
4437 * @doc: a document instance
4438 *
4439 * Try to validate a the root element
4440 * basically it does the following check as described by the
4441 * XML-1.0 recommendation:
4442 * - [ VC: Root Element Type ]
4443 * it doesn't try to recurse or apply other check to the element
4444 *
4445 * returns 1 if valid or 0 otherwise
4446 */
4447
4448int
4449xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4450 xmlNodePtr root;
4451 if (doc == NULL) return(0);
4452
4453 root = xmlDocGetRootElement(doc);
4454 if ((root == NULL) || (root->name == NULL)) {
4455 VERROR(ctxt->userData, "Not valid: no root element\n");
4456 return(0);
4457 }
4458
4459 /*
4460 * When doing post validation against a separate DTD, those may
4461 * no internal subset has been generated
4462 */
4463 if ((doc->intSubset != NULL) &&
4464 (doc->intSubset->name != NULL)) {
4465 /*
4466 * Check first the document root against the NQName
4467 */
4468 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4469 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4470 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004471 snprintf((char *) qname, sizeof(qname), "%s:%s",
4472 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004473 qname[sizeof(qname) - 1] = 0;
4474 if (xmlStrEqual(doc->intSubset->name, qname))
4475 goto name_ok;
4476 }
4477 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4478 (xmlStrEqual(root->name, BAD_CAST "html")))
4479 goto name_ok;
4480 VERROR(ctxt->userData,
4481 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4482 root->name, doc->intSubset->name);
4483 return(0);
4484
4485 }
4486 }
4487name_ok:
4488 return(1);
4489}
4490
4491
4492/**
4493 * xmlValidateElement:
4494 * @ctxt: the validation context
4495 * @doc: a document instance
4496 * @elem: an element instance
4497 *
4498 * Try to validate the subtree under an element
4499 *
4500 * returns 1 if valid or 0 otherwise
4501 */
4502
4503int
4504xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4505 xmlNodePtr child;
4506 xmlAttrPtr attr;
4507 xmlChar *value;
4508 int ret = 1;
4509
4510 if (elem == NULL) return(0);
4511
4512 /*
4513 * XInclude elements were added after parsing in the infoset,
4514 * they don't really mean anything validation wise.
4515 */
4516 if ((elem->type == XML_XINCLUDE_START) ||
4517 (elem->type == XML_XINCLUDE_END))
4518 return(1);
4519
4520 CHECK_DTD;
4521
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004522 /*
4523 * Entities references have to be handled separately
4524 */
4525 if (elem->type == XML_ENTITY_REF_NODE) {
4526 return(1);
4527 }
4528
Owen Taylor3473f882001-02-23 17:55:21 +00004529 ret &= xmlValidateOneElement(ctxt, doc, elem);
4530 attr = elem->properties;
4531 while(attr != NULL) {
4532 value = xmlNodeListGetString(doc, attr->children, 0);
4533 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4534 if (value != NULL)
4535 xmlFree(value);
4536 attr= attr->next;
4537 }
4538 child = elem->children;
4539 while (child != NULL) {
4540 ret &= xmlValidateElement(ctxt, doc, child);
4541 child = child->next;
4542 }
4543
4544 return(ret);
4545}
4546
Daniel Veillard8730c562001-02-26 10:49:57 +00004547/**
4548 * xmlValidateRef:
4549 * @ref: A reference to be validated
4550 * @ctxt: Validation context
4551 * @name: Name of ID we are searching for
4552 *
4553 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004554static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004555xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004556 const xmlChar *name) {
4557 xmlAttrPtr id;
4558 xmlAttrPtr attr;
4559
4560 if (ref == NULL)
4561 return;
4562 attr = ref->attr;
4563 if (attr == NULL)
4564 return;
4565 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4566 id = xmlGetID(ctxt->doc, name);
4567 if (id == NULL) {
4568 VERROR(ctxt->userData,
4569 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4570 attr->name, name);
4571 ctxt->valid = 0;
4572 }
4573 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4574 xmlChar *dup, *str = NULL, *cur, save;
4575
4576 dup = xmlStrdup(name);
4577 if (dup == NULL) {
4578 ctxt->valid = 0;
4579 return;
4580 }
4581 cur = dup;
4582 while (*cur != 0) {
4583 str = cur;
4584 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4585 save = *cur;
4586 *cur = 0;
4587 id = xmlGetID(ctxt->doc, str);
4588 if (id == NULL) {
4589 VERROR(ctxt->userData,
4590 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4591 attr->name, str);
4592 ctxt->valid = 0;
4593 }
4594 if (save == 0)
4595 break;
4596 *cur = save;
4597 while (IS_BLANK(*cur)) cur++;
4598 }
4599 xmlFree(dup);
4600 }
4601}
4602
4603/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004604 * xmlWalkValidateList:
4605 * @data: Contents of current link
4606 * @user: Value supplied by the user
4607 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004608 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004609 */
4610static int
4611xmlWalkValidateList(const void *data, const void *user)
4612{
4613 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4614 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4615 return 1;
4616}
4617
4618/**
4619 * xmlValidateCheckRefCallback:
4620 * @ref_list: List of references
4621 * @ctxt: Validation context
4622 * @name: Name of ID we are searching for
4623 *
4624 */
4625static void
4626xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4627 const xmlChar *name) {
4628 xmlValidateMemo memo;
4629
4630 if (ref_list == NULL)
4631 return;
4632 memo.ctxt = ctxt;
4633 memo.name = name;
4634
4635 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4636
4637}
4638
4639/**
Owen Taylor3473f882001-02-23 17:55:21 +00004640 * xmlValidateDocumentFinal:
4641 * @ctxt: the validation context
4642 * @doc: a document instance
4643 *
4644 * Does the final step for the document validation once all the
4645 * incremental validation steps have been completed
4646 *
4647 * basically it does the following checks described by the XML Rec
4648 *
4649 *
4650 * returns 1 if valid or 0 otherwise
4651 */
4652
4653int
4654xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4655 xmlRefTablePtr table;
4656
4657 if (doc == NULL) {
4658 xmlGenericError(xmlGenericErrorContext,
4659 "xmlValidateDocumentFinal: doc == NULL\n");
4660 return(0);
4661 }
4662
4663 /*
4664 * Check all the NOTATION/NOTATIONS attributes
4665 */
4666 /*
4667 * Check all the ENTITY/ENTITIES attributes definition for validity
4668 */
4669 /*
4670 * Check all the IDREF/IDREFS attributes definition for validity
4671 */
4672 table = (xmlRefTablePtr) doc->refs;
4673 ctxt->doc = doc;
4674 ctxt->valid = 1;
4675 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4676 return(ctxt->valid);
4677}
4678
4679/**
4680 * xmlValidateDtd:
4681 * @ctxt: the validation context
4682 * @doc: a document instance
4683 * @dtd: a dtd instance
4684 *
4685 * Try to validate the document against the dtd instance
4686 *
4687 * basically it does check all the definitions in the DtD.
4688 *
4689 * returns 1 if valid or 0 otherwise
4690 */
4691
4692int
4693xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4694 int ret;
4695 xmlDtdPtr oldExt;
4696 xmlNodePtr root;
4697
4698 if (dtd == NULL) return(0);
4699 if (doc == NULL) return(0);
4700 oldExt = doc->extSubset;
4701 doc->extSubset = dtd;
4702 ret = xmlValidateRoot(ctxt, doc);
4703 if (ret == 0) {
4704 doc->extSubset = oldExt;
4705 return(ret);
4706 }
4707 if (doc->ids != NULL) {
4708 xmlFreeIDTable(doc->ids);
4709 doc->ids = NULL;
4710 }
4711 if (doc->refs != NULL) {
4712 xmlFreeRefTable(doc->refs);
4713 doc->refs = NULL;
4714 }
4715 root = xmlDocGetRootElement(doc);
4716 ret = xmlValidateElement(ctxt, doc, root);
4717 ret &= xmlValidateDocumentFinal(ctxt, doc);
4718 doc->extSubset = oldExt;
4719 return(ret);
4720}
4721
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004722static void
Owen Taylor3473f882001-02-23 17:55:21 +00004723xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004724 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004725 if (cur == NULL)
4726 return;
4727 switch (cur->atype) {
4728 case XML_ATTRIBUTE_CDATA:
4729 case XML_ATTRIBUTE_ID:
4730 case XML_ATTRIBUTE_IDREF :
4731 case XML_ATTRIBUTE_IDREFS:
4732 case XML_ATTRIBUTE_NMTOKEN:
4733 case XML_ATTRIBUTE_NMTOKENS:
4734 case XML_ATTRIBUTE_ENUMERATION:
4735 break;
4736 case XML_ATTRIBUTE_ENTITY:
4737 case XML_ATTRIBUTE_ENTITIES:
4738 case XML_ATTRIBUTE_NOTATION:
4739 if (cur->defaultValue != NULL) {
4740 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4741 cur->name, cur->atype, cur->defaultValue);
4742 }
4743 if (cur->tree != NULL) {
4744 xmlEnumerationPtr tree = cur->tree;
4745 while (tree != NULL) {
4746 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4747 cur->name, cur->atype, tree->name);
4748 tree = tree->next;
4749 }
4750 }
4751 }
4752}
4753
4754/**
4755 * xmlValidateDtdFinal:
4756 * @ctxt: the validation context
4757 * @doc: a document instance
4758 *
4759 * Does the final step for the dtds validation once all the
4760 * subsets have been parsed
4761 *
4762 * basically it does the following checks described by the XML Rec
4763 * - check that ENTITY and ENTITIES type attributes default or
4764 * possible values matches one of the defined entities.
4765 * - check that NOTATION type attributes default or
4766 * possible values matches one of the defined notations.
4767 *
4768 * returns 1 if valid or 0 otherwise
4769 */
4770
4771int
4772xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4773 int ret = 1;
4774 xmlDtdPtr dtd;
4775 xmlAttributeTablePtr table;
4776
4777 if (doc == NULL) return(0);
4778 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4779 return(0);
4780 ctxt->doc = doc;
4781 ctxt->valid = ret;
4782 dtd = doc->intSubset;
4783 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4784 table = (xmlAttributeTablePtr) dtd->attributes;
4785 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4786 }
4787 dtd = doc->extSubset;
4788 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4789 table = (xmlAttributeTablePtr) dtd->attributes;
4790 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4791 }
4792 return(ctxt->valid);
4793}
4794
4795/**
4796 * xmlValidateDocument:
4797 * @ctxt: the validation context
4798 * @doc: a document instance
4799 *
4800 * Try to validate the document instance
4801 *
4802 * basically it does the all the checks described by the XML Rec
4803 * i.e. validates the internal and external subset (if present)
4804 * and validate the document tree.
4805 *
4806 * returns 1 if valid or 0 otherwise
4807 */
4808
4809int
4810xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4811 int ret;
4812 xmlNodePtr root;
4813
4814 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4815 return(0);
4816 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4817 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4818 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4819 doc->intSubset->SystemID);
4820 if (doc->extSubset == NULL) {
4821 if (doc->intSubset->SystemID != NULL) {
4822 VERROR(ctxt->userData,
4823 "Could not load the external subset \"%s\"\n",
4824 doc->intSubset->SystemID);
4825 } else {
4826 VERROR(ctxt->userData,
4827 "Could not load the external subset \"%s\"\n",
4828 doc->intSubset->ExternalID);
4829 }
4830 return(0);
4831 }
4832 }
4833
4834 if (doc->ids != NULL) {
4835 xmlFreeIDTable(doc->ids);
4836 doc->ids = NULL;
4837 }
4838 if (doc->refs != NULL) {
4839 xmlFreeRefTable(doc->refs);
4840 doc->refs = NULL;
4841 }
4842 ret = xmlValidateDtdFinal(ctxt, doc);
4843 if (!xmlValidateRoot(ctxt, doc)) return(0);
4844
4845 root = xmlDocGetRootElement(doc);
4846 ret &= xmlValidateElement(ctxt, doc, root);
4847 ret &= xmlValidateDocumentFinal(ctxt, doc);
4848 return(ret);
4849}
4850
4851
4852/************************************************************************
4853 * *
4854 * Routines for dynamic validation editing *
4855 * *
4856 ************************************************************************/
4857
4858/**
4859 * xmlValidGetPotentialChildren:
4860 * @ctree: an element content tree
4861 * @list: an array to store the list of child names
4862 * @len: a pointer to the number of element in the list
4863 * @max: the size of the array
4864 *
4865 * Build/extend a list of potential children allowed by the content tree
4866 *
4867 * returns the number of element in the list, or -1 in case of error.
4868 */
4869
4870int
4871xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4872 int *len, int max) {
4873 int i;
4874
4875 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4876 return(-1);
4877 if (*len >= max) return(*len);
4878
4879 switch (ctree->type) {
4880 case XML_ELEMENT_CONTENT_PCDATA:
4881 for (i = 0; i < *len;i++)
4882 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4883 list[(*len)++] = BAD_CAST "#PCDATA";
4884 break;
4885 case XML_ELEMENT_CONTENT_ELEMENT:
4886 for (i = 0; i < *len;i++)
4887 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4888 list[(*len)++] = ctree->name;
4889 break;
4890 case XML_ELEMENT_CONTENT_SEQ:
4891 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4892 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4893 break;
4894 case XML_ELEMENT_CONTENT_OR:
4895 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4896 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4897 break;
4898 }
4899
4900 return(*len);
4901}
4902
4903/**
4904 * xmlValidGetValidElements:
4905 * @prev: an element to insert after
4906 * @next: an element to insert next
4907 * @list: an array to store the list of child names
4908 * @max: the size of the array
4909 *
4910 * This function returns the list of authorized children to insert
4911 * within an existing tree while respecting the validity constraints
4912 * forced by the Dtd. The insertion point is defined using @prev and
4913 * @next in the following ways:
4914 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4915 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4916 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4917 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4918 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4919 *
4920 * pointers to the element names are inserted at the beginning of the array
4921 * and do not need to be freed.
4922 *
4923 * returns the number of element in the list, or -1 in case of error. If
4924 * the function returns the value @max the caller is invited to grow the
4925 * receiving array and retry.
4926 */
4927
4928int
4929xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4930 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004931 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00004932 int nb_valid_elements = 0;
4933 const xmlChar *elements[256];
4934 int nb_elements = 0, i;
4935
4936 xmlNode *ref_node;
4937 xmlNode *parent;
4938 xmlNode *test_node;
4939
4940 xmlNode *prev_next;
4941 xmlNode *next_prev;
4942 xmlNode *parent_childs;
4943 xmlNode *parent_last;
4944
4945 xmlElement *element_desc;
4946
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004947 vctxt.userData = NULL;
4948 vctxt.error = NULL;
4949 vctxt.warning = NULL;
4950
Owen Taylor3473f882001-02-23 17:55:21 +00004951 if (prev == NULL && next == NULL)
4952 return(-1);
4953
4954 if (list == NULL) return(-1);
4955 if (max <= 0) return(-1);
4956
4957 nb_valid_elements = 0;
4958 ref_node = prev ? prev : next;
4959 parent = ref_node->parent;
4960
4961 /*
4962 * Retrieves the parent element declaration
4963 */
4964 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4965 parent->name);
4966 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4967 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4968 parent->name);
4969 if (element_desc == NULL) return(-1);
4970
4971 /*
4972 * Do a backup of the current tree structure
4973 */
4974 prev_next = prev ? prev->next : NULL;
4975 next_prev = next ? next->prev : NULL;
4976 parent_childs = parent->children;
4977 parent_last = parent->last;
4978
4979 /*
4980 * Creates a dummy node and insert it into the tree
4981 */
4982 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4983 test_node->doc = ref_node->doc;
4984 test_node->parent = parent;
4985 test_node->prev = prev;
4986 test_node->next = next;
4987
4988 if (prev) prev->next = test_node;
4989 else parent->children = test_node;
4990
4991 if (next) next->prev = test_node;
4992 else parent->last = test_node;
4993
4994 /*
4995 * Insert each potential child node and check if the parent is
4996 * still valid
4997 */
4998 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4999 elements, &nb_elements, 256);
5000
5001 for (i = 0;i < nb_elements;i++) {
5002 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005003 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005004 int j;
5005
5006 for (j = 0; j < nb_valid_elements;j++)
5007 if (xmlStrEqual(elements[i], list[j])) break;
5008 list[nb_valid_elements++] = elements[i];
5009 if (nb_valid_elements >= max) break;
5010 }
5011 }
5012
5013 /*
5014 * Restore the tree structure
5015 */
5016 if (prev) prev->next = prev_next;
5017 if (next) next->prev = next_prev;
5018 parent->children = parent_childs;
5019 parent->last = parent_last;
5020
5021 return(nb_valid_elements);
5022}