blob: 3f5f7308506555b7dfdc97072225f68a816a5d42 [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 }
3735 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003736 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003737 break;
3738 case XML_ELEMENT_NODE:
3739 /*
3740 * Allocate a new node and minimally fills in
3741 * what's required
3742 */
3743 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3744 if (tmp == NULL) {
3745 xmlGenericError(xmlGenericErrorContext,
3746 "xmlValidateElementContent : malloc failed\n");
3747 xmlFreeNodeList(repl);
3748 ret = -1;
3749 goto done;
3750 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003751 tmp->type = cur->type;
3752 tmp->name = cur->name;
3753 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003754 tmp->next = NULL;
3755 if (repl == NULL)
3756 repl = last = tmp;
3757 else {
3758 last->next = tmp;
3759 last = tmp;
3760 }
3761 break;
3762 default:
3763 break;
3764 }
3765 /*
3766 * Switch to next element
3767 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003768 cur = cur->next;
3769 while (cur == NULL) {
3770 cur = nodeVPop(ctxt);
3771 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003772 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003773 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003774 }
3775 }
3776
3777 /*
3778 * Relaunch the validation
3779 */
3780 ctxt->vstate = &ctxt->vstateTab[0];
3781 ctxt->vstateNr = 1;
3782 CONT = cont;
3783 NODE = repl;
3784 DEPTH = 0;
3785 OCCURS = 0;
3786 STATE = 0;
3787 ret = xmlValidateElementType(ctxt);
3788 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003789 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003790 char expr[5000];
3791 char list[5000];
3792
3793 expr[0] = 0;
3794 xmlSprintfElementContent(expr, cont, 1);
3795 list[0] = 0;
3796 if (repl != NULL)
3797 xmlSprintfElements(list, repl, 1);
3798 else
3799 xmlSprintfElements(list, child, 1);
3800
Daniel Veillard7533cc82001-04-24 15:52:00 +00003801 if (name != NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003802 VERROR(ctxt->userData,
3803 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
Daniel Veillard7533cc82001-04-24 15:52:00 +00003804 name, expr, list);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003805 } else {
3806 VERROR(ctxt->userData,
3807 "Element content doesn't follow the Dtd\nExpecting %s, got %s\n",
3808 expr, list);
3809 }
3810 ret = 0;
3811 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003812 if (ret == -3)
3813 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003814
3815
3816done:
3817 /*
3818 * Deallocate the copy if done, and free up the validation stack
3819 */
3820 while (repl != NULL) {
3821 tmp = repl->next;
3822 xmlFree(repl);
3823 repl = tmp;
3824 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003825 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003826 if (ctxt->vstateTab != NULL) {
3827 xmlFree(ctxt->vstateTab);
3828 ctxt->vstateTab = NULL;
3829 }
3830 ctxt->nodeMax = 0;
3831 if (ctxt->nodeTab != NULL) {
3832 xmlFree(ctxt->nodeTab);
3833 ctxt->nodeTab = NULL;
3834 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003835 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003836
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003837}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003838
Owen Taylor3473f882001-02-23 17:55:21 +00003839/**
Owen Taylor3473f882001-02-23 17:55:21 +00003840 * xmlValidateOneElement:
3841 * @ctxt: the validation context
3842 * @doc: a document instance
3843 * @elem: an element instance
3844 *
3845 * Try to validate a single element and it's attributes,
3846 * basically it does the following checks as described by the
3847 * XML-1.0 recommendation:
3848 * - [ VC: Element Valid ]
3849 * - [ VC: Required Attribute ]
3850 * Then call xmlValidateOneAttribute() for each attribute present.
3851 *
3852 * The ID/IDREF checkings are done separately
3853 *
3854 * returns 1 if valid or 0 otherwise
3855 */
3856
3857int
3858xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3859 xmlNodePtr elem) {
3860 xmlElementPtr elemDecl = NULL;
3861 xmlElementContentPtr cont;
3862 xmlAttributePtr attr;
3863 xmlNodePtr child;
3864 int ret = 1;
3865 const xmlChar *name;
3866
3867 CHECK_DTD;
3868
3869 if (elem == NULL) return(0);
3870 if (elem->type == XML_TEXT_NODE) {
3871 }
3872 switch (elem->type) {
3873 case XML_ATTRIBUTE_NODE:
3874 VERROR(ctxt->userData,
3875 "Attribute element not expected here\n");
3876 return(0);
3877 case XML_TEXT_NODE:
3878 if (elem->children != NULL) {
3879 VERROR(ctxt->userData, "Text element has childs !\n");
3880 return(0);
3881 }
3882 if (elem->properties != NULL) {
3883 VERROR(ctxt->userData, "Text element has attributes !\n");
3884 return(0);
3885 }
3886 if (elem->ns != NULL) {
3887 VERROR(ctxt->userData, "Text element has namespace !\n");
3888 return(0);
3889 }
3890 if (elem->nsDef != NULL) {
3891 VERROR(ctxt->userData,
3892 "Text element carries namespace definitions !\n");
3893 return(0);
3894 }
3895 if (elem->content == NULL) {
3896 VERROR(ctxt->userData,
3897 "Text element has no content !\n");
3898 return(0);
3899 }
3900 return(1);
3901 case XML_XINCLUDE_START:
3902 case XML_XINCLUDE_END:
3903 return(1);
3904 case XML_CDATA_SECTION_NODE:
3905 case XML_ENTITY_REF_NODE:
3906 case XML_PI_NODE:
3907 case XML_COMMENT_NODE:
3908 return(1);
3909 case XML_ENTITY_NODE:
3910 VERROR(ctxt->userData,
3911 "Entity element not expected here\n");
3912 return(0);
3913 case XML_NOTATION_NODE:
3914 VERROR(ctxt->userData,
3915 "Notation element not expected here\n");
3916 return(0);
3917 case XML_DOCUMENT_NODE:
3918 case XML_DOCUMENT_TYPE_NODE:
3919 case XML_DOCUMENT_FRAG_NODE:
3920 VERROR(ctxt->userData,
3921 "Document element not expected here\n");
3922 return(0);
3923 case XML_HTML_DOCUMENT_NODE:
3924 VERROR(ctxt->userData,
3925 "\n");
3926 return(0);
3927 case XML_ELEMENT_NODE:
3928 break;
3929 default:
3930 VERROR(ctxt->userData,
3931 "unknown element type %d\n", elem->type);
3932 return(0);
3933 }
3934 if (elem->name == NULL) return(0);
3935
3936 /*
3937 * Fetch the declaration for the qualified name
3938 */
3939 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3940 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3941 elem->name, elem->ns->prefix);
3942 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3943 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3944 elem->name, elem->ns->prefix);
3945 }
3946
3947 /*
3948 * Fetch the declaration for the non qualified name
3949 */
3950 if (elemDecl == NULL) {
3951 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3952 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3953 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3954 }
3955 if (elemDecl == NULL) {
3956 VERROR(ctxt->userData, "No declaration for element %s\n",
3957 elem->name);
3958 return(0);
3959 }
3960
3961 /* Check taht the element content matches the definition */
3962 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003963 case XML_ELEMENT_TYPE_UNDEFINED:
3964 VERROR(ctxt->userData, "No declaration for element %s\n",
3965 elem->name);
3966 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003967 case XML_ELEMENT_TYPE_EMPTY:
3968 if (elem->children != NULL) {
3969 VERROR(ctxt->userData,
3970 "Element %s was declared EMPTY this one has content\n",
3971 elem->name);
3972 ret = 0;
3973 }
3974 break;
3975 case XML_ELEMENT_TYPE_ANY:
3976 /* I don't think anything is required then */
3977 break;
3978 case XML_ELEMENT_TYPE_MIXED:
3979 /* Hum, this start to get messy */
3980 child = elem->children;
3981 while (child != NULL) {
3982 if (child->type == XML_ELEMENT_NODE) {
3983 name = child->name;
3984 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
3985 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003986 snprintf((char *) qname, sizeof(qname), "%s:%s",
3987 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003988 qname[sizeof(qname) - 1] = 0;
3989 cont = elemDecl->content;
3990 while (cont != NULL) {
3991 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3992 if (xmlStrEqual(cont->name, qname)) break;
3993 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3994 (cont->c1 != NULL) &&
3995 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
3996 if (xmlStrEqual(cont->c1->name, qname)) break;
3997 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3998 (cont->c1 == NULL) ||
3999 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4000 /* Internal error !!! */
4001 xmlGenericError(xmlGenericErrorContext,
4002 "Internal: MIXED struct bad\n");
4003 break;
4004 }
4005 cont = cont->c2;
4006 }
4007 if (cont != NULL)
4008 goto child_ok;
4009 }
4010 cont = elemDecl->content;
4011 while (cont != NULL) {
4012 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4013 if (xmlStrEqual(cont->name, name)) break;
4014 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4015 (cont->c1 != NULL) &&
4016 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4017 if (xmlStrEqual(cont->c1->name, name)) break;
4018 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4019 (cont->c1 == NULL) ||
4020 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4021 /* Internal error !!! */
4022 xmlGenericError(xmlGenericErrorContext,
4023 "Internal: MIXED struct bad\n");
4024 break;
4025 }
4026 cont = cont->c2;
4027 }
4028 if (cont == NULL) {
4029 VERROR(ctxt->userData,
4030 "Element %s is not declared in %s list of possible childs\n",
4031 name, elem->name);
4032 ret = 0;
4033 }
4034 }
4035child_ok:
4036 child = child->next;
4037 }
4038 break;
4039 case XML_ELEMENT_TYPE_ELEMENT:
4040 child = elem->children;
4041 cont = elemDecl->content;
Daniel Veillard7533cc82001-04-24 15:52:00 +00004042 ret = xmlValidateElementContent(ctxt, child, cont, 1, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004043 break;
4044 }
4045
4046 /* [ VC: Required Attribute ] */
4047 attr = elemDecl->attributes;
4048 while (attr != NULL) {
4049 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4050 xmlAttrPtr attrib;
4051 int qualified = -1;
4052
4053 attrib = elem->properties;
4054 while (attrib != NULL) {
4055 if (xmlStrEqual(attrib->name, attr->name)) {
4056 if (attr->prefix != NULL) {
4057 xmlNsPtr nameSpace = attrib->ns;
4058
4059 if (nameSpace == NULL)
4060 nameSpace = elem->ns;
4061 /*
4062 * qualified names handling is problematic, having a
4063 * different prefix should be possible but DTDs don't
4064 * allow to define the URI instead of the prefix :-(
4065 */
4066 if (nameSpace == NULL) {
4067 if (qualified < 0)
4068 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004069 } else if (!xmlStrEqual(nameSpace->prefix,
4070 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004071 if (qualified < 1)
4072 qualified = 1;
4073 } else
4074 goto found;
4075 } else {
4076 /*
4077 * We should allow applications to define namespaces
4078 * for their application even if the DTD doesn't
4079 * carry one, otherwise, basically we would always
4080 * break.
4081 */
4082 goto found;
4083 }
4084 }
4085 attrib = attrib->next;
4086 }
4087 if (qualified == -1) {
4088 if (attr->prefix == NULL) {
4089 VERROR(ctxt->userData,
4090 "Element %s doesn't carry attribute %s\n",
4091 elem->name, attr->name);
4092 ret = 0;
4093 } else {
4094 VERROR(ctxt->userData,
4095 "Element %s doesn't carry attribute %s:%s\n",
4096 elem->name, attr->prefix,attr->name);
4097 ret = 0;
4098 }
4099 } else if (qualified == 0) {
4100 VWARNING(ctxt->userData,
4101 "Element %s required attribute %s:%s has no prefix\n",
4102 elem->name, attr->prefix,attr->name);
4103 } else if (qualified == 1) {
4104 VWARNING(ctxt->userData,
4105 "Element %s required attribute %s:%s has different prefix\n",
4106 elem->name, attr->prefix,attr->name);
4107 }
4108 }
4109found:
4110 attr = attr->nexth;
4111 }
4112 return(ret);
4113}
4114
4115/**
4116 * xmlValidateRoot:
4117 * @ctxt: the validation context
4118 * @doc: a document instance
4119 *
4120 * Try to validate a the root element
4121 * basically it does the following check as described by the
4122 * XML-1.0 recommendation:
4123 * - [ VC: Root Element Type ]
4124 * it doesn't try to recurse or apply other check to the element
4125 *
4126 * returns 1 if valid or 0 otherwise
4127 */
4128
4129int
4130xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4131 xmlNodePtr root;
4132 if (doc == NULL) return(0);
4133
4134 root = xmlDocGetRootElement(doc);
4135 if ((root == NULL) || (root->name == NULL)) {
4136 VERROR(ctxt->userData, "Not valid: no root element\n");
4137 return(0);
4138 }
4139
4140 /*
4141 * When doing post validation against a separate DTD, those may
4142 * no internal subset has been generated
4143 */
4144 if ((doc->intSubset != NULL) &&
4145 (doc->intSubset->name != NULL)) {
4146 /*
4147 * Check first the document root against the NQName
4148 */
4149 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4150 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4151 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004152 snprintf((char *) qname, sizeof(qname), "%s:%s",
4153 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004154 qname[sizeof(qname) - 1] = 0;
4155 if (xmlStrEqual(doc->intSubset->name, qname))
4156 goto name_ok;
4157 }
4158 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4159 (xmlStrEqual(root->name, BAD_CAST "html")))
4160 goto name_ok;
4161 VERROR(ctxt->userData,
4162 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4163 root->name, doc->intSubset->name);
4164 return(0);
4165
4166 }
4167 }
4168name_ok:
4169 return(1);
4170}
4171
4172
4173/**
4174 * xmlValidateElement:
4175 * @ctxt: the validation context
4176 * @doc: a document instance
4177 * @elem: an element instance
4178 *
4179 * Try to validate the subtree under an element
4180 *
4181 * returns 1 if valid or 0 otherwise
4182 */
4183
4184int
4185xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4186 xmlNodePtr child;
4187 xmlAttrPtr attr;
4188 xmlChar *value;
4189 int ret = 1;
4190
4191 if (elem == NULL) return(0);
4192
4193 /*
4194 * XInclude elements were added after parsing in the infoset,
4195 * they don't really mean anything validation wise.
4196 */
4197 if ((elem->type == XML_XINCLUDE_START) ||
4198 (elem->type == XML_XINCLUDE_END))
4199 return(1);
4200
4201 CHECK_DTD;
4202
4203 ret &= xmlValidateOneElement(ctxt, doc, elem);
4204 attr = elem->properties;
4205 while(attr != NULL) {
4206 value = xmlNodeListGetString(doc, attr->children, 0);
4207 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4208 if (value != NULL)
4209 xmlFree(value);
4210 attr= attr->next;
4211 }
4212 child = elem->children;
4213 while (child != NULL) {
4214 ret &= xmlValidateElement(ctxt, doc, child);
4215 child = child->next;
4216 }
4217
4218 return(ret);
4219}
4220
Daniel Veillard8730c562001-02-26 10:49:57 +00004221/**
4222 * xmlValidateRef:
4223 * @ref: A reference to be validated
4224 * @ctxt: Validation context
4225 * @name: Name of ID we are searching for
4226 *
4227 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004228static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004229xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004230 const xmlChar *name) {
4231 xmlAttrPtr id;
4232 xmlAttrPtr attr;
4233
4234 if (ref == NULL)
4235 return;
4236 attr = ref->attr;
4237 if (attr == NULL)
4238 return;
4239 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4240 id = xmlGetID(ctxt->doc, name);
4241 if (id == NULL) {
4242 VERROR(ctxt->userData,
4243 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4244 attr->name, name);
4245 ctxt->valid = 0;
4246 }
4247 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4248 xmlChar *dup, *str = NULL, *cur, save;
4249
4250 dup = xmlStrdup(name);
4251 if (dup == NULL) {
4252 ctxt->valid = 0;
4253 return;
4254 }
4255 cur = dup;
4256 while (*cur != 0) {
4257 str = cur;
4258 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4259 save = *cur;
4260 *cur = 0;
4261 id = xmlGetID(ctxt->doc, str);
4262 if (id == NULL) {
4263 VERROR(ctxt->userData,
4264 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4265 attr->name, str);
4266 ctxt->valid = 0;
4267 }
4268 if (save == 0)
4269 break;
4270 *cur = save;
4271 while (IS_BLANK(*cur)) cur++;
4272 }
4273 xmlFree(dup);
4274 }
4275}
4276
4277/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004278 * xmlWalkValidateList:
4279 * @data: Contents of current link
4280 * @user: Value supplied by the user
4281 *
4282 * Return 0 to abort the walk or 1 to continue
4283 */
4284static int
4285xmlWalkValidateList(const void *data, const void *user)
4286{
4287 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4288 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4289 return 1;
4290}
4291
4292/**
4293 * xmlValidateCheckRefCallback:
4294 * @ref_list: List of references
4295 * @ctxt: Validation context
4296 * @name: Name of ID we are searching for
4297 *
4298 */
4299static void
4300xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4301 const xmlChar *name) {
4302 xmlValidateMemo memo;
4303
4304 if (ref_list == NULL)
4305 return;
4306 memo.ctxt = ctxt;
4307 memo.name = name;
4308
4309 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4310
4311}
4312
4313/**
Owen Taylor3473f882001-02-23 17:55:21 +00004314 * xmlValidateDocumentFinal:
4315 * @ctxt: the validation context
4316 * @doc: a document instance
4317 *
4318 * Does the final step for the document validation once all the
4319 * incremental validation steps have been completed
4320 *
4321 * basically it does the following checks described by the XML Rec
4322 *
4323 *
4324 * returns 1 if valid or 0 otherwise
4325 */
4326
4327int
4328xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4329 xmlRefTablePtr table;
4330
4331 if (doc == NULL) {
4332 xmlGenericError(xmlGenericErrorContext,
4333 "xmlValidateDocumentFinal: doc == NULL\n");
4334 return(0);
4335 }
4336
4337 /*
4338 * Check all the NOTATION/NOTATIONS attributes
4339 */
4340 /*
4341 * Check all the ENTITY/ENTITIES attributes definition for validity
4342 */
4343 /*
4344 * Check all the IDREF/IDREFS attributes definition for validity
4345 */
4346 table = (xmlRefTablePtr) doc->refs;
4347 ctxt->doc = doc;
4348 ctxt->valid = 1;
4349 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4350 return(ctxt->valid);
4351}
4352
4353/**
4354 * xmlValidateDtd:
4355 * @ctxt: the validation context
4356 * @doc: a document instance
4357 * @dtd: a dtd instance
4358 *
4359 * Try to validate the document against the dtd instance
4360 *
4361 * basically it does check all the definitions in the DtD.
4362 *
4363 * returns 1 if valid or 0 otherwise
4364 */
4365
4366int
4367xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4368 int ret;
4369 xmlDtdPtr oldExt;
4370 xmlNodePtr root;
4371
4372 if (dtd == NULL) return(0);
4373 if (doc == NULL) return(0);
4374 oldExt = doc->extSubset;
4375 doc->extSubset = dtd;
4376 ret = xmlValidateRoot(ctxt, doc);
4377 if (ret == 0) {
4378 doc->extSubset = oldExt;
4379 return(ret);
4380 }
4381 if (doc->ids != NULL) {
4382 xmlFreeIDTable(doc->ids);
4383 doc->ids = NULL;
4384 }
4385 if (doc->refs != NULL) {
4386 xmlFreeRefTable(doc->refs);
4387 doc->refs = NULL;
4388 }
4389 root = xmlDocGetRootElement(doc);
4390 ret = xmlValidateElement(ctxt, doc, root);
4391 ret &= xmlValidateDocumentFinal(ctxt, doc);
4392 doc->extSubset = oldExt;
4393 return(ret);
4394}
4395
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004396static void
Owen Taylor3473f882001-02-23 17:55:21 +00004397xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004398 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004399 if (cur == NULL)
4400 return;
4401 switch (cur->atype) {
4402 case XML_ATTRIBUTE_CDATA:
4403 case XML_ATTRIBUTE_ID:
4404 case XML_ATTRIBUTE_IDREF :
4405 case XML_ATTRIBUTE_IDREFS:
4406 case XML_ATTRIBUTE_NMTOKEN:
4407 case XML_ATTRIBUTE_NMTOKENS:
4408 case XML_ATTRIBUTE_ENUMERATION:
4409 break;
4410 case XML_ATTRIBUTE_ENTITY:
4411 case XML_ATTRIBUTE_ENTITIES:
4412 case XML_ATTRIBUTE_NOTATION:
4413 if (cur->defaultValue != NULL) {
4414 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4415 cur->name, cur->atype, cur->defaultValue);
4416 }
4417 if (cur->tree != NULL) {
4418 xmlEnumerationPtr tree = cur->tree;
4419 while (tree != NULL) {
4420 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4421 cur->name, cur->atype, tree->name);
4422 tree = tree->next;
4423 }
4424 }
4425 }
4426}
4427
4428/**
4429 * xmlValidateDtdFinal:
4430 * @ctxt: the validation context
4431 * @doc: a document instance
4432 *
4433 * Does the final step for the dtds validation once all the
4434 * subsets have been parsed
4435 *
4436 * basically it does the following checks described by the XML Rec
4437 * - check that ENTITY and ENTITIES type attributes default or
4438 * possible values matches one of the defined entities.
4439 * - check that NOTATION type attributes default or
4440 * possible values matches one of the defined notations.
4441 *
4442 * returns 1 if valid or 0 otherwise
4443 */
4444
4445int
4446xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4447 int ret = 1;
4448 xmlDtdPtr dtd;
4449 xmlAttributeTablePtr table;
4450
4451 if (doc == NULL) return(0);
4452 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4453 return(0);
4454 ctxt->doc = doc;
4455 ctxt->valid = ret;
4456 dtd = doc->intSubset;
4457 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4458 table = (xmlAttributeTablePtr) dtd->attributes;
4459 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4460 }
4461 dtd = doc->extSubset;
4462 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4463 table = (xmlAttributeTablePtr) dtd->attributes;
4464 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4465 }
4466 return(ctxt->valid);
4467}
4468
4469/**
4470 * xmlValidateDocument:
4471 * @ctxt: the validation context
4472 * @doc: a document instance
4473 *
4474 * Try to validate the document instance
4475 *
4476 * basically it does the all the checks described by the XML Rec
4477 * i.e. validates the internal and external subset (if present)
4478 * and validate the document tree.
4479 *
4480 * returns 1 if valid or 0 otherwise
4481 */
4482
4483int
4484xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4485 int ret;
4486 xmlNodePtr root;
4487
4488 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4489 return(0);
4490 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4491 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4492 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4493 doc->intSubset->SystemID);
4494 if (doc->extSubset == NULL) {
4495 if (doc->intSubset->SystemID != NULL) {
4496 VERROR(ctxt->userData,
4497 "Could not load the external subset \"%s\"\n",
4498 doc->intSubset->SystemID);
4499 } else {
4500 VERROR(ctxt->userData,
4501 "Could not load the external subset \"%s\"\n",
4502 doc->intSubset->ExternalID);
4503 }
4504 return(0);
4505 }
4506 }
4507
4508 if (doc->ids != NULL) {
4509 xmlFreeIDTable(doc->ids);
4510 doc->ids = NULL;
4511 }
4512 if (doc->refs != NULL) {
4513 xmlFreeRefTable(doc->refs);
4514 doc->refs = NULL;
4515 }
4516 ret = xmlValidateDtdFinal(ctxt, doc);
4517 if (!xmlValidateRoot(ctxt, doc)) return(0);
4518
4519 root = xmlDocGetRootElement(doc);
4520 ret &= xmlValidateElement(ctxt, doc, root);
4521 ret &= xmlValidateDocumentFinal(ctxt, doc);
4522 return(ret);
4523}
4524
4525
4526/************************************************************************
4527 * *
4528 * Routines for dynamic validation editing *
4529 * *
4530 ************************************************************************/
4531
4532/**
4533 * xmlValidGetPotentialChildren:
4534 * @ctree: an element content tree
4535 * @list: an array to store the list of child names
4536 * @len: a pointer to the number of element in the list
4537 * @max: the size of the array
4538 *
4539 * Build/extend a list of potential children allowed by the content tree
4540 *
4541 * returns the number of element in the list, or -1 in case of error.
4542 */
4543
4544int
4545xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4546 int *len, int max) {
4547 int i;
4548
4549 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4550 return(-1);
4551 if (*len >= max) return(*len);
4552
4553 switch (ctree->type) {
4554 case XML_ELEMENT_CONTENT_PCDATA:
4555 for (i = 0; i < *len;i++)
4556 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4557 list[(*len)++] = BAD_CAST "#PCDATA";
4558 break;
4559 case XML_ELEMENT_CONTENT_ELEMENT:
4560 for (i = 0; i < *len;i++)
4561 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4562 list[(*len)++] = ctree->name;
4563 break;
4564 case XML_ELEMENT_CONTENT_SEQ:
4565 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4566 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4567 break;
4568 case XML_ELEMENT_CONTENT_OR:
4569 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4570 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4571 break;
4572 }
4573
4574 return(*len);
4575}
4576
4577/**
4578 * xmlValidGetValidElements:
4579 * @prev: an element to insert after
4580 * @next: an element to insert next
4581 * @list: an array to store the list of child names
4582 * @max: the size of the array
4583 *
4584 * This function returns the list of authorized children to insert
4585 * within an existing tree while respecting the validity constraints
4586 * forced by the Dtd. The insertion point is defined using @prev and
4587 * @next in the following ways:
4588 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4589 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4590 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4591 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4592 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4593 *
4594 * pointers to the element names are inserted at the beginning of the array
4595 * and do not need to be freed.
4596 *
4597 * returns the number of element in the list, or -1 in case of error. If
4598 * the function returns the value @max the caller is invited to grow the
4599 * receiving array and retry.
4600 */
4601
4602int
4603xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4604 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004605 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00004606 int nb_valid_elements = 0;
4607 const xmlChar *elements[256];
4608 int nb_elements = 0, i;
4609
4610 xmlNode *ref_node;
4611 xmlNode *parent;
4612 xmlNode *test_node;
4613
4614 xmlNode *prev_next;
4615 xmlNode *next_prev;
4616 xmlNode *parent_childs;
4617 xmlNode *parent_last;
4618
4619 xmlElement *element_desc;
4620
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004621 vctxt.userData = NULL;
4622 vctxt.error = NULL;
4623 vctxt.warning = NULL;
4624
Owen Taylor3473f882001-02-23 17:55:21 +00004625 if (prev == NULL && next == NULL)
4626 return(-1);
4627
4628 if (list == NULL) return(-1);
4629 if (max <= 0) return(-1);
4630
4631 nb_valid_elements = 0;
4632 ref_node = prev ? prev : next;
4633 parent = ref_node->parent;
4634
4635 /*
4636 * Retrieves the parent element declaration
4637 */
4638 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4639 parent->name);
4640 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4641 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4642 parent->name);
4643 if (element_desc == NULL) return(-1);
4644
4645 /*
4646 * Do a backup of the current tree structure
4647 */
4648 prev_next = prev ? prev->next : NULL;
4649 next_prev = next ? next->prev : NULL;
4650 parent_childs = parent->children;
4651 parent_last = parent->last;
4652
4653 /*
4654 * Creates a dummy node and insert it into the tree
4655 */
4656 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4657 test_node->doc = ref_node->doc;
4658 test_node->parent = parent;
4659 test_node->prev = prev;
4660 test_node->next = next;
4661
4662 if (prev) prev->next = test_node;
4663 else parent->children = test_node;
4664
4665 if (next) next->prev = test_node;
4666 else parent->last = test_node;
4667
4668 /*
4669 * Insert each potential child node and check if the parent is
4670 * still valid
4671 */
4672 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4673 elements, &nb_elements, 256);
4674
4675 for (i = 0;i < nb_elements;i++) {
4676 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004677 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004678 int j;
4679
4680 for (j = 0; j < nb_valid_elements;j++)
4681 if (xmlStrEqual(elements[i], list[j])) break;
4682 list[nb_valid_elements++] = elements[i];
4683 if (nb_valid_elements >= max) break;
4684 }
4685 }
4686
4687 /*
4688 * Restore the tree structure
4689 */
4690 if (prev) prev->next = prev_next;
4691 if (next) next->prev = next_prev;
4692 parent->children = parent_childs;
4693 parent->last = parent_last;
4694
4695 return(nb_valid_elements);
4696}