blob: 07ad05f9321eaeb52c94de57adbb20aa03564330 [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 *
7 * Daniel.Veillard@w3.org
8 */
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>
25
Daniel Veillarde62d36c2001-05-15 08:53:16 +000026/* #define DEBUG_VALID_ALGO */
27
Owen Taylor3473f882001-02-23 17:55:21 +000028/*
29 * Generic function for accessing stacks in the Validity Context
30 */
31
32#define PUSH_AND_POP(scope, type, name) \
33scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000034 if (ctxt->name##Max <= 0) { \
35 ctxt->name##Max = 4; \
36 ctxt->name##Tab = (type *) xmlMalloc( \
37 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
38 if (ctxt->name##Tab == NULL) { \
39 xmlGenericError(xmlGenericErrorContext, \
40 "malloc failed !\n"); \
41 return(0); \
42 } \
43 } \
Owen Taylor3473f882001-02-23 17:55:21 +000044 if (ctxt->name##Nr >= ctxt->name##Max) { \
45 ctxt->name##Max *= 2; \
46 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
47 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
48 if (ctxt->name##Tab == NULL) { \
49 xmlGenericError(xmlGenericErrorContext, \
50 "realloc failed !\n"); \
51 return(0); \
52 } \
53 } \
54 ctxt->name##Tab[ctxt->name##Nr] = value; \
55 ctxt->name = value; \
56 return(ctxt->name##Nr++); \
57} \
58scope type name##VPop(xmlValidCtxtPtr ctxt) { \
59 type ret; \
60 if (ctxt->name##Nr <= 0) return(0); \
61 ctxt->name##Nr--; \
62 if (ctxt->name##Nr > 0) \
63 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
64 else \
65 ctxt->name = NULL; \
66 ret = ctxt->name##Tab[ctxt->name##Nr]; \
67 ctxt->name##Tab[ctxt->name##Nr] = 0; \
68 return(ret); \
69} \
70
Daniel Veillarddab4cb32001-04-20 13:03:48 +000071/*
72 * I will use a home made algorithm less complex and easier to
73 * debug/maintin than a generic NFA -> DFA state based algo. The
74 * only restriction is on the deepness of the tree limited by the
75 * size of the occurs bitfield
76 *
77 * this is the content of a saved state for rollbacks
78 */
79
80#define ROLLBACK_OR 0
81#define ROLLBACK_PARENT 1
82
83struct _xmlValidState {
84 xmlElementContentPtr cont; /* pointer to the content model subtree */
85 xmlNodePtr node; /* pointer to the current node in the list */
86 long occurs;/* bitfield for multiple occurences */
87 unsigned char depth; /* current depth in the overall tree */
88 unsigned char state; /* ROLLBACK_XXX */
89} _xmlValidState;
90
91#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
92#define CONT ctxt->vstate->cont
93#define NODE ctxt->vstate->node
94#define DEPTH ctxt->vstate->depth
95#define OCCURS ctxt->vstate->occurs
96#define STATE ctxt->vstate->state
97
98#define OCCURENCE (ctxt->vstate->occurs & (1 << DEPTH))
99#define PARENT_OCCURENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
100
101#define SET_OCCURENCE ctxt->vstate->occurs |= (1 << DEPTH)
102#define RESET_OCCURENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
103
104static int
105vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
106 xmlNodePtr node, unsigned char depth, long occurs,
107 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000108 int i = ctxt->vstateNr - 1;
109
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000110 if (ctxt->vstateNr >= ctxt->vstateMax) {
111 ctxt->vstateMax *= 2;
112 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
113 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
114 if (ctxt->vstateTab == NULL) {
115 xmlGenericError(xmlGenericErrorContext,
116 "realloc failed !n");
117 return(0);
118 }
Daniel Veillard06803992001-04-22 10:35:56 +0000119 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000120 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000121 /*
122 * Don't push on the stack a state already here
123 */
124 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
125 (ctxt->vstateTab[i].node == node) &&
126 (ctxt->vstateTab[i].depth == depth) &&
127 (ctxt->vstateTab[i].occurs == occurs) &&
128 (ctxt->vstateTab[i].state == state))
129 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000130 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
131 ctxt->vstateTab[ctxt->vstateNr].node = node;
132 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
133 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
134 ctxt->vstateTab[ctxt->vstateNr].state = state;
135 return(ctxt->vstateNr++);
136}
137
138static int
139vstateVPop(xmlValidCtxtPtr ctxt) {
140 if (ctxt->vstateNr <= 1) return(-1);
141 ctxt->vstateNr--;
142 ctxt->vstate = &ctxt->vstateTab[0];
143 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
144 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
145 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
146 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
147 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
148 return(ctxt->vstateNr);
149}
150
Owen Taylor3473f882001-02-23 17:55:21 +0000151PUSH_AND_POP(static, xmlNodePtr, node)
152
Owen Taylor3473f882001-02-23 17:55:21 +0000153#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000154static void
155xmlValidPrintNode(xmlNodePtr cur) {
156 if (cur == NULL) {
157 xmlGenericError(xmlGenericErrorContext, "null");
158 return;
159 }
160 switch (cur->type) {
161 case XML_ELEMENT_NODE:
162 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
163 break;
164 case XML_TEXT_NODE:
165 xmlGenericError(xmlGenericErrorContext, "text ");
166 break;
167 case XML_CDATA_SECTION_NODE:
168 xmlGenericError(xmlGenericErrorContext, "cdata ");
169 break;
170 case XML_ENTITY_REF_NODE:
171 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
172 break;
173 case XML_PI_NODE:
174 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
175 break;
176 case XML_COMMENT_NODE:
177 xmlGenericError(xmlGenericErrorContext, "comment ");
178 break;
179 case XML_ATTRIBUTE_NODE:
180 xmlGenericError(xmlGenericErrorContext, "?attr? ");
181 break;
182 case XML_ENTITY_NODE:
183 xmlGenericError(xmlGenericErrorContext, "?ent? ");
184 break;
185 case XML_DOCUMENT_NODE:
186 xmlGenericError(xmlGenericErrorContext, "?doc? ");
187 break;
188 case XML_DOCUMENT_TYPE_NODE:
189 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
190 break;
191 case XML_DOCUMENT_FRAG_NODE:
192 xmlGenericError(xmlGenericErrorContext, "?frag? ");
193 break;
194 case XML_NOTATION_NODE:
195 xmlGenericError(xmlGenericErrorContext, "?nota? ");
196 break;
197 case XML_HTML_DOCUMENT_NODE:
198 xmlGenericError(xmlGenericErrorContext, "?html? ");
199 break;
200 case XML_DTD_NODE:
201 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
202 break;
203 case XML_ELEMENT_DECL:
204 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
205 break;
206 case XML_ATTRIBUTE_DECL:
207 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
208 break;
209 case XML_ENTITY_DECL:
210 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
211 break;
212 case XML_NAMESPACE_DECL:
213 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
214 break;
215 case XML_XINCLUDE_START:
216 xmlGenericError(xmlGenericErrorContext, "incstart ");
217 break;
218 case XML_XINCLUDE_END:
219 xmlGenericError(xmlGenericErrorContext, "incend ");
220 break;
221 }
222}
223
224static void
225xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000226 if (cur == NULL)
227 xmlGenericError(xmlGenericErrorContext, "null ");
228 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000229 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000230 cur = cur->next;
231 }
232}
233
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000234static void
235xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000236 char expr[1000];
237
238 expr[0] = 0;
239 xmlGenericError(xmlGenericErrorContext, "valid: ");
240 xmlValidPrintNodeList(cur);
241 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000242 xmlSprintfElementContent(expr, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000243 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
244}
245
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000246static void
247xmlValidDebugState(xmlValidStatePtr state) {
248 xmlGenericError(xmlGenericErrorContext, "(");
249 if (state->cont == NULL)
250 xmlGenericError(xmlGenericErrorContext, "null,");
251 else
252 switch (state->cont->type) {
253 case XML_ELEMENT_CONTENT_PCDATA:
254 xmlGenericError(xmlGenericErrorContext, "pcdata,");
255 break;
256 case XML_ELEMENT_CONTENT_ELEMENT:
257 xmlGenericError(xmlGenericErrorContext, "%s,",
258 state->cont->name);
259 break;
260 case XML_ELEMENT_CONTENT_SEQ:
261 xmlGenericError(xmlGenericErrorContext, "seq,");
262 break;
263 case XML_ELEMENT_CONTENT_OR:
264 xmlGenericError(xmlGenericErrorContext, "or,");
265 break;
266 }
267 xmlValidPrintNode(state->node);
268 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
269 state->depth, state->occurs, state->state);
270}
271
272static void
273xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
274 int i, j;
275
276 xmlGenericError(xmlGenericErrorContext, "state: ");
277 xmlValidDebugState(ctxt->vstate);
278 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
279 ctxt->vstateNr - 1);
280 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
281 xmlValidDebugState(&ctxt->vstateTab[j]);
282 xmlGenericError(xmlGenericErrorContext, "\n");
283}
284
285/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000286#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000287 *****/
288
289#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000290#define DEBUG_VALID_MSG(m) \
291 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
292
Owen Taylor3473f882001-02-23 17:55:21 +0000293#else
294#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000295#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000296#endif
297
298/* TODO: use hash table for accesses to elem and attribute dedinitions */
299
300#define VERROR \
301 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
302
303#define VWARNING \
304 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
305
306#define CHECK_DTD \
307 if (doc == NULL) return(0); \
308 else if ((doc->intSubset == NULL) && \
309 (doc->extSubset == NULL)) return(0)
310
311xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000312static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
313 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000314xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
315
316/************************************************************************
317 * *
318 * QName handling helper *
319 * *
320 ************************************************************************/
321
322/**
323 * xmlSplitQName2:
324 * @name: an XML parser context
325 * @prefix: a xmlChar **
326 *
327 * parse an XML qualified name string
328 *
329 * [NS 5] QName ::= (Prefix ':')? LocalPart
330 *
331 * [NS 6] Prefix ::= NCName
332 *
333 * [NS 7] LocalPart ::= NCName
334 *
335 * Returns NULL if not a QName, otherwise the local part, and prefix
336 * is updated to get the Prefix if any.
337 */
338
339xmlChar *
340xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
341 int len = 0;
342 xmlChar *ret = NULL;
343
344 *prefix = NULL;
345
346 /* xml: prefix is not really a namespace */
347 if ((name[0] == 'x') && (name[1] == 'm') &&
348 (name[2] == 'l') && (name[3] == ':'))
349 return(NULL);
350
351 /* nasty but valid */
352 if (name[0] == ':')
353 return(NULL);
354
355 /*
356 * we are not trying to validate but just to cut, and yes it will
357 * work even if this is as set of UTF-8 encoded chars
358 */
359 while ((name[len] != 0) && (name[len] != ':'))
360 len++;
361
362 if (name[len] == 0)
363 return(NULL);
364
365 *prefix = xmlStrndup(name, len);
366 ret = xmlStrdup(&name[len + 1]);
367
368 return(ret);
369}
370
371/****************************************************************
372 * *
373 * Util functions for data allocation/deallocation *
374 * *
375 ****************************************************************/
376
377/**
378 * xmlNewElementContent:
379 * @name: the subelement name or NULL
380 * @type: the type of element content decl
381 *
382 * Allocate an element content structure.
383 *
384 * Returns NULL if not, othervise the new element content structure
385 */
386xmlElementContentPtr
387xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
388 xmlElementContentPtr ret;
389
390 switch(type) {
391 case XML_ELEMENT_CONTENT_ELEMENT:
392 if (name == NULL) {
393 xmlGenericError(xmlGenericErrorContext,
394 "xmlNewElementContent : name == NULL !\n");
395 }
396 break;
397 case XML_ELEMENT_CONTENT_PCDATA:
398 case XML_ELEMENT_CONTENT_SEQ:
399 case XML_ELEMENT_CONTENT_OR:
400 if (name != NULL) {
401 xmlGenericError(xmlGenericErrorContext,
402 "xmlNewElementContent : name != NULL !\n");
403 }
404 break;
405 default:
406 xmlGenericError(xmlGenericErrorContext,
407 "xmlNewElementContent: unknown type %d\n", type);
408 return(NULL);
409 }
410 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
411 if (ret == NULL) {
412 xmlGenericError(xmlGenericErrorContext,
413 "xmlNewElementContent : out of memory!\n");
414 return(NULL);
415 }
416 ret->type = type;
417 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
418 if (name != NULL)
419 ret->name = xmlStrdup(name);
420 else
421 ret->name = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000422 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000423 return(ret);
424}
425
426/**
427 * xmlCopyElementContent:
428 * @content: An element content pointer.
429 *
430 * Build a copy of an element content description.
431 *
432 * Returns the new xmlElementContentPtr or NULL in case of error.
433 */
434xmlElementContentPtr
435xmlCopyElementContent(xmlElementContentPtr cur) {
436 xmlElementContentPtr ret;
437
438 if (cur == NULL) return(NULL);
439 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
440 if (ret == NULL) {
441 xmlGenericError(xmlGenericErrorContext,
442 "xmlCopyElementContent : out of memory\n");
443 return(NULL);
444 }
445 ret->ocur = cur->ocur;
446 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000447 if (ret->c1 != NULL)
448 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000449 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000450 if (ret->c2 != NULL)
451 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000452 return(ret);
453}
454
455/**
456 * xmlFreeElementContent:
457 * @cur: the element content tree to free
458 *
459 * Free an element content structure. This is a recursive call !
460 */
461void
462xmlFreeElementContent(xmlElementContentPtr cur) {
463 if (cur == NULL) return;
464 switch (cur->type) {
465 case XML_ELEMENT_CONTENT_PCDATA:
466 case XML_ELEMENT_CONTENT_ELEMENT:
467 case XML_ELEMENT_CONTENT_SEQ:
468 case XML_ELEMENT_CONTENT_OR:
469 break;
470 default:
471 xmlGenericError(xmlGenericErrorContext,
472 "xmlFreeElementContent : type %d\n", cur->type);
473 return;
474 }
475 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
476 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
477 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000478 xmlFree(cur);
479}
480
481/**
482 * xmlDumpElementContent:
483 * @buf: An XML buffer
484 * @content: An element table
485 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
486 *
487 * This will dump the content of the element table as an XML DTD definition
488 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000489static void
Owen Taylor3473f882001-02-23 17:55:21 +0000490xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
491 if (content == NULL) return;
492
493 if (glob) xmlBufferWriteChar(buf, "(");
494 switch (content->type) {
495 case XML_ELEMENT_CONTENT_PCDATA:
496 xmlBufferWriteChar(buf, "#PCDATA");
497 break;
498 case XML_ELEMENT_CONTENT_ELEMENT:
499 xmlBufferWriteCHAR(buf, content->name);
500 break;
501 case XML_ELEMENT_CONTENT_SEQ:
502 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
503 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
504 xmlDumpElementContent(buf, content->c1, 1);
505 else
506 xmlDumpElementContent(buf, content->c1, 0);
507 xmlBufferWriteChar(buf, " , ");
508 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
509 xmlDumpElementContent(buf, content->c2, 1);
510 else
511 xmlDumpElementContent(buf, content->c2, 0);
512 break;
513 case XML_ELEMENT_CONTENT_OR:
514 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
515 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
516 xmlDumpElementContent(buf, content->c1, 1);
517 else
518 xmlDumpElementContent(buf, content->c1, 0);
519 xmlBufferWriteChar(buf, " | ");
520 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
521 xmlDumpElementContent(buf, content->c2, 1);
522 else
523 xmlDumpElementContent(buf, content->c2, 0);
524 break;
525 default:
526 xmlGenericError(xmlGenericErrorContext,
527 "xmlDumpElementContent: unknown type %d\n",
528 content->type);
529 }
530 if (glob)
531 xmlBufferWriteChar(buf, ")");
532 switch (content->ocur) {
533 case XML_ELEMENT_CONTENT_ONCE:
534 break;
535 case XML_ELEMENT_CONTENT_OPT:
536 xmlBufferWriteChar(buf, "?");
537 break;
538 case XML_ELEMENT_CONTENT_MULT:
539 xmlBufferWriteChar(buf, "*");
540 break;
541 case XML_ELEMENT_CONTENT_PLUS:
542 xmlBufferWriteChar(buf, "+");
543 break;
544 }
545}
546
547/**
548 * xmlSprintfElementContent:
549 * @buf: an output buffer
550 * @content: An element table
551 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
552 *
553 * This will dump the content of the element content definition
554 * Intended just for the debug routine
555 */
556void
557xmlSprintfElementContent(char *buf, xmlElementContentPtr content, int glob) {
558 if (content == NULL) return;
559 if (glob) strcat(buf, "(");
560 switch (content->type) {
561 case XML_ELEMENT_CONTENT_PCDATA:
562 strcat(buf, "#PCDATA");
563 break;
564 case XML_ELEMENT_CONTENT_ELEMENT:
565 strcat(buf, (char *) content->name);
566 break;
567 case XML_ELEMENT_CONTENT_SEQ:
568 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
569 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
570 xmlSprintfElementContent(buf, content->c1, 1);
571 else
572 xmlSprintfElementContent(buf, content->c1, 0);
573 strcat(buf, " , ");
574 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
575 xmlSprintfElementContent(buf, content->c2, 1);
576 else
577 xmlSprintfElementContent(buf, content->c2, 0);
578 break;
579 case XML_ELEMENT_CONTENT_OR:
580 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
581 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
582 xmlSprintfElementContent(buf, content->c1, 1);
583 else
584 xmlSprintfElementContent(buf, content->c1, 0);
585 strcat(buf, " | ");
586 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
587 xmlSprintfElementContent(buf, content->c2, 1);
588 else
589 xmlSprintfElementContent(buf, content->c2, 0);
590 break;
591 }
592 if (glob)
593 strcat(buf, ")");
594 switch (content->ocur) {
595 case XML_ELEMENT_CONTENT_ONCE:
596 break;
597 case XML_ELEMENT_CONTENT_OPT:
598 strcat(buf, "?");
599 break;
600 case XML_ELEMENT_CONTENT_MULT:
601 strcat(buf, "*");
602 break;
603 case XML_ELEMENT_CONTENT_PLUS:
604 strcat(buf, "+");
605 break;
606 }
607}
608
609/****************************************************************
610 * *
611 * Registration of DTD declarations *
612 * *
613 ****************************************************************/
614
615/**
616 * xmlCreateElementTable:
617 *
618 * create and initialize an empty element hash table.
619 *
620 * Returns the xmlElementTablePtr just created or NULL in case of error.
621 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000622static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000623xmlCreateElementTable(void) {
624 return(xmlHashCreate(0));
625}
626
627/**
628 * xmlFreeElement:
629 * @elem: An element
630 *
631 * Deallocate the memory used by an element definition
632 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000633static void
Owen Taylor3473f882001-02-23 17:55:21 +0000634xmlFreeElement(xmlElementPtr elem) {
635 if (elem == NULL) return;
636 xmlUnlinkNode((xmlNodePtr) elem);
637 xmlFreeElementContent(elem->content);
638 if (elem->name != NULL)
639 xmlFree((xmlChar *) elem->name);
640 if (elem->prefix != NULL)
641 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000642 xmlFree(elem);
643}
644
645
646/**
647 * xmlAddElementDecl:
648 * @ctxt: the validation context
649 * @dtd: pointer to the DTD
650 * @name: the entity name
651 * @type: the element type
652 * @content: the element content tree or NULL
653 *
654 * Register a new element declaration
655 *
656 * Returns NULL if not, othervise the entity
657 */
658xmlElementPtr
659xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
660 xmlElementTypeVal type,
661 xmlElementContentPtr content) {
662 xmlElementPtr ret;
663 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000664 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000665 xmlChar *ns, *uqname;
666
667 if (dtd == NULL) {
668 xmlGenericError(xmlGenericErrorContext,
669 "xmlAddElementDecl: dtd == NULL\n");
670 return(NULL);
671 }
672 if (name == NULL) {
673 xmlGenericError(xmlGenericErrorContext,
674 "xmlAddElementDecl: name == NULL\n");
675 return(NULL);
676 }
677 switch (type) {
678 case XML_ELEMENT_TYPE_EMPTY:
679 if (content != NULL) {
680 xmlGenericError(xmlGenericErrorContext,
681 "xmlAddElementDecl: content != NULL for EMPTY\n");
682 return(NULL);
683 }
684 break;
685 case XML_ELEMENT_TYPE_ANY:
686 if (content != NULL) {
687 xmlGenericError(xmlGenericErrorContext,
688 "xmlAddElementDecl: content != NULL for ANY\n");
689 return(NULL);
690 }
691 break;
692 case XML_ELEMENT_TYPE_MIXED:
693 if (content == NULL) {
694 xmlGenericError(xmlGenericErrorContext,
695 "xmlAddElementDecl: content == NULL for MIXED\n");
696 return(NULL);
697 }
698 break;
699 case XML_ELEMENT_TYPE_ELEMENT:
700 if (content == NULL) {
701 xmlGenericError(xmlGenericErrorContext,
702 "xmlAddElementDecl: content == NULL for ELEMENT\n");
703 return(NULL);
704 }
705 break;
706 default:
707 xmlGenericError(xmlGenericErrorContext,
708 "xmlAddElementDecl: unknown type %d\n", type);
709 return(NULL);
710 }
711
712 /*
713 * check if name is a QName
714 */
715 uqname = xmlSplitQName2(name, &ns);
716 if (uqname != NULL)
717 name = uqname;
718
719 /*
720 * Create the Element table if needed.
721 */
722 table = (xmlElementTablePtr) dtd->elements;
723 if (table == NULL) {
724 table = xmlCreateElementTable();
725 dtd->elements = (void *) table;
726 }
727 if (table == NULL) {
728 xmlGenericError(xmlGenericErrorContext,
729 "xmlAddElementDecl: Table creation failed!\n");
730 return(NULL);
731 }
732
Daniel Veillarda10efa82001-04-18 13:09:01 +0000733 /*
734 * lookup old attributes inserted on an undefined element in the
735 * internal subset.
736 */
737 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
738 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
739 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
740 oldAttributes = ret->attributes;
741 ret->attributes = NULL;
742 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
743 xmlFreeElement(ret);
744 }
Owen Taylor3473f882001-02-23 17:55:21 +0000745 }
Owen Taylor3473f882001-02-23 17:55:21 +0000746
747 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000748 * The element may already be present if one of its attribute
749 * was registered first
750 */
751 ret = xmlHashLookup2(table, name, ns);
752 if (ret != NULL) {
753 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
754 /*
755 * The element is already defined in this Dtd.
756 */
757 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
758 if (uqname != NULL)
759 xmlFree(uqname);
760 return(NULL);
761 }
762 } else {
763 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
764 if (ret == NULL) {
765 xmlGenericError(xmlGenericErrorContext,
766 "xmlAddElementDecl: out of memory\n");
767 return(NULL);
768 }
769 memset(ret, 0, sizeof(xmlElement));
770 ret->type = XML_ELEMENT_DECL;
771
772 /*
773 * fill the structure.
774 */
775 ret->name = xmlStrdup(name);
776 ret->prefix = ns;
777
778 /*
779 * Validity Check:
780 * Insertion must not fail
781 */
782 if (xmlHashAddEntry2(table, name, ns, ret)) {
783 /*
784 * The element is already defined in this Dtd.
785 */
786 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
787 xmlFreeElement(ret);
788 if (uqname != NULL)
789 xmlFree(uqname);
790 return(NULL);
791 }
792 }
793
794 /*
795 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000796 */
797 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000798 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000799 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000800
801 /*
802 * Link it to the Dtd
803 */
804 ret->parent = dtd;
805 ret->doc = dtd->doc;
806 if (dtd->last == NULL) {
807 dtd->children = dtd->last = (xmlNodePtr) ret;
808 } else {
809 dtd->last->next = (xmlNodePtr) ret;
810 ret->prev = dtd->last;
811 dtd->last = (xmlNodePtr) ret;
812 }
813 if (uqname != NULL)
814 xmlFree(uqname);
815 return(ret);
816}
817
818/**
819 * xmlFreeElementTable:
820 * @table: An element table
821 *
822 * Deallocate the memory used by an element hash table.
823 */
824void
825xmlFreeElementTable(xmlElementTablePtr table) {
826 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
827}
828
829/**
830 * xmlCopyElement:
831 * @elem: An element
832 *
833 * Build a copy of an element.
834 *
835 * Returns the new xmlElementPtr or NULL in case of error.
836 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000837static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000838xmlCopyElement(xmlElementPtr elem) {
839 xmlElementPtr cur;
840
841 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
842 if (cur == NULL) {
843 xmlGenericError(xmlGenericErrorContext,
844 "xmlCopyElement: out of memory !\n");
845 return(NULL);
846 }
847 memset(cur, 0, sizeof(xmlElement));
848 cur->type = XML_ELEMENT_DECL;
849 cur->etype = elem->etype;
850 if (elem->name != NULL)
851 cur->name = xmlStrdup(elem->name);
852 else
853 cur->name = NULL;
854 if (elem->prefix != NULL)
855 cur->prefix = xmlStrdup(elem->prefix);
856 else
857 cur->prefix = NULL;
858 cur->content = xmlCopyElementContent(elem->content);
859 /* TODO : rebuild the attribute list on the copy */
860 cur->attributes = NULL;
861 return(cur);
862}
863
864/**
865 * xmlCopyElementTable:
866 * @table: An element table
867 *
868 * Build a copy of an element table.
869 *
870 * Returns the new xmlElementTablePtr or NULL in case of error.
871 */
872xmlElementTablePtr
873xmlCopyElementTable(xmlElementTablePtr table) {
874 return((xmlElementTablePtr) xmlHashCopy(table,
875 (xmlHashCopier) xmlCopyElement));
876}
877
878/**
879 * xmlDumpElementDecl:
880 * @buf: the XML buffer output
881 * @elem: An element table
882 *
883 * This will dump the content of the element declaration as an XML
884 * DTD definition
885 */
886void
887xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
888 switch (elem->etype) {
889 case XML_ELEMENT_TYPE_EMPTY:
890 xmlBufferWriteChar(buf, "<!ELEMENT ");
891 xmlBufferWriteCHAR(buf, elem->name);
892 xmlBufferWriteChar(buf, " EMPTY>\n");
893 break;
894 case XML_ELEMENT_TYPE_ANY:
895 xmlBufferWriteChar(buf, "<!ELEMENT ");
896 xmlBufferWriteCHAR(buf, elem->name);
897 xmlBufferWriteChar(buf, " ANY>\n");
898 break;
899 case XML_ELEMENT_TYPE_MIXED:
900 xmlBufferWriteChar(buf, "<!ELEMENT ");
901 xmlBufferWriteCHAR(buf, elem->name);
902 xmlBufferWriteChar(buf, " ");
903 xmlDumpElementContent(buf, elem->content, 1);
904 xmlBufferWriteChar(buf, ">\n");
905 break;
906 case XML_ELEMENT_TYPE_ELEMENT:
907 xmlBufferWriteChar(buf, "<!ELEMENT ");
908 xmlBufferWriteCHAR(buf, elem->name);
909 xmlBufferWriteChar(buf, " ");
910 xmlDumpElementContent(buf, elem->content, 1);
911 xmlBufferWriteChar(buf, ">\n");
912 break;
913 default:
914 xmlGenericError(xmlGenericErrorContext,
915 "xmlDumpElementDecl: internal: unknown type %d\n",
916 elem->etype);
917 }
918}
919
920/**
921 * xmlDumpElementTable:
922 * @buf: the XML buffer output
923 * @table: An element table
924 *
925 * This will dump the content of the element table as an XML DTD definition
926 */
927void
928xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
929 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
930}
931
932/**
933 * xmlCreateEnumeration:
934 * @name: the enumeration name or NULL
935 *
936 * create and initialize an enumeration attribute node.
937 *
938 * Returns the xmlEnumerationPtr just created or NULL in case
939 * of error.
940 */
941xmlEnumerationPtr
942xmlCreateEnumeration(xmlChar *name) {
943 xmlEnumerationPtr ret;
944
945 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
946 if (ret == NULL) {
947 xmlGenericError(xmlGenericErrorContext,
948 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
949 (long)sizeof(xmlEnumeration));
950 return(NULL);
951 }
952 memset(ret, 0, sizeof(xmlEnumeration));
953
954 if (name != NULL)
955 ret->name = xmlStrdup(name);
956 return(ret);
957}
958
959/**
960 * xmlFreeEnumeration:
961 * @cur: the tree to free.
962 *
963 * free an enumeration attribute node (recursive).
964 */
965void
966xmlFreeEnumeration(xmlEnumerationPtr cur) {
967 if (cur == NULL) return;
968
969 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
970
971 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000972 xmlFree(cur);
973}
974
975/**
976 * xmlCopyEnumeration:
977 * @cur: the tree to copy.
978 *
979 * Copy an enumeration attribute node (recursive).
980 *
981 * Returns the xmlEnumerationPtr just created or NULL in case
982 * of error.
983 */
984xmlEnumerationPtr
985xmlCopyEnumeration(xmlEnumerationPtr cur) {
986 xmlEnumerationPtr ret;
987
988 if (cur == NULL) return(NULL);
989 ret = xmlCreateEnumeration((xmlChar *) cur->name);
990
991 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
992 else ret->next = NULL;
993
994 return(ret);
995}
996
997/**
998 * xmlDumpEnumeration:
999 * @buf: the XML buffer output
1000 * @enum: An enumeration
1001 *
1002 * This will dump the content of the enumeration
1003 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001004static void
Owen Taylor3473f882001-02-23 17:55:21 +00001005xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1006 if (cur == NULL) return;
1007
1008 xmlBufferWriteCHAR(buf, cur->name);
1009 if (cur->next == NULL)
1010 xmlBufferWriteChar(buf, ")");
1011 else {
1012 xmlBufferWriteChar(buf, " | ");
1013 xmlDumpEnumeration(buf, cur->next);
1014 }
1015}
1016
1017/**
1018 * xmlCreateAttributeTable:
1019 *
1020 * create and initialize an empty attribute hash table.
1021 *
1022 * Returns the xmlAttributeTablePtr just created or NULL in case
1023 * of error.
1024 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001025static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001026xmlCreateAttributeTable(void) {
1027 return(xmlHashCreate(0));
1028}
1029
1030/**
1031 * xmlScanAttributeDeclCallback:
1032 * @attr: the attribute decl
1033 * @list: the list to update
1034 *
1035 * Callback called by xmlScanAttributeDecl when a new attribute
1036 * has to be entered in the list.
1037 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001038static void
Owen Taylor3473f882001-02-23 17:55:21 +00001039xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001040 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001041 attr->nexth = *list;
1042 *list = attr;
1043}
1044
1045/**
1046 * xmlScanAttributeDecl:
1047 * @dtd: pointer to the DTD
1048 * @elem: the element name
1049 *
1050 * When inserting a new element scan the DtD for existing attributes
1051 * for taht element and initialize the Attribute chain
1052 *
1053 * Returns the pointer to the first attribute decl in the chain,
1054 * possibly NULL.
1055 */
1056xmlAttributePtr
1057xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1058 xmlAttributePtr ret = NULL;
1059 xmlAttributeTablePtr table;
1060
1061 if (dtd == NULL) {
1062 xmlGenericError(xmlGenericErrorContext,
1063 "xmlScanAttributeDecl: dtd == NULL\n");
1064 return(NULL);
1065 }
1066 if (elem == NULL) {
1067 xmlGenericError(xmlGenericErrorContext,
1068 "xmlScanAttributeDecl: elem == NULL\n");
1069 return(NULL);
1070 }
1071 table = (xmlAttributeTablePtr) dtd->attributes;
1072 if (table == NULL)
1073 return(NULL);
1074
1075 /* WRONG !!! */
1076 xmlHashScan3(table, NULL, NULL, elem,
1077 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1078 return(ret);
1079}
1080
1081/**
1082 * xmlScanIDAttributeDecl:
1083 * @ctxt: the validation context
1084 * @elem: the element name
1085 *
1086 * Verify that the element don't have too many ID attributes
1087 * declared.
1088 *
1089 * Returns the number of ID attributes found.
1090 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001091static int
Owen Taylor3473f882001-02-23 17:55:21 +00001092xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1093 xmlAttributePtr cur;
1094 int ret = 0;
1095
1096 if (elem == NULL) return(0);
1097 cur = elem->attributes;
1098 while (cur != NULL) {
1099 if (cur->atype == XML_ATTRIBUTE_ID) {
1100 ret ++;
1101 if (ret > 1)
1102 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001103 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001104 elem->name, cur->name);
1105 }
1106 cur = cur->nexth;
1107 }
1108 return(ret);
1109}
1110
1111/**
1112 * xmlFreeAttribute:
1113 * @elem: An attribute
1114 *
1115 * Deallocate the memory used by an attribute definition
1116 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001117static void
Owen Taylor3473f882001-02-23 17:55:21 +00001118xmlFreeAttribute(xmlAttributePtr attr) {
1119 if (attr == NULL) return;
1120 xmlUnlinkNode((xmlNodePtr) attr);
1121 if (attr->tree != NULL)
1122 xmlFreeEnumeration(attr->tree);
1123 if (attr->elem != NULL)
1124 xmlFree((xmlChar *) attr->elem);
1125 if (attr->name != NULL)
1126 xmlFree((xmlChar *) attr->name);
1127 if (attr->defaultValue != NULL)
1128 xmlFree((xmlChar *) attr->defaultValue);
1129 if (attr->prefix != NULL)
1130 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001131 xmlFree(attr);
1132}
1133
1134
1135/**
1136 * xmlAddAttributeDecl:
1137 * @ctxt: the validation context
1138 * @dtd: pointer to the DTD
1139 * @elem: the element name
1140 * @name: the attribute name
1141 * @ns: the attribute namespace prefix
1142 * @type: the attribute type
1143 * @def: the attribute default type
1144 * @defaultValue: the attribute default value
1145 * @tree: if it's an enumeration, the associated list
1146 *
1147 * Register a new attribute declaration
1148 * Note that @tree becomes the ownership of the DTD
1149 *
1150 * Returns NULL if not new, othervise the attribute decl
1151 */
1152xmlAttributePtr
1153xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1154 const xmlChar *name, const xmlChar *ns,
1155 xmlAttributeType type, xmlAttributeDefault def,
1156 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1157 xmlAttributePtr ret;
1158 xmlAttributeTablePtr table;
1159 xmlElementPtr elemDef;
1160
1161 if (dtd == NULL) {
1162 xmlGenericError(xmlGenericErrorContext,
1163 "xmlAddAttributeDecl: dtd == NULL\n");
1164 xmlFreeEnumeration(tree);
1165 return(NULL);
1166 }
1167 if (name == NULL) {
1168 xmlGenericError(xmlGenericErrorContext,
1169 "xmlAddAttributeDecl: name == NULL\n");
1170 xmlFreeEnumeration(tree);
1171 return(NULL);
1172 }
1173 if (elem == NULL) {
1174 xmlGenericError(xmlGenericErrorContext,
1175 "xmlAddAttributeDecl: elem == NULL\n");
1176 xmlFreeEnumeration(tree);
1177 return(NULL);
1178 }
1179 /*
1180 * Check the type and possibly the default value.
1181 */
1182 switch (type) {
1183 case XML_ATTRIBUTE_CDATA:
1184 break;
1185 case XML_ATTRIBUTE_ID:
1186 break;
1187 case XML_ATTRIBUTE_IDREF:
1188 break;
1189 case XML_ATTRIBUTE_IDREFS:
1190 break;
1191 case XML_ATTRIBUTE_ENTITY:
1192 break;
1193 case XML_ATTRIBUTE_ENTITIES:
1194 break;
1195 case XML_ATTRIBUTE_NMTOKEN:
1196 break;
1197 case XML_ATTRIBUTE_NMTOKENS:
1198 break;
1199 case XML_ATTRIBUTE_ENUMERATION:
1200 break;
1201 case XML_ATTRIBUTE_NOTATION:
1202 break;
1203 default:
1204 xmlGenericError(xmlGenericErrorContext,
1205 "xmlAddAttributeDecl: unknown type %d\n", type);
1206 xmlFreeEnumeration(tree);
1207 return(NULL);
1208 }
1209 if ((defaultValue != NULL) &&
1210 (!xmlValidateAttributeValue(type, defaultValue))) {
1211 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1212 elem, name, defaultValue);
1213 defaultValue = NULL;
1214 }
1215
1216 /*
1217 * Create the Attribute table if needed.
1218 */
1219 table = (xmlAttributeTablePtr) dtd->attributes;
1220 if (table == NULL) {
1221 table = xmlCreateAttributeTable();
1222 dtd->attributes = (void *) table;
1223 }
1224 if (table == NULL) {
1225 xmlGenericError(xmlGenericErrorContext,
1226 "xmlAddAttributeDecl: Table creation failed!\n");
1227 return(NULL);
1228 }
1229
1230
1231 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1232 if (ret == NULL) {
1233 xmlGenericError(xmlGenericErrorContext,
1234 "xmlAddAttributeDecl: out of memory\n");
1235 return(NULL);
1236 }
1237 memset(ret, 0, sizeof(xmlAttribute));
1238 ret->type = XML_ATTRIBUTE_DECL;
1239
1240 /*
1241 * fill the structure.
1242 */
1243 ret->atype = type;
1244 ret->name = xmlStrdup(name);
1245 ret->prefix = xmlStrdup(ns);
1246 ret->elem = xmlStrdup(elem);
1247 ret->def = def;
1248 ret->tree = tree;
1249 if (defaultValue != NULL)
1250 ret->defaultValue = xmlStrdup(defaultValue);
1251
1252 /*
1253 * Validity Check:
1254 * Search the DTD for previous declarations of the ATTLIST
1255 */
1256 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1257 /*
1258 * The attribute is already defined in this Dtd.
1259 */
1260 VWARNING(ctxt->userData,
1261 "Attribute %s on %s: already defined\n",
1262 name, elem);
1263 xmlFreeAttribute(ret);
1264 return(NULL);
1265 }
1266
1267 /*
1268 * Validity Check:
1269 * Multiple ID per element
1270 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001271 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001272 if (elemDef != NULL) {
1273 if ((type == XML_ATTRIBUTE_ID) &&
1274 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1275 VERROR(ctxt->userData,
1276 "Element %s has too may ID attributes defined : %s\n",
1277 elem, name);
1278 ret->nexth = elemDef->attributes;
1279 elemDef->attributes = ret;
1280 }
1281
1282 /*
1283 * Link it to the Dtd
1284 */
1285 ret->parent = dtd;
1286 ret->doc = dtd->doc;
1287 if (dtd->last == NULL) {
1288 dtd->children = dtd->last = (xmlNodePtr) ret;
1289 } else {
1290 dtd->last->next = (xmlNodePtr) ret;
1291 ret->prev = dtd->last;
1292 dtd->last = (xmlNodePtr) ret;
1293 }
1294 return(ret);
1295}
1296
1297/**
1298 * xmlFreeAttributeTable:
1299 * @table: An attribute table
1300 *
1301 * Deallocate the memory used by an entities hash table.
1302 */
1303void
1304xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1305 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1306}
1307
1308/**
1309 * xmlCopyAttribute:
1310 * @attr: An attribute
1311 *
1312 * Build a copy of an attribute.
1313 *
1314 * Returns the new xmlAttributePtr or NULL in case of error.
1315 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001316static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001317xmlCopyAttribute(xmlAttributePtr attr) {
1318 xmlAttributePtr cur;
1319
1320 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1321 if (cur == NULL) {
1322 xmlGenericError(xmlGenericErrorContext,
1323 "xmlCopyAttribute: out of memory !\n");
1324 return(NULL);
1325 }
1326 memset(cur, 0, sizeof(xmlAttribute));
1327 cur->atype = attr->atype;
1328 cur->def = attr->def;
1329 cur->tree = xmlCopyEnumeration(attr->tree);
1330 if (attr->elem != NULL)
1331 cur->elem = xmlStrdup(attr->elem);
1332 if (attr->name != NULL)
1333 cur->name = xmlStrdup(attr->name);
1334 if (attr->defaultValue != NULL)
1335 cur->defaultValue = xmlStrdup(attr->defaultValue);
1336 return(cur);
1337}
1338
1339/**
1340 * xmlCopyAttributeTable:
1341 * @table: An attribute table
1342 *
1343 * Build a copy of an attribute table.
1344 *
1345 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1346 */
1347xmlAttributeTablePtr
1348xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1349 return((xmlAttributeTablePtr) xmlHashCopy(table,
1350 (xmlHashCopier) xmlCopyAttribute));
1351}
1352
1353/**
1354 * xmlDumpAttributeDecl:
1355 * @buf: the XML buffer output
1356 * @attr: An attribute declaration
1357 *
1358 * This will dump the content of the attribute declaration as an XML
1359 * DTD definition
1360 */
1361void
1362xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1363 xmlBufferWriteChar(buf, "<!ATTLIST ");
1364 xmlBufferWriteCHAR(buf, attr->elem);
1365 xmlBufferWriteChar(buf, " ");
1366 if (attr->prefix != NULL) {
1367 xmlBufferWriteCHAR(buf, attr->prefix);
1368 xmlBufferWriteChar(buf, ":");
1369 }
1370 xmlBufferWriteCHAR(buf, attr->name);
1371 switch (attr->atype) {
1372 case XML_ATTRIBUTE_CDATA:
1373 xmlBufferWriteChar(buf, " CDATA");
1374 break;
1375 case XML_ATTRIBUTE_ID:
1376 xmlBufferWriteChar(buf, " ID");
1377 break;
1378 case XML_ATTRIBUTE_IDREF:
1379 xmlBufferWriteChar(buf, " IDREF");
1380 break;
1381 case XML_ATTRIBUTE_IDREFS:
1382 xmlBufferWriteChar(buf, " IDREFS");
1383 break;
1384 case XML_ATTRIBUTE_ENTITY:
1385 xmlBufferWriteChar(buf, " ENTITY");
1386 break;
1387 case XML_ATTRIBUTE_ENTITIES:
1388 xmlBufferWriteChar(buf, " ENTITIES");
1389 break;
1390 case XML_ATTRIBUTE_NMTOKEN:
1391 xmlBufferWriteChar(buf, " NMTOKEN");
1392 break;
1393 case XML_ATTRIBUTE_NMTOKENS:
1394 xmlBufferWriteChar(buf, " NMTOKENS");
1395 break;
1396 case XML_ATTRIBUTE_ENUMERATION:
1397 xmlBufferWriteChar(buf, " (");
1398 xmlDumpEnumeration(buf, attr->tree);
1399 break;
1400 case XML_ATTRIBUTE_NOTATION:
1401 xmlBufferWriteChar(buf, " NOTATION (");
1402 xmlDumpEnumeration(buf, attr->tree);
1403 break;
1404 default:
1405 xmlGenericError(xmlGenericErrorContext,
1406 "xmlDumpAttributeTable: internal: unknown type %d\n",
1407 attr->atype);
1408 }
1409 switch (attr->def) {
1410 case XML_ATTRIBUTE_NONE:
1411 break;
1412 case XML_ATTRIBUTE_REQUIRED:
1413 xmlBufferWriteChar(buf, " #REQUIRED");
1414 break;
1415 case XML_ATTRIBUTE_IMPLIED:
1416 xmlBufferWriteChar(buf, " #IMPLIED");
1417 break;
1418 case XML_ATTRIBUTE_FIXED:
1419 xmlBufferWriteChar(buf, " #FIXED");
1420 break;
1421 default:
1422 xmlGenericError(xmlGenericErrorContext,
1423 "xmlDumpAttributeTable: internal: unknown default %d\n",
1424 attr->def);
1425 }
1426 if (attr->defaultValue != NULL) {
1427 xmlBufferWriteChar(buf, " ");
1428 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1429 }
1430 xmlBufferWriteChar(buf, ">\n");
1431}
1432
1433/**
1434 * xmlDumpAttributeTable:
1435 * @buf: the XML buffer output
1436 * @table: An attribute table
1437 *
1438 * This will dump the content of the attribute table as an XML DTD definition
1439 */
1440void
1441xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1442 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1443}
1444
1445/************************************************************************
1446 * *
1447 * NOTATIONs *
1448 * *
1449 ************************************************************************/
1450/**
1451 * xmlCreateNotationTable:
1452 *
1453 * create and initialize an empty notation hash table.
1454 *
1455 * Returns the xmlNotationTablePtr just created or NULL in case
1456 * of error.
1457 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001458static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001459xmlCreateNotationTable(void) {
1460 return(xmlHashCreate(0));
1461}
1462
1463/**
1464 * xmlFreeNotation:
1465 * @not: A notation
1466 *
1467 * Deallocate the memory used by an notation definition
1468 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001469static void
Owen Taylor3473f882001-02-23 17:55:21 +00001470xmlFreeNotation(xmlNotationPtr nota) {
1471 if (nota == NULL) return;
1472 if (nota->name != NULL)
1473 xmlFree((xmlChar *) nota->name);
1474 if (nota->PublicID != NULL)
1475 xmlFree((xmlChar *) nota->PublicID);
1476 if (nota->SystemID != NULL)
1477 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001478 xmlFree(nota);
1479}
1480
1481
1482/**
1483 * xmlAddNotationDecl:
1484 * @dtd: pointer to the DTD
1485 * @ctxt: the validation context
1486 * @name: the entity name
1487 * @PublicID: the public identifier or NULL
1488 * @SystemID: the system identifier or NULL
1489 *
1490 * Register a new notation declaration
1491 *
1492 * Returns NULL if not, othervise the entity
1493 */
1494xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001495xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001496 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001497 const xmlChar *PublicID, const xmlChar *SystemID) {
1498 xmlNotationPtr ret;
1499 xmlNotationTablePtr table;
1500
1501 if (dtd == NULL) {
1502 xmlGenericError(xmlGenericErrorContext,
1503 "xmlAddNotationDecl: dtd == NULL\n");
1504 return(NULL);
1505 }
1506 if (name == NULL) {
1507 xmlGenericError(xmlGenericErrorContext,
1508 "xmlAddNotationDecl: name == NULL\n");
1509 return(NULL);
1510 }
1511 if ((PublicID == NULL) && (SystemID == NULL)) {
1512 xmlGenericError(xmlGenericErrorContext,
1513 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1514 }
1515
1516 /*
1517 * Create the Notation table if needed.
1518 */
1519 table = (xmlNotationTablePtr) dtd->notations;
1520 if (table == NULL)
1521 dtd->notations = table = xmlCreateNotationTable();
1522 if (table == NULL) {
1523 xmlGenericError(xmlGenericErrorContext,
1524 "xmlAddNotationDecl: Table creation failed!\n");
1525 return(NULL);
1526 }
1527
1528 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1529 if (ret == NULL) {
1530 xmlGenericError(xmlGenericErrorContext,
1531 "xmlAddNotationDecl: out of memory\n");
1532 return(NULL);
1533 }
1534 memset(ret, 0, sizeof(xmlNotation));
1535
1536 /*
1537 * fill the structure.
1538 */
1539 ret->name = xmlStrdup(name);
1540 if (SystemID != NULL)
1541 ret->SystemID = xmlStrdup(SystemID);
1542 if (PublicID != NULL)
1543 ret->PublicID = xmlStrdup(PublicID);
1544
1545 /*
1546 * Validity Check:
1547 * Check the DTD for previous declarations of the ATTLIST
1548 */
1549 if (xmlHashAddEntry(table, name, ret)) {
1550 xmlGenericError(xmlGenericErrorContext,
1551 "xmlAddNotationDecl: %s already defined\n", name);
1552 xmlFreeNotation(ret);
1553 return(NULL);
1554 }
1555 return(ret);
1556}
1557
1558/**
1559 * xmlFreeNotationTable:
1560 * @table: An notation table
1561 *
1562 * Deallocate the memory used by an entities hash table.
1563 */
1564void
1565xmlFreeNotationTable(xmlNotationTablePtr table) {
1566 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1567}
1568
1569/**
1570 * xmlCopyNotation:
1571 * @nota: A notation
1572 *
1573 * Build a copy of a notation.
1574 *
1575 * Returns the new xmlNotationPtr or NULL in case of error.
1576 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001577static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001578xmlCopyNotation(xmlNotationPtr nota) {
1579 xmlNotationPtr cur;
1580
1581 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1582 if (cur == NULL) {
1583 xmlGenericError(xmlGenericErrorContext,
1584 "xmlCopyNotation: out of memory !\n");
1585 return(NULL);
1586 }
1587 if (nota->name != NULL)
1588 cur->name = xmlStrdup(nota->name);
1589 else
1590 cur->name = NULL;
1591 if (nota->PublicID != NULL)
1592 cur->PublicID = xmlStrdup(nota->PublicID);
1593 else
1594 cur->PublicID = NULL;
1595 if (nota->SystemID != NULL)
1596 cur->SystemID = xmlStrdup(nota->SystemID);
1597 else
1598 cur->SystemID = NULL;
1599 return(cur);
1600}
1601
1602/**
1603 * xmlCopyNotationTable:
1604 * @table: A notation table
1605 *
1606 * Build a copy of a notation table.
1607 *
1608 * Returns the new xmlNotationTablePtr or NULL in case of error.
1609 */
1610xmlNotationTablePtr
1611xmlCopyNotationTable(xmlNotationTablePtr table) {
1612 return((xmlNotationTablePtr) xmlHashCopy(table,
1613 (xmlHashCopier) xmlCopyNotation));
1614}
1615
1616/**
1617 * xmlDumpNotationDecl:
1618 * @buf: the XML buffer output
1619 * @nota: A notation declaration
1620 *
1621 * This will dump the content the notation declaration as an XML DTD definition
1622 */
1623void
1624xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1625 xmlBufferWriteChar(buf, "<!NOTATION ");
1626 xmlBufferWriteCHAR(buf, nota->name);
1627 if (nota->PublicID != NULL) {
1628 xmlBufferWriteChar(buf, " PUBLIC ");
1629 xmlBufferWriteQuotedString(buf, nota->PublicID);
1630 if (nota->SystemID != NULL) {
1631 xmlBufferWriteChar(buf, " ");
1632 xmlBufferWriteCHAR(buf, nota->SystemID);
1633 }
1634 } else {
1635 xmlBufferWriteChar(buf, " SYSTEM ");
1636 xmlBufferWriteCHAR(buf, nota->SystemID);
1637 }
1638 xmlBufferWriteChar(buf, " >\n");
1639}
1640
1641/**
1642 * xmlDumpNotationTable:
1643 * @buf: the XML buffer output
1644 * @table: A notation table
1645 *
1646 * This will dump the content of the notation table as an XML DTD definition
1647 */
1648void
1649xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1650 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1651}
1652
1653/************************************************************************
1654 * *
1655 * IDs *
1656 * *
1657 ************************************************************************/
1658/**
1659 * xmlCreateIDTable:
1660 *
1661 * create and initialize an empty id hash table.
1662 *
1663 * Returns the xmlIDTablePtr just created or NULL in case
1664 * of error.
1665 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001666static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001667xmlCreateIDTable(void) {
1668 return(xmlHashCreate(0));
1669}
1670
1671/**
1672 * xmlFreeID:
1673 * @not: A id
1674 *
1675 * Deallocate the memory used by an id definition
1676 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001677static void
Owen Taylor3473f882001-02-23 17:55:21 +00001678xmlFreeID(xmlIDPtr id) {
1679 if (id == NULL) return;
1680 if (id->value != NULL)
1681 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001682 xmlFree(id);
1683}
1684
1685/**
1686 * xmlAddID:
1687 * @ctxt: the validation context
1688 * @doc: pointer to the document
1689 * @value: the value name
1690 * @attr: the attribute holding the ID
1691 *
1692 * Register a new id declaration
1693 *
1694 * Returns NULL if not, othervise the new xmlIDPtr
1695 */
1696xmlIDPtr
1697xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1698 xmlAttrPtr attr) {
1699 xmlIDPtr ret;
1700 xmlIDTablePtr table;
1701
1702 if (doc == NULL) {
1703 xmlGenericError(xmlGenericErrorContext,
1704 "xmlAddIDDecl: doc == NULL\n");
1705 return(NULL);
1706 }
1707 if (value == NULL) {
1708 xmlGenericError(xmlGenericErrorContext,
1709 "xmlAddIDDecl: value == NULL\n");
1710 return(NULL);
1711 }
1712 if (attr == NULL) {
1713 xmlGenericError(xmlGenericErrorContext,
1714 "xmlAddIDDecl: attr == NULL\n");
1715 return(NULL);
1716 }
1717
1718 /*
1719 * Create the ID table if needed.
1720 */
1721 table = (xmlIDTablePtr) doc->ids;
1722 if (table == NULL)
1723 doc->ids = table = xmlCreateIDTable();
1724 if (table == NULL) {
1725 xmlGenericError(xmlGenericErrorContext,
1726 "xmlAddID: Table creation failed!\n");
1727 return(NULL);
1728 }
1729
1730 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1731 if (ret == NULL) {
1732 xmlGenericError(xmlGenericErrorContext,
1733 "xmlAddID: out of memory\n");
1734 return(NULL);
1735 }
1736
1737 /*
1738 * fill the structure.
1739 */
1740 ret->value = xmlStrdup(value);
1741 ret->attr = attr;
1742
1743 if (xmlHashAddEntry(table, value, ret) < 0) {
1744 /*
1745 * The id is already defined in this Dtd.
1746 */
1747 VERROR(ctxt->userData, "ID %s already defined\n", value);
1748 xmlFreeID(ret);
1749 return(NULL);
1750 }
1751 return(ret);
1752}
1753
1754/**
1755 * xmlFreeIDTable:
1756 * @table: An id table
1757 *
1758 * Deallocate the memory used by an ID hash table.
1759 */
1760void
1761xmlFreeIDTable(xmlIDTablePtr table) {
1762 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1763}
1764
1765/**
1766 * xmlIsID:
1767 * @doc: the document
1768 * @elem: the element carrying the attribute
1769 * @attr: the attribute
1770 *
1771 * Determine whether an attribute is of type ID. In case we have Dtd(s)
1772 * then this is simple, otherwise we use an heuristic: name ID (upper
1773 * or lowercase).
1774 *
1775 * Returns 0 or 1 depending on the lookup result
1776 */
1777int
1778xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1779 if (doc == NULL) return(0);
1780 if (attr == NULL) return(0);
1781 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1782 return(0);
1783 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1784 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1785 (xmlStrEqual(BAD_CAST "name", attr->name)))
1786 return(1);
1787 return(0);
1788 } else {
1789 xmlAttributePtr attrDecl;
1790
1791 if (elem == NULL) return(0);
1792 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1793 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1794 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1795 attr->name);
1796
1797 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1798 return(1);
1799 }
1800 return(0);
1801}
1802
1803/**
1804 * xmlRemoveID
1805 * @doc: the document
1806 * @attr: the attribute
1807 *
1808 * Remove the given attribute from the ID table maintained internally.
1809 *
1810 * Returns -1 if the lookup failed and 0 otherwise
1811 */
1812int
1813xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1814 xmlAttrPtr cur;
1815 xmlIDTablePtr table;
1816 xmlChar *ID;
1817
1818 if (doc == NULL) return(-1);
1819 if (attr == NULL) return(-1);
1820 table = (xmlIDTablePtr) doc->ids;
1821 if (table == NULL)
1822 return(-1);
1823
1824 if (attr == NULL)
1825 return(-1);
1826 ID = xmlNodeListGetString(doc, attr->children, 1);
1827 if (ID == NULL)
1828 return(-1);
1829 cur = xmlHashLookup(table, ID);
1830 if (cur != attr) {
1831 xmlFree(ID);
1832 return(-1);
1833 }
1834 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1835 xmlFree(ID);
1836 return(0);
1837}
1838
1839/**
1840 * xmlGetID:
1841 * @doc: pointer to the document
1842 * @ID: the ID value
1843 *
1844 * Search the attribute declaring the given ID
1845 *
1846 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1847 */
1848xmlAttrPtr
1849xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1850 xmlIDTablePtr table;
1851 xmlIDPtr id;
1852
1853 if (doc == NULL) {
1854 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1855 return(NULL);
1856 }
1857
1858 if (ID == NULL) {
1859 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1860 return(NULL);
1861 }
1862
1863 table = (xmlIDTablePtr) doc->ids;
1864 if (table == NULL)
1865 return(NULL);
1866
1867 id = xmlHashLookup(table, ID);
1868 if (id == NULL)
1869 return(NULL);
1870 return(id->attr);
1871}
1872
1873/************************************************************************
1874 * *
1875 * Refs *
1876 * *
1877 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001878typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001879{
1880 xmlListPtr l;
1881 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001882} xmlRemoveMemo;
1883
1884typedef xmlRemoveMemo *xmlRemoveMemoPtr;
1885
1886typedef struct xmlValidateMemo_t
1887{
1888 xmlValidCtxtPtr ctxt;
1889 const xmlChar *name;
1890} xmlValidateMemo;
1891
1892typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00001893
1894/**
1895 * xmlCreateRefTable:
1896 *
1897 * create and initialize an empty ref hash table.
1898 *
1899 * Returns the xmlRefTablePtr just created or NULL in case
1900 * of error.
1901 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001902static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001903xmlCreateRefTable(void) {
1904 return(xmlHashCreate(0));
1905}
1906
1907/**
1908 * xmlFreeRef:
1909 * @lk: A list link
1910 *
1911 * Deallocate the memory used by a ref definition
1912 */
1913static void
1914xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00001915 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
1916 if (ref == NULL) return;
1917 if (ref->value != NULL)
1918 xmlFree((xmlChar *)ref->value);
1919 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00001920}
1921
1922/**
1923 * xmlFreeRefList:
1924 * @list_ref: A list of references.
1925 *
1926 * Deallocate the memory used by a list of references
1927 */
1928static void
1929xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00001930 if (list_ref == NULL) return;
1931 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00001932}
1933
1934/**
1935 * xmlWalkRemoveRef:
1936 * @data: Contents of current link
1937 * @user: Value supplied by the user
1938 *
1939 * Return 0 to abort the walk or 1 to continue
1940 */
1941static int
1942xmlWalkRemoveRef(const void *data, const void *user)
1943{
Daniel Veillard37721922001-05-04 15:21:12 +00001944 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
1945 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
1946 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00001947
Daniel Veillard37721922001-05-04 15:21:12 +00001948 if (attr0 == attr1) { /* Matched: remove and terminate walk */
1949 xmlListRemoveFirst(ref_list, (void *)data);
1950 return 0;
1951 }
1952 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00001953}
1954
1955/**
1956 * xmlAddRef:
1957 * @ctxt: the validation context
1958 * @doc: pointer to the document
1959 * @value: the value name
1960 * @attr: the attribute holding the Ref
1961 *
1962 * Register a new ref declaration
1963 *
1964 * Returns NULL if not, othervise the new xmlRefPtr
1965 */
1966xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001967xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00001968 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00001969 xmlRefPtr ret;
1970 xmlRefTablePtr table;
1971 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00001972
Daniel Veillard37721922001-05-04 15:21:12 +00001973 if (doc == NULL) {
1974 xmlGenericError(xmlGenericErrorContext,
1975 "xmlAddRefDecl: doc == NULL\n");
1976 return(NULL);
1977 }
1978 if (value == NULL) {
1979 xmlGenericError(xmlGenericErrorContext,
1980 "xmlAddRefDecl: value == NULL\n");
1981 return(NULL);
1982 }
1983 if (attr == NULL) {
1984 xmlGenericError(xmlGenericErrorContext,
1985 "xmlAddRefDecl: attr == NULL\n");
1986 return(NULL);
1987 }
Owen Taylor3473f882001-02-23 17:55:21 +00001988
Daniel Veillard37721922001-05-04 15:21:12 +00001989 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001990 * Create the Ref table if needed.
1991 */
Daniel Veillard37721922001-05-04 15:21:12 +00001992 table = (xmlRefTablePtr) doc->refs;
1993 if (table == NULL)
1994 doc->refs = table = xmlCreateRefTable();
1995 if (table == NULL) {
1996 xmlGenericError(xmlGenericErrorContext,
1997 "xmlAddRef: Table creation failed!\n");
1998 return(NULL);
1999 }
Owen Taylor3473f882001-02-23 17:55:21 +00002000
Daniel Veillard37721922001-05-04 15:21:12 +00002001 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2002 if (ret == NULL) {
2003 xmlGenericError(xmlGenericErrorContext,
2004 "xmlAddRef: out of memory\n");
2005 return(NULL);
2006 }
Owen Taylor3473f882001-02-23 17:55:21 +00002007
Daniel Veillard37721922001-05-04 15:21:12 +00002008 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002009 * fill the structure.
2010 */
Daniel Veillard37721922001-05-04 15:21:12 +00002011 ret->value = xmlStrdup(value);
2012 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002013
Daniel Veillard37721922001-05-04 15:21:12 +00002014 /* To add a reference :-
2015 * References are maintained as a list of references,
2016 * Lookup the entry, if no entry create new nodelist
2017 * Add the owning node to the NodeList
2018 * Return the ref
2019 */
Owen Taylor3473f882001-02-23 17:55:21 +00002020
Daniel Veillard37721922001-05-04 15:21:12 +00002021 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2022 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2023 xmlGenericError(xmlGenericErrorContext,
2024 "xmlAddRef: Reference list creation failed!\n");
2025 return(NULL);
2026 }
2027 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2028 xmlListDelete(ref_list);
2029 xmlGenericError(xmlGenericErrorContext,
2030 "xmlAddRef: Reference list insertion failed!\n");
2031 return(NULL);
2032 }
2033 }
2034 xmlListInsert(ref_list, ret);
2035 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002036}
2037
2038/**
2039 * xmlFreeRefTable:
2040 * @table: An ref table
2041 *
2042 * Deallocate the memory used by an Ref hash table.
2043 */
2044void
2045xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002046 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002047}
2048
2049/**
2050 * xmlIsRef:
2051 * @doc: the document
2052 * @elem: the element carrying the attribute
2053 * @attr: the attribute
2054 *
2055 * Determine whether an attribute is of type Ref. In case we have Dtd(s)
2056 * then this is simple, otherwise we use an heuristic: name Ref (upper
2057 * or lowercase).
2058 *
2059 * Returns 0 or 1 depending on the lookup result
2060 */
2061int
2062xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002063 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2064 return(0);
2065 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2066 /* TODO @@@ */
2067 return(0);
2068 } else {
2069 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002070
Daniel Veillard37721922001-05-04 15:21:12 +00002071 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2072 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2073 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2074 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002075
Daniel Veillard37721922001-05-04 15:21:12 +00002076 if ((attrDecl != NULL) &&
2077 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2078 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2079 return(1);
2080 }
2081 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002082}
2083
2084/**
2085 * xmlRemoveRef
2086 * @doc: the document
2087 * @attr: the attribute
2088 *
2089 * Remove the given attribute from the Ref table maintained internally.
2090 *
2091 * Returns -1 if the lookup failed and 0 otherwise
2092 */
2093int
2094xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002095 xmlListPtr ref_list;
2096 xmlRefTablePtr table;
2097 xmlChar *ID;
2098 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002099
Daniel Veillard37721922001-05-04 15:21:12 +00002100 if (doc == NULL) return(-1);
2101 if (attr == NULL) return(-1);
2102 table = (xmlRefTablePtr) doc->refs;
2103 if (table == NULL)
2104 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002105
Daniel Veillard37721922001-05-04 15:21:12 +00002106 if (attr == NULL)
2107 return(-1);
2108 ID = xmlNodeListGetString(doc, attr->children, 1);
2109 if (ID == NULL)
2110 return(-1);
2111 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002112
Daniel Veillard37721922001-05-04 15:21:12 +00002113 if(ref_list == NULL) {
2114 xmlFree(ID);
2115 return (-1);
2116 }
2117 /* At this point, ref_list refers to a list of references which
2118 * have the same key as the supplied attr. Our list of references
2119 * is ordered by reference address and we don't have that information
2120 * here to use when removing. We'll have to walk the list and
2121 * check for a matching attribute, when we find one stop the walk
2122 * and remove the entry.
2123 * The list is ordered by reference, so that means we don't have the
2124 * key. Passing the list and the reference to the walker means we
2125 * will have enough data to be able to remove the entry.
2126 */
2127 target.l = ref_list;
2128 target.ap = attr;
2129
2130 /* Remove the supplied attr from our list */
2131 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002132
Daniel Veillard37721922001-05-04 15:21:12 +00002133 /*If the list is empty then remove the list entry in the hash */
2134 if (xmlListEmpty(ref_list))
2135 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2136 xmlFreeRefList);
2137 xmlFree(ID);
2138 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002139}
2140
2141/**
2142 * xmlGetRefs:
2143 * @doc: pointer to the document
2144 * @ID: the ID value
2145 *
2146 * Find the set of references for the supplied ID.
2147 *
2148 * Returns NULL if not found, otherwise node set for the ID.
2149 */
2150xmlListPtr
2151xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002152 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002153
Daniel Veillard37721922001-05-04 15:21:12 +00002154 if (doc == NULL) {
2155 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
2156 return(NULL);
2157 }
Owen Taylor3473f882001-02-23 17:55:21 +00002158
Daniel Veillard37721922001-05-04 15:21:12 +00002159 if (ID == NULL) {
2160 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: ID == NULL\n");
2161 return(NULL);
2162 }
Owen Taylor3473f882001-02-23 17:55:21 +00002163
Daniel Veillard37721922001-05-04 15:21:12 +00002164 table = (xmlRefTablePtr) doc->refs;
2165 if (table == NULL)
2166 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002167
Daniel Veillard37721922001-05-04 15:21:12 +00002168 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002169}
2170
2171/************************************************************************
2172 * *
2173 * Routines for validity checking *
2174 * *
2175 ************************************************************************/
2176
2177/**
2178 * xmlGetDtdElementDesc:
2179 * @dtd: a pointer to the DtD to search
2180 * @name: the element name
2181 *
2182 * Search the Dtd for the description of this element
2183 *
2184 * returns the xmlElementPtr if found or NULL
2185 */
2186
2187xmlElementPtr
2188xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2189 xmlElementTablePtr table;
2190 xmlElementPtr cur;
2191 xmlChar *uqname = NULL, *prefix = NULL;
2192
2193 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002194 if (dtd->elements == NULL)
2195 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002196 table = (xmlElementTablePtr) dtd->elements;
2197
2198 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002199 if (uqname != NULL)
2200 name = uqname;
2201 cur = xmlHashLookup2(table, name, prefix);
2202 if (prefix != NULL) xmlFree(prefix);
2203 if (uqname != NULL) xmlFree(uqname);
2204 return(cur);
2205}
2206/**
2207 * xmlGetDtdElementDesc2:
2208 * @dtd: a pointer to the DtD to search
2209 * @name: the element name
2210 * @create: create an empty description if not found
2211 *
2212 * Search the Dtd for the description of this element
2213 *
2214 * returns the xmlElementPtr if found or NULL
2215 */
2216
2217xmlElementPtr
2218xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2219 xmlElementTablePtr table;
2220 xmlElementPtr cur;
2221 xmlChar *uqname = NULL, *prefix = NULL;
2222
2223 if (dtd == NULL) return(NULL);
2224 if (dtd->elements == NULL) {
2225 if (!create)
2226 return(NULL);
2227 /*
2228 * Create the Element table if needed.
2229 */
2230 table = (xmlElementTablePtr) dtd->elements;
2231 if (table == NULL) {
2232 table = xmlCreateElementTable();
2233 dtd->elements = (void *) table;
2234 }
2235 if (table == NULL) {
2236 xmlGenericError(xmlGenericErrorContext,
2237 "xmlGetDtdElementDesc: Table creation failed!\n");
2238 return(NULL);
2239 }
2240 }
2241 table = (xmlElementTablePtr) dtd->elements;
2242
2243 uqname = xmlSplitQName2(name, &prefix);
2244 if (uqname != NULL)
2245 name = uqname;
2246 cur = xmlHashLookup2(table, name, prefix);
2247 if ((cur == NULL) && (create)) {
2248 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2249 if (cur == NULL) {
2250 xmlGenericError(xmlGenericErrorContext,
2251 "xmlGetDtdElementDesc: out of memory\n");
2252 return(NULL);
2253 }
2254 memset(cur, 0, sizeof(xmlElement));
2255 cur->type = XML_ELEMENT_DECL;
2256
2257 /*
2258 * fill the structure.
2259 */
2260 cur->name = xmlStrdup(name);
2261 cur->prefix = xmlStrdup(prefix);
2262 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2263
2264 xmlHashAddEntry2(table, name, prefix, cur);
2265 }
2266 if (prefix != NULL) xmlFree(prefix);
2267 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002268 return(cur);
2269}
2270
2271/**
2272 * xmlGetDtdQElementDesc:
2273 * @dtd: a pointer to the DtD to search
2274 * @name: the element name
2275 * @prefix: the element namespace prefix
2276 *
2277 * Search the Dtd for the description of this element
2278 *
2279 * returns the xmlElementPtr if found or NULL
2280 */
2281
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002282static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002283xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2284 const xmlChar *prefix) {
2285 xmlElementTablePtr table;
2286
2287 if (dtd == NULL) return(NULL);
2288 if (dtd->elements == NULL) return(NULL);
2289 table = (xmlElementTablePtr) dtd->elements;
2290
2291 return(xmlHashLookup2(table, name, prefix));
2292}
2293
2294/**
2295 * xmlGetDtdAttrDesc:
2296 * @dtd: a pointer to the DtD to search
2297 * @elem: the element name
2298 * @name: the attribute name
2299 *
2300 * Search the Dtd for the description of this attribute on
2301 * this element.
2302 *
2303 * returns the xmlAttributePtr if found or NULL
2304 */
2305
2306xmlAttributePtr
2307xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2308 xmlAttributeTablePtr table;
2309 xmlAttributePtr cur;
2310 xmlChar *uqname = NULL, *prefix = NULL;
2311
2312 if (dtd == NULL) return(NULL);
2313 if (dtd->attributes == NULL) return(NULL);
2314
2315 table = (xmlAttributeTablePtr) dtd->attributes;
2316 if (table == NULL)
2317 return(NULL);
2318
2319 uqname = xmlSplitQName2(name, &prefix);
2320
2321 if (uqname != NULL) {
2322 cur = xmlHashLookup3(table, uqname, prefix, elem);
2323 if (prefix != NULL) xmlFree(prefix);
2324 if (uqname != NULL) xmlFree(uqname);
2325 } else
2326 cur = xmlHashLookup3(table, name, NULL, elem);
2327 return(cur);
2328}
2329
2330/**
2331 * xmlGetDtdQAttrDesc:
2332 * @dtd: a pointer to the DtD to search
2333 * @elem: the element name
2334 * @name: the attribute name
2335 * @prefix: the attribute namespace prefix
2336 *
2337 * Search the Dtd for the description of this qualified attribute on
2338 * this element.
2339 *
2340 * returns the xmlAttributePtr if found or NULL
2341 */
2342
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002343static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002344xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2345 const xmlChar *prefix) {
2346 xmlAttributeTablePtr table;
2347
2348 if (dtd == NULL) return(NULL);
2349 if (dtd->attributes == NULL) return(NULL);
2350 table = (xmlAttributeTablePtr) dtd->attributes;
2351
2352 return(xmlHashLookup3(table, name, prefix, elem));
2353}
2354
2355/**
2356 * xmlGetDtdNotationDesc:
2357 * @dtd: a pointer to the DtD to search
2358 * @name: the notation name
2359 *
2360 * Search the Dtd for the description of this notation
2361 *
2362 * returns the xmlNotationPtr if found or NULL
2363 */
2364
2365xmlNotationPtr
2366xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2367 xmlNotationTablePtr table;
2368
2369 if (dtd == NULL) return(NULL);
2370 if (dtd->notations == NULL) return(NULL);
2371 table = (xmlNotationTablePtr) dtd->notations;
2372
2373 return(xmlHashLookup(table, name));
2374}
2375
2376/**
2377 * xmlValidateNotationUse:
2378 * @ctxt: the validation context
2379 * @doc: the document
2380 * @notationName: the notation name to check
2381 *
2382 * Validate that the given mame match a notation declaration.
2383 * - [ VC: Notation Declared ]
2384 *
2385 * returns 1 if valid or 0 otherwise
2386 */
2387
2388int
2389xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2390 const xmlChar *notationName) {
2391 xmlNotationPtr notaDecl;
2392 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2393
2394 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2395 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2396 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2397
2398 if (notaDecl == NULL) {
2399 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2400 notationName);
2401 return(0);
2402 }
2403 return(1);
2404}
2405
2406/**
2407 * xmlIsMixedElement
2408 * @doc: the document
2409 * @name: the element name
2410 *
2411 * Search in the DtDs whether an element accept Mixed content (or ANY)
2412 * basically if it is supposed to accept text childs
2413 *
2414 * returns 0 if no, 1 if yes, and -1 if no element description is available
2415 */
2416
2417int
2418xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2419 xmlElementPtr elemDecl;
2420
2421 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2422
2423 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2424 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2425 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2426 if (elemDecl == NULL) return(-1);
2427 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002428 case XML_ELEMENT_TYPE_UNDEFINED:
2429 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002430 case XML_ELEMENT_TYPE_ELEMENT:
2431 return(0);
2432 case XML_ELEMENT_TYPE_EMPTY:
2433 /*
2434 * return 1 for EMPTY since we want VC error to pop up
2435 * on <empty> </empty> for example
2436 */
2437 case XML_ELEMENT_TYPE_ANY:
2438 case XML_ELEMENT_TYPE_MIXED:
2439 return(1);
2440 }
2441 return(1);
2442}
2443
2444/**
2445 * xmlValidateNameValue:
2446 * @value: an Name value
2447 *
2448 * Validate that the given value match Name production
2449 *
2450 * returns 1 if valid or 0 otherwise
2451 */
2452
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002453static int
Owen Taylor3473f882001-02-23 17:55:21 +00002454xmlValidateNameValue(const xmlChar *value) {
2455 const xmlChar *cur;
2456
2457 if (value == NULL) return(0);
2458 cur = value;
2459
2460 if (!IS_LETTER(*cur) && (*cur != '_') &&
2461 (*cur != ':')) {
2462 return(0);
2463 }
2464
2465 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2466 (*cur == '.') || (*cur == '-') ||
2467 (*cur == '_') || (*cur == ':') ||
2468 (IS_COMBINING(*cur)) ||
2469 (IS_EXTENDER(*cur)))
2470 cur++;
2471
2472 if (*cur != 0) return(0);
2473
2474 return(1);
2475}
2476
2477/**
2478 * xmlValidateNamesValue:
2479 * @value: an Names value
2480 *
2481 * Validate that the given value match Names production
2482 *
2483 * returns 1 if valid or 0 otherwise
2484 */
2485
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002486static int
Owen Taylor3473f882001-02-23 17:55:21 +00002487xmlValidateNamesValue(const xmlChar *value) {
2488 const xmlChar *cur;
2489
2490 if (value == NULL) return(0);
2491 cur = value;
2492
2493 if (!IS_LETTER(*cur) && (*cur != '_') &&
2494 (*cur != ':')) {
2495 return(0);
2496 }
2497
2498 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2499 (*cur == '.') || (*cur == '-') ||
2500 (*cur == '_') || (*cur == ':') ||
2501 (IS_COMBINING(*cur)) ||
2502 (IS_EXTENDER(*cur)))
2503 cur++;
2504
2505 while (IS_BLANK(*cur)) {
2506 while (IS_BLANK(*cur)) cur++;
2507
2508 if (!IS_LETTER(*cur) && (*cur != '_') &&
2509 (*cur != ':')) {
2510 return(0);
2511 }
2512
2513 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2514 (*cur == '.') || (*cur == '-') ||
2515 (*cur == '_') || (*cur == ':') ||
2516 (IS_COMBINING(*cur)) ||
2517 (IS_EXTENDER(*cur)))
2518 cur++;
2519 }
2520
2521 if (*cur != 0) return(0);
2522
2523 return(1);
2524}
2525
2526/**
2527 * xmlValidateNmtokenValue:
2528 * @value: an Mntoken value
2529 *
2530 * Validate that the given value match Nmtoken production
2531 *
2532 * [ VC: Name Token ]
2533 *
2534 * returns 1 if valid or 0 otherwise
2535 */
2536
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002537static int
Owen Taylor3473f882001-02-23 17:55:21 +00002538xmlValidateNmtokenValue(const xmlChar *value) {
2539 const xmlChar *cur;
2540
2541 if (value == NULL) return(0);
2542 cur = value;
2543
2544 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2545 (*cur != '.') && (*cur != '-') &&
2546 (*cur != '_') && (*cur != ':') &&
2547 (!IS_COMBINING(*cur)) &&
2548 (!IS_EXTENDER(*cur)))
2549 return(0);
2550
2551 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2552 (*cur == '.') || (*cur == '-') ||
2553 (*cur == '_') || (*cur == ':') ||
2554 (IS_COMBINING(*cur)) ||
2555 (IS_EXTENDER(*cur)))
2556 cur++;
2557
2558 if (*cur != 0) return(0);
2559
2560 return(1);
2561}
2562
2563/**
2564 * xmlValidateNmtokensValue:
2565 * @value: an Mntokens value
2566 *
2567 * Validate that the given value match Nmtokens production
2568 *
2569 * [ VC: Name Token ]
2570 *
2571 * returns 1 if valid or 0 otherwise
2572 */
2573
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002574static int
Owen Taylor3473f882001-02-23 17:55:21 +00002575xmlValidateNmtokensValue(const xmlChar *value) {
2576 const xmlChar *cur;
2577
2578 if (value == NULL) return(0);
2579 cur = value;
2580
2581 while (IS_BLANK(*cur)) cur++;
2582 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2583 (*cur != '.') && (*cur != '-') &&
2584 (*cur != '_') && (*cur != ':') &&
2585 (!IS_COMBINING(*cur)) &&
2586 (!IS_EXTENDER(*cur)))
2587 return(0);
2588
2589 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2590 (*cur == '.') || (*cur == '-') ||
2591 (*cur == '_') || (*cur == ':') ||
2592 (IS_COMBINING(*cur)) ||
2593 (IS_EXTENDER(*cur)))
2594 cur++;
2595
2596 while (IS_BLANK(*cur)) {
2597 while (IS_BLANK(*cur)) cur++;
2598 if (*cur == 0) return(1);
2599
2600 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2601 (*cur != '.') && (*cur != '-') &&
2602 (*cur != '_') && (*cur != ':') &&
2603 (!IS_COMBINING(*cur)) &&
2604 (!IS_EXTENDER(*cur)))
2605 return(0);
2606
2607 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2608 (*cur == '.') || (*cur == '-') ||
2609 (*cur == '_') || (*cur == ':') ||
2610 (IS_COMBINING(*cur)) ||
2611 (IS_EXTENDER(*cur)))
2612 cur++;
2613 }
2614
2615 if (*cur != 0) return(0);
2616
2617 return(1);
2618}
2619
2620/**
2621 * xmlValidateNotationDecl:
2622 * @ctxt: the validation context
2623 * @doc: a document instance
2624 * @nota: a notation definition
2625 *
2626 * Try to validate a single notation definition
2627 * basically it does the following checks as described by the
2628 * XML-1.0 recommendation:
2629 * - it seems that no validity constraing exist on notation declarations
2630 * But this function get called anyway ...
2631 *
2632 * returns 1 if valid or 0 otherwise
2633 */
2634
2635int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002636xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2637 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002638 int ret = 1;
2639
2640 return(ret);
2641}
2642
2643/**
2644 * xmlValidateAttributeValue:
2645 * @type: an attribute type
2646 * @value: an attribute value
2647 *
2648 * Validate that the given attribute value match the proper production
2649 *
2650 * [ VC: ID ]
2651 * Values of type ID must match the Name production....
2652 *
2653 * [ VC: IDREF ]
2654 * Values of type IDREF must match the Name production, and values
2655 * of type IDREFS must match Names ...
2656 *
2657 * [ VC: Entity Name ]
2658 * Values of type ENTITY must match the Name production, values
2659 * of type ENTITIES must match Names ...
2660 *
2661 * [ VC: Name Token ]
2662 * Values of type NMTOKEN must match the Nmtoken production; values
2663 * of type NMTOKENS must match Nmtokens.
2664 *
2665 * returns 1 if valid or 0 otherwise
2666 */
2667
2668int
2669xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2670 switch (type) {
2671 case XML_ATTRIBUTE_ENTITIES:
2672 case XML_ATTRIBUTE_IDREFS:
2673 return(xmlValidateNamesValue(value));
2674 case XML_ATTRIBUTE_ENTITY:
2675 case XML_ATTRIBUTE_IDREF:
2676 case XML_ATTRIBUTE_ID:
2677 case XML_ATTRIBUTE_NOTATION:
2678 return(xmlValidateNameValue(value));
2679 case XML_ATTRIBUTE_NMTOKENS:
2680 case XML_ATTRIBUTE_ENUMERATION:
2681 return(xmlValidateNmtokensValue(value));
2682 case XML_ATTRIBUTE_NMTOKEN:
2683 return(xmlValidateNmtokenValue(value));
2684 case XML_ATTRIBUTE_CDATA:
2685 break;
2686 }
2687 return(1);
2688}
2689
2690/**
2691 * xmlValidateAttributeValue2:
2692 * @ctxt: the validation context
2693 * @doc: the document
2694 * @name: the attribute name (used for error reporting only)
2695 * @type: the attribute type
2696 * @value: the attribute value
2697 *
2698 * Validate that the given attribute value match a given type.
2699 * This typically cannot be done before having finished parsing
2700 * the subsets.
2701 *
2702 * [ VC: IDREF ]
2703 * Values of type IDREF must match one of the declared IDs
2704 * Values of type IDREFS must match a sequence of the declared IDs
2705 * each Name must match the value of an ID attribute on some element
2706 * in the XML document; i.e. IDREF values must match the value of
2707 * some ID attribute
2708 *
2709 * [ VC: Entity Name ]
2710 * Values of type ENTITY must match one declared entity
2711 * Values of type ENTITIES must match a sequence of declared entities
2712 *
2713 * [ VC: Notation Attributes ]
2714 * all notation names in the declaration must be declared.
2715 *
2716 * returns 1 if valid or 0 otherwise
2717 */
2718
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002719static int
Owen Taylor3473f882001-02-23 17:55:21 +00002720xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2721 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2722 int ret = 1;
2723 switch (type) {
2724 case XML_ATTRIBUTE_IDREFS:
2725 case XML_ATTRIBUTE_IDREF:
2726 case XML_ATTRIBUTE_ID:
2727 case XML_ATTRIBUTE_NMTOKENS:
2728 case XML_ATTRIBUTE_ENUMERATION:
2729 case XML_ATTRIBUTE_NMTOKEN:
2730 case XML_ATTRIBUTE_CDATA:
2731 break;
2732 case XML_ATTRIBUTE_ENTITY: {
2733 xmlEntityPtr ent;
2734
2735 ent = xmlGetDocEntity(doc, value);
2736 if (ent == NULL) {
2737 VERROR(ctxt->userData,
2738 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2739 name, value);
2740 ret = 0;
2741 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2742 VERROR(ctxt->userData,
2743 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2744 name, value);
2745 ret = 0;
2746 }
2747 break;
2748 }
2749 case XML_ATTRIBUTE_ENTITIES: {
2750 xmlChar *dup, *nam = NULL, *cur, save;
2751 xmlEntityPtr ent;
2752
2753 dup = xmlStrdup(value);
2754 if (dup == NULL)
2755 return(0);
2756 cur = dup;
2757 while (*cur != 0) {
2758 nam = cur;
2759 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2760 save = *cur;
2761 *cur = 0;
2762 ent = xmlGetDocEntity(doc, nam);
2763 if (ent == NULL) {
2764 VERROR(ctxt->userData,
2765 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2766 name, nam);
2767 ret = 0;
2768 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2769 VERROR(ctxt->userData,
2770 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2771 name, nam);
2772 ret = 0;
2773 }
2774 if (save == 0)
2775 break;
2776 *cur = save;
2777 while (IS_BLANK(*cur)) cur++;
2778 }
2779 xmlFree(dup);
2780 break;
2781 }
2782 case XML_ATTRIBUTE_NOTATION: {
2783 xmlNotationPtr nota;
2784
2785 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2786 if ((nota == NULL) && (doc->extSubset != NULL))
2787 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2788
2789 if (nota == NULL) {
2790 VERROR(ctxt->userData,
2791 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2792 name, value);
2793 ret = 0;
2794 }
2795 break;
2796 }
2797 }
2798 return(ret);
2799}
2800
2801/**
2802 * xmlValidNormalizeAttributeValue:
2803 * @doc: the document
2804 * @elem: the parent
2805 * @name: the attribute name
2806 * @value: the attribute value
2807 *
2808 * Does the validation related extra step of the normalization of attribute
2809 * values:
2810 *
2811 * If the declared value is not CDATA, then the XML processor must further
2812 * process the normalized attribute value by discarding any leading and
2813 * trailing space (#x20) characters, and by replacing sequences of space
2814 * (#x20) characters by single space (#x20) character.
2815 *
2816 * returns a new normalized string if normalization is needed, NULL otherwise
2817 * the caller must free the returned value.
2818 */
2819
2820xmlChar *
2821xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2822 const xmlChar *name, const xmlChar *value) {
2823 xmlChar *ret, *dst;
2824 const xmlChar *src;
2825 xmlAttributePtr attrDecl = NULL;
2826
2827 if (doc == NULL) return(NULL);
2828 if (elem == NULL) return(NULL);
2829 if (name == NULL) return(NULL);
2830 if (value == NULL) return(NULL);
2831
2832 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2833 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002834 snprintf((char *) qname, sizeof(qname), "%s:%s",
2835 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002836 qname[sizeof(qname) - 1] = 0;
2837 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2838 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2839 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2840 }
2841 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2842 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2843 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2844
2845 if (attrDecl == NULL)
2846 return(NULL);
2847 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2848 return(NULL);
2849
2850 ret = xmlStrdup(value);
2851 if (ret == NULL)
2852 return(NULL);
2853 src = value;
2854 dst = ret;
2855 while (*src == 0x20) src++;
2856 while (*src != 0) {
2857 if (*src == 0x20) {
2858 while (*src == 0x20) src++;
2859 if (*src != 0)
2860 *dst++ = 0x20;
2861 } else {
2862 *dst++ = *src++;
2863 }
2864 }
2865 *dst = 0;
2866 return(ret);
2867}
2868
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002869static void
Owen Taylor3473f882001-02-23 17:55:21 +00002870xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002871 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002872 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
2873}
2874
2875/**
2876 * xmlValidateAttributeDecl:
2877 * @ctxt: the validation context
2878 * @doc: a document instance
2879 * @attr: an attribute definition
2880 *
2881 * Try to validate a single attribute definition
2882 * basically it does the following checks as described by the
2883 * XML-1.0 recommendation:
2884 * - [ VC: Attribute Default Legal ]
2885 * - [ VC: Enumeration ]
2886 * - [ VC: ID Attribute Default ]
2887 *
2888 * The ID/IDREF uniqueness and matching are done separately
2889 *
2890 * returns 1 if valid or 0 otherwise
2891 */
2892
2893int
2894xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2895 xmlAttributePtr attr) {
2896 int ret = 1;
2897 int val;
2898 CHECK_DTD;
2899 if(attr == NULL) return(1);
2900
2901 /* Attribute Default Legal */
2902 /* Enumeration */
2903 if (attr->defaultValue != NULL) {
2904 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
2905 if (val == 0) {
2906 VERROR(ctxt->userData,
2907 "Syntax of default value for attribute %s on %s is not valid\n",
2908 attr->name, attr->elem);
2909 }
2910 ret &= val;
2911 }
2912
2913 /* ID Attribute Default */
2914 if ((attr->atype == XML_ATTRIBUTE_ID)&&
2915 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
2916 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
2917 VERROR(ctxt->userData,
2918 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
2919 attr->name, attr->elem);
2920 ret = 0;
2921 }
2922
2923 /* One ID per Element Type */
2924 if (attr->atype == XML_ATTRIBUTE_ID) {
2925 int nbId;
2926
2927 /* the trick is taht we parse DtD as their own internal subset */
2928 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
2929 attr->elem);
2930 if (elem != NULL) {
2931 nbId = xmlScanIDAttributeDecl(NULL, elem);
2932 } else {
2933 xmlAttributeTablePtr table;
2934
2935 /*
2936 * The attribute may be declared in the internal subset and the
2937 * element in the external subset.
2938 */
2939 nbId = 0;
2940 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
2941 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
2942 xmlValidateAttributeIdCallback, &nbId);
2943 }
2944 if (nbId > 1) {
2945 VERROR(ctxt->userData,
2946 "Element %s has %d ID attribute defined in the internal subset : %s\n",
2947 attr->elem, nbId, attr->name);
2948 } else if (doc->extSubset != NULL) {
2949 int extId = 0;
2950 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
2951 if (elem != NULL) {
2952 extId = xmlScanIDAttributeDecl(NULL, elem);
2953 }
2954 if (extId > 1) {
2955 VERROR(ctxt->userData,
2956 "Element %s has %d ID attribute defined in the external subset : %s\n",
2957 attr->elem, extId, attr->name);
2958 } else if (extId + nbId > 1) {
2959 VERROR(ctxt->userData,
2960"Element %s has ID attributes defined in the internal and external subset : %s\n",
2961 attr->elem, attr->name);
2962 }
2963 }
2964 }
2965
2966 /* Validity Constraint: Enumeration */
2967 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
2968 xmlEnumerationPtr tree = attr->tree;
2969 while (tree != NULL) {
2970 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
2971 tree = tree->next;
2972 }
2973 if (tree == NULL) {
2974 VERROR(ctxt->userData,
2975"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
2976 attr->defaultValue, attr->name, attr->elem);
2977 ret = 0;
2978 }
2979 }
2980
2981 return(ret);
2982}
2983
2984/**
2985 * xmlValidateElementDecl:
2986 * @ctxt: the validation context
2987 * @doc: a document instance
2988 * @elem: an element definition
2989 *
2990 * Try to validate a single element definition
2991 * basically it does the following checks as described by the
2992 * XML-1.0 recommendation:
2993 * - [ VC: One ID per Element Type ]
2994 * - [ VC: No Duplicate Types ]
2995 * - [ VC: Unique Element Type Declaration ]
2996 *
2997 * returns 1 if valid or 0 otherwise
2998 */
2999
3000int
3001xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3002 xmlElementPtr elem) {
3003 int ret = 1;
3004 xmlElementPtr tst;
3005
3006 CHECK_DTD;
3007
3008 if (elem == NULL) return(1);
3009
3010 /* No Duplicate Types */
3011 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3012 xmlElementContentPtr cur, next;
3013 const xmlChar *name;
3014
3015 cur = elem->content;
3016 while (cur != NULL) {
3017 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3018 if (cur->c1 == NULL) break;
3019 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3020 name = cur->c1->name;
3021 next = cur->c2;
3022 while (next != NULL) {
3023 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3024 if (xmlStrEqual(next->name, name)) {
3025 VERROR(ctxt->userData,
3026 "Definition of %s has duplicate references of %s\n",
3027 elem->name, name);
3028 ret = 0;
3029 }
3030 break;
3031 }
3032 if (next->c1 == NULL) break;
3033 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3034 if (xmlStrEqual(next->c1->name, name)) {
3035 VERROR(ctxt->userData,
3036 "Definition of %s has duplicate references of %s\n",
3037 elem->name, name);
3038 ret = 0;
3039 }
3040 next = next->c2;
3041 }
3042 }
3043 cur = cur->c2;
3044 }
3045 }
3046
3047 /* VC: Unique Element Type Declaration */
3048 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003049 if ((tst != NULL ) && (tst != elem) &&
3050 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003051 VERROR(ctxt->userData, "Redefinition of element %s\n",
3052 elem->name);
3053 ret = 0;
3054 }
3055 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003056 if ((tst != NULL ) && (tst != elem) &&
3057 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003058 VERROR(ctxt->userData, "Redefinition of element %s\n",
3059 elem->name);
3060 ret = 0;
3061 }
3062
Daniel Veillarda10efa82001-04-18 13:09:01 +00003063 /* One ID per Element Type
3064 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003065 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3066 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003067 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003068 return(ret);
3069}
3070
3071/**
3072 * xmlValidateOneAttribute:
3073 * @ctxt: the validation context
3074 * @doc: a document instance
3075 * @elem: an element instance
3076 * @attr: an attribute instance
3077 * @value: the attribute value (without entities processing)
3078 *
3079 * Try to validate a single attribute for an element
3080 * basically it does the following checks as described by the
3081 * XML-1.0 recommendation:
3082 * - [ VC: Attribute Value Type ]
3083 * - [ VC: Fixed Attribute Default ]
3084 * - [ VC: Entity Name ]
3085 * - [ VC: Name Token ]
3086 * - [ VC: ID ]
3087 * - [ VC: IDREF ]
3088 * - [ VC: Entity Name ]
3089 * - [ VC: Notation Attributes ]
3090 *
3091 * The ID/IDREF uniqueness and matching are done separately
3092 *
3093 * returns 1 if valid or 0 otherwise
3094 */
3095
3096int
3097xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3098 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3099 /* xmlElementPtr elemDecl; */
3100 xmlAttributePtr attrDecl = NULL;
3101 int val;
3102 int ret = 1;
3103
3104 CHECK_DTD;
3105 if ((elem == NULL) || (elem->name == NULL)) return(0);
3106 if ((attr == NULL) || (attr->name == NULL)) return(0);
3107
3108 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3109 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003110 snprintf((char *) qname, sizeof(qname), "%s:%s",
3111 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003112 qname[sizeof(qname) - 1] = 0;
3113 if (attr->ns != NULL) {
3114 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3115 attr->name, attr->ns->prefix);
3116 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3117 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3118 attr->name, attr->ns->prefix);
3119 } else {
3120 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3121 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3122 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3123 qname, attr->name);
3124 }
3125 }
3126 if (attrDecl == NULL) {
3127 if (attr->ns != NULL) {
3128 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3129 attr->name, attr->ns->prefix);
3130 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3131 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3132 attr->name, attr->ns->prefix);
3133 } else {
3134 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3135 elem->name, attr->name);
3136 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3137 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3138 elem->name, attr->name);
3139 }
3140 }
3141
3142
3143 /* Validity Constraint: Attribute Value Type */
3144 if (attrDecl == NULL) {
3145 VERROR(ctxt->userData,
3146 "No declaration for attribute %s on element %s\n",
3147 attr->name, elem->name);
3148 return(0);
3149 }
3150 attr->atype = attrDecl->atype;
3151
3152 val = xmlValidateAttributeValue(attrDecl->atype, value);
3153 if (val == 0) {
3154 VERROR(ctxt->userData,
3155 "Syntax of value for attribute %s on %s is not valid\n",
3156 attr->name, elem->name);
3157 ret = 0;
3158 }
3159
3160 /* Validity constraint: Fixed Attribute Default */
3161 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3162 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3163 VERROR(ctxt->userData,
3164 "Value for attribute %s on %s is differnt from default \"%s\"\n",
3165 attr->name, elem->name, attrDecl->defaultValue);
3166 ret = 0;
3167 }
3168 }
3169
3170 /* Validity Constraint: ID uniqueness */
3171 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3172 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3173 ret = 0;
3174 }
3175
3176 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3177 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3178 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3179 ret = 0;
3180 }
3181
3182 /* Validity Constraint: Notation Attributes */
3183 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3184 xmlEnumerationPtr tree = attrDecl->tree;
3185 xmlNotationPtr nota;
3186
3187 /* First check that the given NOTATION was declared */
3188 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3189 if (nota == NULL)
3190 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3191
3192 if (nota == NULL) {
3193 VERROR(ctxt->userData,
3194 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3195 value, attr->name, elem->name);
3196 ret = 0;
3197 }
3198
3199 /* Second, verify that it's among the list */
3200 while (tree != NULL) {
3201 if (xmlStrEqual(tree->name, value)) break;
3202 tree = tree->next;
3203 }
3204 if (tree == NULL) {
3205 VERROR(ctxt->userData,
3206"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3207 value, attr->name, elem->name);
3208 ret = 0;
3209 }
3210 }
3211
3212 /* Validity Constraint: Enumeration */
3213 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3214 xmlEnumerationPtr tree = attrDecl->tree;
3215 while (tree != NULL) {
3216 if (xmlStrEqual(tree->name, value)) break;
3217 tree = tree->next;
3218 }
3219 if (tree == NULL) {
3220 VERROR(ctxt->userData,
3221 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3222 value, attr->name, elem->name);
3223 ret = 0;
3224 }
3225 }
3226
3227 /* Fixed Attribute Default */
3228 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3229 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3230 VERROR(ctxt->userData,
3231 "Value for attribute %s on %s must be \"%s\"\n",
3232 attr->name, elem->name, attrDecl->defaultValue);
3233 ret = 0;
3234 }
3235
3236 /* Extra check for the attribute value */
3237 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3238 attrDecl->atype, value);
3239
3240 return(ret);
3241}
3242
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003243/**
3244 * xmlValidateSkipIgnorable:
3245 * @ctxt: the validation context
3246 * @child: the child list
3247 *
3248 * Skip ignorable elements w.r.t. the validation process
3249 *
3250 * returns the first element to consider for validation of the content model
3251 */
3252
3253static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003254xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003255 while (child != NULL) {
3256 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003257 /* These things are ignored (skipped) during validation. */
3258 case XML_PI_NODE:
3259 case XML_COMMENT_NODE:
3260 case XML_XINCLUDE_START:
3261 case XML_XINCLUDE_END:
3262 child = child->next;
3263 break;
3264 case XML_TEXT_NODE:
3265 if (xmlIsBlankNode(child))
3266 child = child->next;
3267 else
3268 return(child);
3269 break;
3270 /* keep current node */
3271 default:
3272 return(child);
3273 }
3274 }
3275 return(child);
3276}
3277
3278/**
3279 * xmlValidateElementType:
3280 * @ctxt: the validation context
3281 *
3282 * Try to validate the content model of an element internal function
3283 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003284 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3285 * reference is found and -3 if the validation succeeded but
3286 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003287 */
3288
3289static int
3290xmlValidateElementType(xmlValidCtxtPtr ctxt) {
3291 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003292 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003293
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003294 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003295 if ((NODE == NULL) && (CONT == NULL))
3296 return(1);
3297 if ((NODE == NULL) &&
3298 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3299 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3300 return(1);
3301 }
3302 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003303 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003304 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003305
3306 /*
3307 * We arrive here when more states need to be examined
3308 */
3309cont:
3310
3311 /*
3312 * We just recovered from a rollback generated by a possible
3313 * epsilon transition, go directly to the analysis phase
3314 */
3315 if (STATE == ROLLBACK_PARENT) {
3316 DEBUG_VALID_MSG("restaured parent branch");
3317 DEBUG_VALID_STATE(NODE, CONT)
3318 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003319 goto analyze;
3320 }
3321
3322 DEBUG_VALID_STATE(NODE, CONT)
3323 /*
3324 * we may have to save a backup state here. This is the equivalent
3325 * of handling epsilon transition in NFAs.
3326 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003327 if ((CONT != NULL) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003328 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003329 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
3330 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003331 DEBUG_VALID_MSG("saving parent branch");
3332 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3333 }
3334
3335
3336 /*
3337 * Check first if the content matches
3338 */
3339 switch (CONT->type) {
3340 case XML_ELEMENT_CONTENT_PCDATA:
3341 if (NODE == NULL) {
3342 DEBUG_VALID_MSG("pcdata failed no node");
3343 ret = 0;
3344 break;
3345 }
3346 if (NODE->type == XML_TEXT_NODE) {
3347 DEBUG_VALID_MSG("pcdata found, skip to next");
3348 /*
3349 * go to next element in the content model
3350 * skipping ignorable elems
3351 */
3352 do {
3353 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003354 NODE = xmlValidateSkipIgnorable(NODE);
3355 if ((NODE != NULL) &&
3356 (NODE->type == XML_ENTITY_REF_NODE))
3357 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003358 } while ((NODE != NULL) &&
3359 ((NODE->type != XML_ELEMENT_NODE) &&
3360 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003361 ret = 1;
3362 break;
3363 } else {
3364 DEBUG_VALID_MSG("pcdata failed");
3365 ret = 0;
3366 break;
3367 }
3368 break;
3369 case XML_ELEMENT_CONTENT_ELEMENT:
3370 if (NODE == NULL) {
3371 DEBUG_VALID_MSG("element failed no node");
3372 ret = 0;
3373 break;
3374 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003375 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3376 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003377 if (ret == 1) {
3378 DEBUG_VALID_MSG("element found, skip to next");
3379 /*
3380 * go to next element in the content model
3381 * skipping ignorable elems
3382 */
3383 do {
3384 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003385 NODE = xmlValidateSkipIgnorable(NODE);
3386 if ((NODE != NULL) &&
3387 (NODE->type == XML_ENTITY_REF_NODE))
3388 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003389 } while ((NODE != NULL) &&
3390 ((NODE->type != XML_ELEMENT_NODE) &&
3391 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003392 } else {
3393 DEBUG_VALID_MSG("element failed");
3394 ret = 0;
3395 break;
3396 }
3397 break;
3398 case XML_ELEMENT_CONTENT_OR:
3399 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003400 * Small optimization.
3401 */
3402 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3403 if ((NODE == NULL) ||
3404 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3405 DEPTH++;
3406 CONT = CONT->c2;
3407 goto cont;
3408 }
3409 }
3410
3411 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003412 * save the second branch 'or' branch
3413 */
3414 DEBUG_VALID_MSG("saving 'or' branch");
3415 vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1, OCCURS, ROLLBACK_OR);
3416
3417 DEPTH++;
3418 CONT = CONT->c1;
3419 goto cont;
3420 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003421 /*
3422 * Small optimization.
3423 */
3424 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3425 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3426 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3427 if ((NODE == NULL) ||
3428 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3429 DEPTH++;
3430 CONT = CONT->c2;
3431 goto cont;
3432 }
3433 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003434 DEPTH++;
3435 CONT = CONT->c1;
3436 goto cont;
3437 }
3438
3439 /*
3440 * At this point handle going up in the tree
3441 */
3442 if (ret == -1) {
3443 DEBUG_VALID_MSG("error found returning");
3444 return(ret);
3445 }
3446analyze:
3447 while (CONT != NULL) {
3448 /*
3449 * First do the analysis depending on the occurence model at
3450 * this level.
3451 */
3452 if (ret == 0) {
3453 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003454 xmlNodePtr cur;
3455
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003456 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003457 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003458 DEBUG_VALID_MSG("Once branch failed, rollback");
3459 if (vstateVPop(ctxt) < 0 ) {
3460 DEBUG_VALID_MSG("exhaustion, failed");
3461 return(0);
3462 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003463 if (cur != ctxt->vstate->node)
3464 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003465 goto cont;
3466 case XML_ELEMENT_CONTENT_PLUS:
3467 if (OCCURENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003468 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003469 DEBUG_VALID_MSG("Plus branch failed, rollback");
3470 if (vstateVPop(ctxt) < 0 ) {
3471 DEBUG_VALID_MSG("exhaustion, failed");
3472 return(0);
3473 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003474 if (cur != ctxt->vstate->node)
3475 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003476 goto cont;
3477 }
3478 DEBUG_VALID_MSG("Plus branch found");
3479 ret = 1;
3480 break;
3481 case XML_ELEMENT_CONTENT_MULT:
3482#ifdef DEBUG_VALID_ALGO
3483 if (OCCURENCE == 0) {
3484 DEBUG_VALID_MSG("Mult branch failed");
3485 } else {
3486 DEBUG_VALID_MSG("Mult branch found");
3487 }
3488#endif
3489 ret = 1;
3490 break;
3491 case XML_ELEMENT_CONTENT_OPT:
3492 DEBUG_VALID_MSG("Option branch failed");
3493 ret = 1;
3494 break;
3495 }
3496 } else {
3497 switch (CONT->ocur) {
3498 case XML_ELEMENT_CONTENT_OPT:
3499 DEBUG_VALID_MSG("Option branch succeeded");
3500 ret = 1;
3501 break;
3502 case XML_ELEMENT_CONTENT_ONCE:
3503 DEBUG_VALID_MSG("Once branch succeeded");
3504 ret = 1;
3505 break;
3506 case XML_ELEMENT_CONTENT_PLUS:
3507 if (STATE == ROLLBACK_PARENT) {
3508 DEBUG_VALID_MSG("Plus branch rollback");
3509 ret = 1;
3510 break;
3511 }
3512 if (NODE == NULL) {
3513 DEBUG_VALID_MSG("Plus branch exhausted");
3514 ret = 1;
3515 break;
3516 }
3517 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
3518 SET_OCCURENCE;
3519 goto cont;
3520 case XML_ELEMENT_CONTENT_MULT:
3521 if (STATE == ROLLBACK_PARENT) {
3522 DEBUG_VALID_MSG("Mult branch rollback");
3523 ret = 1;
3524 break;
3525 }
3526 if (NODE == NULL) {
3527 DEBUG_VALID_MSG("Mult branch exhausted");
3528 ret = 1;
3529 break;
3530 }
3531 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
3532 SET_OCCURENCE;
3533 goto cont;
3534 }
3535 }
3536 STATE = 0;
3537
3538 /*
3539 * Then act accordingly at the parent level
3540 */
3541 RESET_OCCURENCE;
3542 if (CONT->parent == NULL)
3543 break;
3544
3545 switch (CONT->parent->type) {
3546 case XML_ELEMENT_CONTENT_PCDATA:
3547 DEBUG_VALID_MSG("Error: parent pcdata");
3548 return(-1);
3549 case XML_ELEMENT_CONTENT_ELEMENT:
3550 DEBUG_VALID_MSG("Error: parent element");
3551 return(-1);
3552 case XML_ELEMENT_CONTENT_OR:
3553 if (ret == 1) {
3554 DEBUG_VALID_MSG("Or succeeded");
3555 CONT = CONT->parent;
3556 DEPTH--;
3557 } else {
3558 DEBUG_VALID_MSG("Or failed");
3559 CONT = CONT->parent;
3560 DEPTH--;
3561 }
3562 break;
3563 case XML_ELEMENT_CONTENT_SEQ:
3564 if (ret == 0) {
3565 DEBUG_VALID_MSG("Sequence failed");
3566 CONT = CONT->parent;
3567 DEPTH--;
3568 } else if (CONT == CONT->parent->c1) {
3569 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3570 CONT = CONT->parent->c2;
3571 goto cont;
3572 } else {
3573 DEBUG_VALID_MSG("Sequence succeeded");
3574 CONT = CONT->parent;
3575 DEPTH--;
3576 }
3577 }
3578 }
3579 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003580 xmlNodePtr cur;
3581
3582 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003583 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3584 if (vstateVPop(ctxt) < 0 ) {
3585 DEBUG_VALID_MSG("exhaustion, failed");
3586 return(0);
3587 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003588 if (cur != ctxt->vstate->node)
3589 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003590 goto cont;
3591 }
3592 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003593 xmlNodePtr cur;
3594
3595 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003596 DEBUG_VALID_MSG("Failure, rollback");
3597 if (vstateVPop(ctxt) < 0 ) {
3598 DEBUG_VALID_MSG("exhaustion, failed");
3599 return(0);
3600 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003601 if (cur != ctxt->vstate->node)
3602 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003603 goto cont;
3604 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003605 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003606}
3607
3608/**
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003609 * xmlSprintfElements:
3610 * @buf: an output buffer
3611 * @content: An element
3612 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3613 *
3614 * This will dump the list of elements to the buffer
3615 * Intended just for the debug routine
3616 */
3617static void
3618xmlSprintfElements(char *buf, xmlNodePtr node, int glob) {
3619 xmlNodePtr cur;
3620
3621 if (node == NULL) return;
3622 if (glob) strcat(buf, "(");
3623 cur = node;
3624 while (cur != NULL) {
3625 switch (cur->type) {
3626 case XML_ELEMENT_NODE:
3627 strcat(buf, (char *) cur->name);
3628 if (cur->next != NULL)
3629 strcat(buf, " ");
3630 break;
3631 case XML_TEXT_NODE:
3632 if (xmlIsBlankNode(cur))
3633 break;
3634 case XML_CDATA_SECTION_NODE:
3635 case XML_ENTITY_REF_NODE:
3636 strcat(buf, "CDATA");
3637 if (cur->next != NULL)
3638 strcat(buf, " ");
3639 break;
3640 case XML_ATTRIBUTE_NODE:
3641 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003642#ifdef LIBXML_DOCB_ENABLED
3643 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003644#endif
3645 case XML_HTML_DOCUMENT_NODE:
3646 case XML_DOCUMENT_TYPE_NODE:
3647 case XML_DOCUMENT_FRAG_NODE:
3648 case XML_NOTATION_NODE:
3649 case XML_NAMESPACE_DECL:
3650 strcat(buf, "???");
3651 if (cur->next != NULL)
3652 strcat(buf, " ");
3653 break;
3654 case XML_ENTITY_NODE:
3655 case XML_PI_NODE:
3656 case XML_DTD_NODE:
3657 case XML_COMMENT_NODE:
3658 case XML_ELEMENT_DECL:
3659 case XML_ATTRIBUTE_DECL:
3660 case XML_ENTITY_DECL:
3661 case XML_XINCLUDE_START:
3662 case XML_XINCLUDE_END:
3663 break;
3664 }
3665 cur = cur->next;
3666 }
3667 if (glob) strcat(buf, ")");
3668}
3669
3670/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003671 * xmlValidateElementContent:
3672 * @ctxt: the validation context
3673 * @child: the child list
3674 * @cont: pointer to the content declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003675 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003676 *
3677 * Try to validate the content model of an element
3678 *
3679 * returns 1 if valid or 0 if not and -1 in case of error
3680 */
3681
3682static int
3683xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillard7533cc82001-04-24 15:52:00 +00003684 xmlElementContentPtr cont, int warn, const xmlChar *name) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003685 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003686 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003687
3688 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003689 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003690 */
3691 ctxt->vstateMax = 8;
3692 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3693 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3694 if (ctxt->vstateTab == NULL) {
3695 xmlGenericError(xmlGenericErrorContext,
3696 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003697 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003698 }
3699 /*
3700 * The first entry in the stack is reserved to the current state
3701 */
Daniel Veillard61b33d52001-04-24 13:55:12 +00003702 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003703 ctxt->vstate = &ctxt->vstateTab[0];
3704 ctxt->vstateNr = 1;
3705 CONT = cont;
3706 NODE = child;
3707 DEPTH = 0;
3708 OCCURS = 0;
3709 STATE = 0;
3710 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003711 if ((ret == -3) && (warn)) {
3712 VWARNING(ctxt->userData,
3713 "Element %s content model is ambiguous\n", name);
3714 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003715 /*
3716 * An entities reference appeared at this level.
3717 * Buid a minimal representation of this node content
3718 * sufficient to run the validation process on it
3719 */
3720 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003721 cur = child;
3722 while (cur != NULL) {
3723 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003724 case XML_ENTITY_REF_NODE:
3725 /*
3726 * Push the current node to be able to roll back
3727 * and process within the entity
3728 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003729 if ((cur->children != NULL) &&
3730 (cur->children->children != NULL)) {
3731 nodeVPush(ctxt, cur);
3732 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003733 continue;
3734 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00003735 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003736 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003737 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003738 break;
3739 case XML_ELEMENT_NODE:
3740 /*
3741 * Allocate a new node and minimally fills in
3742 * what's required
3743 */
3744 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3745 if (tmp == NULL) {
3746 xmlGenericError(xmlGenericErrorContext,
3747 "xmlValidateElementContent : malloc failed\n");
3748 xmlFreeNodeList(repl);
3749 ret = -1;
3750 goto done;
3751 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003752 tmp->type = cur->type;
3753 tmp->name = cur->name;
3754 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003755 tmp->next = NULL;
3756 if (repl == NULL)
3757 repl = last = tmp;
3758 else {
3759 last->next = tmp;
3760 last = tmp;
3761 }
3762 break;
3763 default:
3764 break;
3765 }
3766 /*
3767 * Switch to next element
3768 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003769 cur = cur->next;
3770 while (cur == NULL) {
3771 cur = nodeVPop(ctxt);
3772 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003773 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003774 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003775 }
3776 }
3777
3778 /*
3779 * Relaunch the validation
3780 */
3781 ctxt->vstate = &ctxt->vstateTab[0];
3782 ctxt->vstateNr = 1;
3783 CONT = cont;
3784 NODE = repl;
3785 DEPTH = 0;
3786 OCCURS = 0;
3787 STATE = 0;
3788 ret = xmlValidateElementType(ctxt);
3789 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003790 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003791 char expr[5000];
3792 char list[5000];
3793
3794 expr[0] = 0;
3795 xmlSprintfElementContent(expr, cont, 1);
3796 list[0] = 0;
3797 if (repl != NULL)
3798 xmlSprintfElements(list, repl, 1);
3799 else
3800 xmlSprintfElements(list, child, 1);
3801
Daniel Veillard7533cc82001-04-24 15:52:00 +00003802 if (name != NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003803 VERROR(ctxt->userData,
3804 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
Daniel Veillard7533cc82001-04-24 15:52:00 +00003805 name, expr, list);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003806 } else {
3807 VERROR(ctxt->userData,
3808 "Element content doesn't follow the Dtd\nExpecting %s, got %s\n",
3809 expr, list);
3810 }
3811 ret = 0;
3812 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003813 if (ret == -3)
3814 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003815
3816
3817done:
3818 /*
3819 * Deallocate the copy if done, and free up the validation stack
3820 */
3821 while (repl != NULL) {
3822 tmp = repl->next;
3823 xmlFree(repl);
3824 repl = tmp;
3825 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003826 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003827 if (ctxt->vstateTab != NULL) {
3828 xmlFree(ctxt->vstateTab);
3829 ctxt->vstateTab = NULL;
3830 }
3831 ctxt->nodeMax = 0;
3832 if (ctxt->nodeTab != NULL) {
3833 xmlFree(ctxt->nodeTab);
3834 ctxt->nodeTab = NULL;
3835 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003836 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003837
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003838}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003839
Owen Taylor3473f882001-02-23 17:55:21 +00003840/**
Owen Taylor3473f882001-02-23 17:55:21 +00003841 * xmlValidateOneElement:
3842 * @ctxt: the validation context
3843 * @doc: a document instance
3844 * @elem: an element instance
3845 *
3846 * Try to validate a single element and it's attributes,
3847 * basically it does the following checks as described by the
3848 * XML-1.0 recommendation:
3849 * - [ VC: Element Valid ]
3850 * - [ VC: Required Attribute ]
3851 * Then call xmlValidateOneAttribute() for each attribute present.
3852 *
3853 * The ID/IDREF checkings are done separately
3854 *
3855 * returns 1 if valid or 0 otherwise
3856 */
3857
3858int
3859xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3860 xmlNodePtr elem) {
3861 xmlElementPtr elemDecl = NULL;
3862 xmlElementContentPtr cont;
3863 xmlAttributePtr attr;
3864 xmlNodePtr child;
3865 int ret = 1;
3866 const xmlChar *name;
3867
3868 CHECK_DTD;
3869
3870 if (elem == NULL) return(0);
3871 if (elem->type == XML_TEXT_NODE) {
3872 }
3873 switch (elem->type) {
3874 case XML_ATTRIBUTE_NODE:
3875 VERROR(ctxt->userData,
3876 "Attribute element not expected here\n");
3877 return(0);
3878 case XML_TEXT_NODE:
3879 if (elem->children != NULL) {
3880 VERROR(ctxt->userData, "Text element has childs !\n");
3881 return(0);
3882 }
3883 if (elem->properties != NULL) {
3884 VERROR(ctxt->userData, "Text element has attributes !\n");
3885 return(0);
3886 }
3887 if (elem->ns != NULL) {
3888 VERROR(ctxt->userData, "Text element has namespace !\n");
3889 return(0);
3890 }
3891 if (elem->nsDef != NULL) {
3892 VERROR(ctxt->userData,
3893 "Text element carries namespace definitions !\n");
3894 return(0);
3895 }
3896 if (elem->content == NULL) {
3897 VERROR(ctxt->userData,
3898 "Text element has no content !\n");
3899 return(0);
3900 }
3901 return(1);
3902 case XML_XINCLUDE_START:
3903 case XML_XINCLUDE_END:
3904 return(1);
3905 case XML_CDATA_SECTION_NODE:
3906 case XML_ENTITY_REF_NODE:
3907 case XML_PI_NODE:
3908 case XML_COMMENT_NODE:
3909 return(1);
3910 case XML_ENTITY_NODE:
3911 VERROR(ctxt->userData,
3912 "Entity element not expected here\n");
3913 return(0);
3914 case XML_NOTATION_NODE:
3915 VERROR(ctxt->userData,
3916 "Notation element not expected here\n");
3917 return(0);
3918 case XML_DOCUMENT_NODE:
3919 case XML_DOCUMENT_TYPE_NODE:
3920 case XML_DOCUMENT_FRAG_NODE:
3921 VERROR(ctxt->userData,
3922 "Document element not expected here\n");
3923 return(0);
3924 case XML_HTML_DOCUMENT_NODE:
3925 VERROR(ctxt->userData,
3926 "\n");
3927 return(0);
3928 case XML_ELEMENT_NODE:
3929 break;
3930 default:
3931 VERROR(ctxt->userData,
3932 "unknown element type %d\n", elem->type);
3933 return(0);
3934 }
3935 if (elem->name == NULL) return(0);
3936
3937 /*
3938 * Fetch the declaration for the qualified name
3939 */
3940 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3941 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3942 elem->name, elem->ns->prefix);
3943 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3944 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3945 elem->name, elem->ns->prefix);
3946 }
3947
3948 /*
3949 * Fetch the declaration for the non qualified name
3950 */
3951 if (elemDecl == NULL) {
3952 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3953 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3954 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3955 }
3956 if (elemDecl == NULL) {
3957 VERROR(ctxt->userData, "No declaration for element %s\n",
3958 elem->name);
3959 return(0);
3960 }
3961
3962 /* Check taht the element content matches the definition */
3963 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003964 case XML_ELEMENT_TYPE_UNDEFINED:
3965 VERROR(ctxt->userData, "No declaration for element %s\n",
3966 elem->name);
3967 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003968 case XML_ELEMENT_TYPE_EMPTY:
3969 if (elem->children != NULL) {
3970 VERROR(ctxt->userData,
3971 "Element %s was declared EMPTY this one has content\n",
3972 elem->name);
3973 ret = 0;
3974 }
3975 break;
3976 case XML_ELEMENT_TYPE_ANY:
3977 /* I don't think anything is required then */
3978 break;
3979 case XML_ELEMENT_TYPE_MIXED:
3980 /* Hum, this start to get messy */
3981 child = elem->children;
3982 while (child != NULL) {
3983 if (child->type == XML_ELEMENT_NODE) {
3984 name = child->name;
3985 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
3986 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003987 snprintf((char *) qname, sizeof(qname), "%s:%s",
3988 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003989 qname[sizeof(qname) - 1] = 0;
3990 cont = elemDecl->content;
3991 while (cont != NULL) {
3992 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3993 if (xmlStrEqual(cont->name, qname)) break;
3994 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3995 (cont->c1 != NULL) &&
3996 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
3997 if (xmlStrEqual(cont->c1->name, qname)) break;
3998 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3999 (cont->c1 == NULL) ||
4000 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4001 /* Internal error !!! */
4002 xmlGenericError(xmlGenericErrorContext,
4003 "Internal: MIXED struct bad\n");
4004 break;
4005 }
4006 cont = cont->c2;
4007 }
4008 if (cont != NULL)
4009 goto child_ok;
4010 }
4011 cont = elemDecl->content;
4012 while (cont != NULL) {
4013 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4014 if (xmlStrEqual(cont->name, name)) break;
4015 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4016 (cont->c1 != NULL) &&
4017 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4018 if (xmlStrEqual(cont->c1->name, name)) break;
4019 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4020 (cont->c1 == NULL) ||
4021 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4022 /* Internal error !!! */
4023 xmlGenericError(xmlGenericErrorContext,
4024 "Internal: MIXED struct bad\n");
4025 break;
4026 }
4027 cont = cont->c2;
4028 }
4029 if (cont == NULL) {
4030 VERROR(ctxt->userData,
4031 "Element %s is not declared in %s list of possible childs\n",
4032 name, elem->name);
4033 ret = 0;
4034 }
4035 }
4036child_ok:
4037 child = child->next;
4038 }
4039 break;
4040 case XML_ELEMENT_TYPE_ELEMENT:
4041 child = elem->children;
4042 cont = elemDecl->content;
Daniel Veillard7533cc82001-04-24 15:52:00 +00004043 ret = xmlValidateElementContent(ctxt, child, cont, 1, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004044 break;
4045 }
4046
4047 /* [ VC: Required Attribute ] */
4048 attr = elemDecl->attributes;
4049 while (attr != NULL) {
4050 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4051 xmlAttrPtr attrib;
4052 int qualified = -1;
4053
4054 attrib = elem->properties;
4055 while (attrib != NULL) {
4056 if (xmlStrEqual(attrib->name, attr->name)) {
4057 if (attr->prefix != NULL) {
4058 xmlNsPtr nameSpace = attrib->ns;
4059
4060 if (nameSpace == NULL)
4061 nameSpace = elem->ns;
4062 /*
4063 * qualified names handling is problematic, having a
4064 * different prefix should be possible but DTDs don't
4065 * allow to define the URI instead of the prefix :-(
4066 */
4067 if (nameSpace == NULL) {
4068 if (qualified < 0)
4069 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004070 } else if (!xmlStrEqual(nameSpace->prefix,
4071 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004072 if (qualified < 1)
4073 qualified = 1;
4074 } else
4075 goto found;
4076 } else {
4077 /*
4078 * We should allow applications to define namespaces
4079 * for their application even if the DTD doesn't
4080 * carry one, otherwise, basically we would always
4081 * break.
4082 */
4083 goto found;
4084 }
4085 }
4086 attrib = attrib->next;
4087 }
4088 if (qualified == -1) {
4089 if (attr->prefix == NULL) {
4090 VERROR(ctxt->userData,
4091 "Element %s doesn't carry attribute %s\n",
4092 elem->name, attr->name);
4093 ret = 0;
4094 } else {
4095 VERROR(ctxt->userData,
4096 "Element %s doesn't carry attribute %s:%s\n",
4097 elem->name, attr->prefix,attr->name);
4098 ret = 0;
4099 }
4100 } else if (qualified == 0) {
4101 VWARNING(ctxt->userData,
4102 "Element %s required attribute %s:%s has no prefix\n",
4103 elem->name, attr->prefix,attr->name);
4104 } else if (qualified == 1) {
4105 VWARNING(ctxt->userData,
4106 "Element %s required attribute %s:%s has different prefix\n",
4107 elem->name, attr->prefix,attr->name);
4108 }
4109 }
4110found:
4111 attr = attr->nexth;
4112 }
4113 return(ret);
4114}
4115
4116/**
4117 * xmlValidateRoot:
4118 * @ctxt: the validation context
4119 * @doc: a document instance
4120 *
4121 * Try to validate a the root element
4122 * basically it does the following check as described by the
4123 * XML-1.0 recommendation:
4124 * - [ VC: Root Element Type ]
4125 * it doesn't try to recurse or apply other check to the element
4126 *
4127 * returns 1 if valid or 0 otherwise
4128 */
4129
4130int
4131xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4132 xmlNodePtr root;
4133 if (doc == NULL) return(0);
4134
4135 root = xmlDocGetRootElement(doc);
4136 if ((root == NULL) || (root->name == NULL)) {
4137 VERROR(ctxt->userData, "Not valid: no root element\n");
4138 return(0);
4139 }
4140
4141 /*
4142 * When doing post validation against a separate DTD, those may
4143 * no internal subset has been generated
4144 */
4145 if ((doc->intSubset != NULL) &&
4146 (doc->intSubset->name != NULL)) {
4147 /*
4148 * Check first the document root against the NQName
4149 */
4150 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4151 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4152 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004153 snprintf((char *) qname, sizeof(qname), "%s:%s",
4154 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004155 qname[sizeof(qname) - 1] = 0;
4156 if (xmlStrEqual(doc->intSubset->name, qname))
4157 goto name_ok;
4158 }
4159 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4160 (xmlStrEqual(root->name, BAD_CAST "html")))
4161 goto name_ok;
4162 VERROR(ctxt->userData,
4163 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4164 root->name, doc->intSubset->name);
4165 return(0);
4166
4167 }
4168 }
4169name_ok:
4170 return(1);
4171}
4172
4173
4174/**
4175 * xmlValidateElement:
4176 * @ctxt: the validation context
4177 * @doc: a document instance
4178 * @elem: an element instance
4179 *
4180 * Try to validate the subtree under an element
4181 *
4182 * returns 1 if valid or 0 otherwise
4183 */
4184
4185int
4186xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4187 xmlNodePtr child;
4188 xmlAttrPtr attr;
4189 xmlChar *value;
4190 int ret = 1;
4191
4192 if (elem == NULL) return(0);
4193
4194 /*
4195 * XInclude elements were added after parsing in the infoset,
4196 * they don't really mean anything validation wise.
4197 */
4198 if ((elem->type == XML_XINCLUDE_START) ||
4199 (elem->type == XML_XINCLUDE_END))
4200 return(1);
4201
4202 CHECK_DTD;
4203
4204 ret &= xmlValidateOneElement(ctxt, doc, elem);
4205 attr = elem->properties;
4206 while(attr != NULL) {
4207 value = xmlNodeListGetString(doc, attr->children, 0);
4208 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4209 if (value != NULL)
4210 xmlFree(value);
4211 attr= attr->next;
4212 }
4213 child = elem->children;
4214 while (child != NULL) {
4215 ret &= xmlValidateElement(ctxt, doc, child);
4216 child = child->next;
4217 }
4218
4219 return(ret);
4220}
4221
Daniel Veillard8730c562001-02-26 10:49:57 +00004222/**
4223 * xmlValidateRef:
4224 * @ref: A reference to be validated
4225 * @ctxt: Validation context
4226 * @name: Name of ID we are searching for
4227 *
4228 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004229static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004230xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004231 const xmlChar *name) {
4232 xmlAttrPtr id;
4233 xmlAttrPtr attr;
4234
4235 if (ref == NULL)
4236 return;
4237 attr = ref->attr;
4238 if (attr == NULL)
4239 return;
4240 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4241 id = xmlGetID(ctxt->doc, name);
4242 if (id == NULL) {
4243 VERROR(ctxt->userData,
4244 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4245 attr->name, name);
4246 ctxt->valid = 0;
4247 }
4248 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4249 xmlChar *dup, *str = NULL, *cur, save;
4250
4251 dup = xmlStrdup(name);
4252 if (dup == NULL) {
4253 ctxt->valid = 0;
4254 return;
4255 }
4256 cur = dup;
4257 while (*cur != 0) {
4258 str = cur;
4259 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4260 save = *cur;
4261 *cur = 0;
4262 id = xmlGetID(ctxt->doc, str);
4263 if (id == NULL) {
4264 VERROR(ctxt->userData,
4265 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4266 attr->name, str);
4267 ctxt->valid = 0;
4268 }
4269 if (save == 0)
4270 break;
4271 *cur = save;
4272 while (IS_BLANK(*cur)) cur++;
4273 }
4274 xmlFree(dup);
4275 }
4276}
4277
4278/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004279 * xmlWalkValidateList:
4280 * @data: Contents of current link
4281 * @user: Value supplied by the user
4282 *
4283 * Return 0 to abort the walk or 1 to continue
4284 */
4285static int
4286xmlWalkValidateList(const void *data, const void *user)
4287{
4288 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4289 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4290 return 1;
4291}
4292
4293/**
4294 * xmlValidateCheckRefCallback:
4295 * @ref_list: List of references
4296 * @ctxt: Validation context
4297 * @name: Name of ID we are searching for
4298 *
4299 */
4300static void
4301xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4302 const xmlChar *name) {
4303 xmlValidateMemo memo;
4304
4305 if (ref_list == NULL)
4306 return;
4307 memo.ctxt = ctxt;
4308 memo.name = name;
4309
4310 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4311
4312}
4313
4314/**
Owen Taylor3473f882001-02-23 17:55:21 +00004315 * xmlValidateDocumentFinal:
4316 * @ctxt: the validation context
4317 * @doc: a document instance
4318 *
4319 * Does the final step for the document validation once all the
4320 * incremental validation steps have been completed
4321 *
4322 * basically it does the following checks described by the XML Rec
4323 *
4324 *
4325 * returns 1 if valid or 0 otherwise
4326 */
4327
4328int
4329xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4330 xmlRefTablePtr table;
4331
4332 if (doc == NULL) {
4333 xmlGenericError(xmlGenericErrorContext,
4334 "xmlValidateDocumentFinal: doc == NULL\n");
4335 return(0);
4336 }
4337
4338 /*
4339 * Check all the NOTATION/NOTATIONS attributes
4340 */
4341 /*
4342 * Check all the ENTITY/ENTITIES attributes definition for validity
4343 */
4344 /*
4345 * Check all the IDREF/IDREFS attributes definition for validity
4346 */
4347 table = (xmlRefTablePtr) doc->refs;
4348 ctxt->doc = doc;
4349 ctxt->valid = 1;
4350 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4351 return(ctxt->valid);
4352}
4353
4354/**
4355 * xmlValidateDtd:
4356 * @ctxt: the validation context
4357 * @doc: a document instance
4358 * @dtd: a dtd instance
4359 *
4360 * Try to validate the document against the dtd instance
4361 *
4362 * basically it does check all the definitions in the DtD.
4363 *
4364 * returns 1 if valid or 0 otherwise
4365 */
4366
4367int
4368xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4369 int ret;
4370 xmlDtdPtr oldExt;
4371 xmlNodePtr root;
4372
4373 if (dtd == NULL) return(0);
4374 if (doc == NULL) return(0);
4375 oldExt = doc->extSubset;
4376 doc->extSubset = dtd;
4377 ret = xmlValidateRoot(ctxt, doc);
4378 if (ret == 0) {
4379 doc->extSubset = oldExt;
4380 return(ret);
4381 }
4382 if (doc->ids != NULL) {
4383 xmlFreeIDTable(doc->ids);
4384 doc->ids = NULL;
4385 }
4386 if (doc->refs != NULL) {
4387 xmlFreeRefTable(doc->refs);
4388 doc->refs = NULL;
4389 }
4390 root = xmlDocGetRootElement(doc);
4391 ret = xmlValidateElement(ctxt, doc, root);
4392 ret &= xmlValidateDocumentFinal(ctxt, doc);
4393 doc->extSubset = oldExt;
4394 return(ret);
4395}
4396
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004397static void
Owen Taylor3473f882001-02-23 17:55:21 +00004398xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004399 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004400 if (cur == NULL)
4401 return;
4402 switch (cur->atype) {
4403 case XML_ATTRIBUTE_CDATA:
4404 case XML_ATTRIBUTE_ID:
4405 case XML_ATTRIBUTE_IDREF :
4406 case XML_ATTRIBUTE_IDREFS:
4407 case XML_ATTRIBUTE_NMTOKEN:
4408 case XML_ATTRIBUTE_NMTOKENS:
4409 case XML_ATTRIBUTE_ENUMERATION:
4410 break;
4411 case XML_ATTRIBUTE_ENTITY:
4412 case XML_ATTRIBUTE_ENTITIES:
4413 case XML_ATTRIBUTE_NOTATION:
4414 if (cur->defaultValue != NULL) {
4415 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4416 cur->name, cur->atype, cur->defaultValue);
4417 }
4418 if (cur->tree != NULL) {
4419 xmlEnumerationPtr tree = cur->tree;
4420 while (tree != NULL) {
4421 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4422 cur->name, cur->atype, tree->name);
4423 tree = tree->next;
4424 }
4425 }
4426 }
4427}
4428
4429/**
4430 * xmlValidateDtdFinal:
4431 * @ctxt: the validation context
4432 * @doc: a document instance
4433 *
4434 * Does the final step for the dtds validation once all the
4435 * subsets have been parsed
4436 *
4437 * basically it does the following checks described by the XML Rec
4438 * - check that ENTITY and ENTITIES type attributes default or
4439 * possible values matches one of the defined entities.
4440 * - check that NOTATION type attributes default or
4441 * possible values matches one of the defined notations.
4442 *
4443 * returns 1 if valid or 0 otherwise
4444 */
4445
4446int
4447xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4448 int ret = 1;
4449 xmlDtdPtr dtd;
4450 xmlAttributeTablePtr table;
4451
4452 if (doc == NULL) return(0);
4453 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4454 return(0);
4455 ctxt->doc = doc;
4456 ctxt->valid = ret;
4457 dtd = doc->intSubset;
4458 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4459 table = (xmlAttributeTablePtr) dtd->attributes;
4460 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4461 }
4462 dtd = doc->extSubset;
4463 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4464 table = (xmlAttributeTablePtr) dtd->attributes;
4465 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4466 }
4467 return(ctxt->valid);
4468}
4469
4470/**
4471 * xmlValidateDocument:
4472 * @ctxt: the validation context
4473 * @doc: a document instance
4474 *
4475 * Try to validate the document instance
4476 *
4477 * basically it does the all the checks described by the XML Rec
4478 * i.e. validates the internal and external subset (if present)
4479 * and validate the document tree.
4480 *
4481 * returns 1 if valid or 0 otherwise
4482 */
4483
4484int
4485xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4486 int ret;
4487 xmlNodePtr root;
4488
4489 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4490 return(0);
4491 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4492 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4493 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4494 doc->intSubset->SystemID);
4495 if (doc->extSubset == NULL) {
4496 if (doc->intSubset->SystemID != NULL) {
4497 VERROR(ctxt->userData,
4498 "Could not load the external subset \"%s\"\n",
4499 doc->intSubset->SystemID);
4500 } else {
4501 VERROR(ctxt->userData,
4502 "Could not load the external subset \"%s\"\n",
4503 doc->intSubset->ExternalID);
4504 }
4505 return(0);
4506 }
4507 }
4508
4509 if (doc->ids != NULL) {
4510 xmlFreeIDTable(doc->ids);
4511 doc->ids = NULL;
4512 }
4513 if (doc->refs != NULL) {
4514 xmlFreeRefTable(doc->refs);
4515 doc->refs = NULL;
4516 }
4517 ret = xmlValidateDtdFinal(ctxt, doc);
4518 if (!xmlValidateRoot(ctxt, doc)) return(0);
4519
4520 root = xmlDocGetRootElement(doc);
4521 ret &= xmlValidateElement(ctxt, doc, root);
4522 ret &= xmlValidateDocumentFinal(ctxt, doc);
4523 return(ret);
4524}
4525
4526
4527/************************************************************************
4528 * *
4529 * Routines for dynamic validation editing *
4530 * *
4531 ************************************************************************/
4532
4533/**
4534 * xmlValidGetPotentialChildren:
4535 * @ctree: an element content tree
4536 * @list: an array to store the list of child names
4537 * @len: a pointer to the number of element in the list
4538 * @max: the size of the array
4539 *
4540 * Build/extend a list of potential children allowed by the content tree
4541 *
4542 * returns the number of element in the list, or -1 in case of error.
4543 */
4544
4545int
4546xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4547 int *len, int max) {
4548 int i;
4549
4550 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4551 return(-1);
4552 if (*len >= max) return(*len);
4553
4554 switch (ctree->type) {
4555 case XML_ELEMENT_CONTENT_PCDATA:
4556 for (i = 0; i < *len;i++)
4557 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4558 list[(*len)++] = BAD_CAST "#PCDATA";
4559 break;
4560 case XML_ELEMENT_CONTENT_ELEMENT:
4561 for (i = 0; i < *len;i++)
4562 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4563 list[(*len)++] = ctree->name;
4564 break;
4565 case XML_ELEMENT_CONTENT_SEQ:
4566 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4567 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4568 break;
4569 case XML_ELEMENT_CONTENT_OR:
4570 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4571 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4572 break;
4573 }
4574
4575 return(*len);
4576}
4577
4578/**
4579 * xmlValidGetValidElements:
4580 * @prev: an element to insert after
4581 * @next: an element to insert next
4582 * @list: an array to store the list of child names
4583 * @max: the size of the array
4584 *
4585 * This function returns the list of authorized children to insert
4586 * within an existing tree while respecting the validity constraints
4587 * forced by the Dtd. The insertion point is defined using @prev and
4588 * @next in the following ways:
4589 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4590 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4591 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4592 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4593 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4594 *
4595 * pointers to the element names are inserted at the beginning of the array
4596 * and do not need to be freed.
4597 *
4598 * returns the number of element in the list, or -1 in case of error. If
4599 * the function returns the value @max the caller is invited to grow the
4600 * receiving array and retry.
4601 */
4602
4603int
4604xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4605 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004606 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00004607 int nb_valid_elements = 0;
4608 const xmlChar *elements[256];
4609 int nb_elements = 0, i;
4610
4611 xmlNode *ref_node;
4612 xmlNode *parent;
4613 xmlNode *test_node;
4614
4615 xmlNode *prev_next;
4616 xmlNode *next_prev;
4617 xmlNode *parent_childs;
4618 xmlNode *parent_last;
4619
4620 xmlElement *element_desc;
4621
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004622 vctxt.userData = NULL;
4623 vctxt.error = NULL;
4624 vctxt.warning = NULL;
4625
Owen Taylor3473f882001-02-23 17:55:21 +00004626 if (prev == NULL && next == NULL)
4627 return(-1);
4628
4629 if (list == NULL) return(-1);
4630 if (max <= 0) return(-1);
4631
4632 nb_valid_elements = 0;
4633 ref_node = prev ? prev : next;
4634 parent = ref_node->parent;
4635
4636 /*
4637 * Retrieves the parent element declaration
4638 */
4639 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4640 parent->name);
4641 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4642 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4643 parent->name);
4644 if (element_desc == NULL) return(-1);
4645
4646 /*
4647 * Do a backup of the current tree structure
4648 */
4649 prev_next = prev ? prev->next : NULL;
4650 next_prev = next ? next->prev : NULL;
4651 parent_childs = parent->children;
4652 parent_last = parent->last;
4653
4654 /*
4655 * Creates a dummy node and insert it into the tree
4656 */
4657 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4658 test_node->doc = ref_node->doc;
4659 test_node->parent = parent;
4660 test_node->prev = prev;
4661 test_node->next = next;
4662
4663 if (prev) prev->next = test_node;
4664 else parent->children = test_node;
4665
4666 if (next) next->prev = test_node;
4667 else parent->last = test_node;
4668
4669 /*
4670 * Insert each potential child node and check if the parent is
4671 * still valid
4672 */
4673 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4674 elements, &nb_elements, 256);
4675
4676 for (i = 0;i < nb_elements;i++) {
4677 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004678 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004679 int j;
4680
4681 for (j = 0; j < nb_valid_elements;j++)
4682 if (xmlStrEqual(elements[i], list[j])) break;
4683 list[nb_valid_elements++] = elements[i];
4684 if (nb_valid_elements >= max) break;
4685 }
4686 }
4687
4688 /*
4689 * Restore the tree structure
4690 */
4691 if (prev) prev->next = prev_next;
4692 if (next) next->prev = next_prev;
4693 parent->children = parent_childs;
4694 parent->last = parent_last;
4695
4696 return(nb_valid_elements);
4697}