blob: adab5afc9327fc00a03b2dad2350ce15f6f22d2a [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * valid.c : part of the code use to do the DTD handling and the validity
3 * checking
4 *
5 * See Copyright for the status of this software.
6 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00007 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00008 */
9
Daniel Veillard34ce8be2002-03-18 19:37:11 +000010#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000011#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000012
Owen Taylor3473f882001-02-23 17:55:21 +000013#include <string.h>
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18
19#include <libxml/xmlmemory.h>
20#include <libxml/hash.h>
21#include <libxml/valid.h>
22#include <libxml/parser.h>
23#include <libxml/parserInternals.h>
24#include <libxml/xmlerror.h>
25#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000026#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000027
Daniel Veillarde62d36c2001-05-15 08:53:16 +000028/* #define DEBUG_VALID_ALGO */
29
Owen Taylor3473f882001-02-23 17:55:21 +000030/*
31 * Generic function for accessing stacks in the Validity Context
32 */
33
34#define PUSH_AND_POP(scope, type, name) \
35scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000036 if (ctxt->name##Max <= 0) { \
37 ctxt->name##Max = 4; \
38 ctxt->name##Tab = (type *) xmlMalloc( \
39 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
40 if (ctxt->name##Tab == NULL) { \
41 xmlGenericError(xmlGenericErrorContext, \
42 "malloc failed !\n"); \
Daniel Veillarda9142e72001-06-19 11:07:54 +000043 ctxt->name##Max = 0; \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000044 return(0); \
45 } \
46 } \
Owen Taylor3473f882001-02-23 17:55:21 +000047 if (ctxt->name##Nr >= ctxt->name##Max) { \
48 ctxt->name##Max *= 2; \
49 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
50 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
51 if (ctxt->name##Tab == NULL) { \
52 xmlGenericError(xmlGenericErrorContext, \
53 "realloc failed !\n"); \
54 return(0); \
55 } \
56 } \
57 ctxt->name##Tab[ctxt->name##Nr] = value; \
58 ctxt->name = value; \
59 return(ctxt->name##Nr++); \
60} \
61scope type name##VPop(xmlValidCtxtPtr ctxt) { \
62 type ret; \
63 if (ctxt->name##Nr <= 0) return(0); \
64 ctxt->name##Nr--; \
65 if (ctxt->name##Nr > 0) \
66 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
67 else \
68 ctxt->name = NULL; \
69 ret = ctxt->name##Tab[ctxt->name##Nr]; \
70 ctxt->name##Tab[ctxt->name##Nr] = 0; \
71 return(ret); \
72} \
73
Daniel Veillarddab4cb32001-04-20 13:03:48 +000074/*
Daniel Veillardb44025c2001-10-11 22:55:55 +000075 * I use a home made algorithm less complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +000076 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000077 * only restriction is on the deepness of the tree limited by the
78 * size of the occurs bitfield
79 *
80 * this is the content of a saved state for rollbacks
81 */
82
83#define ROLLBACK_OR 0
84#define ROLLBACK_PARENT 1
85
Daniel Veillardb44025c2001-10-11 22:55:55 +000086typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +000087 xmlElementContentPtr cont; /* pointer to the content model subtree */
88 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000089 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000090 unsigned char depth; /* current depth in the overall tree */
91 unsigned char state; /* ROLLBACK_XXX */
92} _xmlValidState;
93
Daniel Veillardfc57b412002-04-29 15:50:14 +000094#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +000095#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
96#define CONT ctxt->vstate->cont
97#define NODE ctxt->vstate->node
98#define DEPTH ctxt->vstate->depth
99#define OCCURS ctxt->vstate->occurs
100#define STATE ctxt->vstate->state
101
Daniel Veillard5344c602001-12-31 16:37:34 +0000102#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
103#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000104
Daniel Veillard5344c602001-12-31 16:37:34 +0000105#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
106#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000107
108static int
109vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
110 xmlNodePtr node, unsigned char depth, long occurs,
111 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000112 int i = ctxt->vstateNr - 1;
113
Daniel Veillard940492d2002-04-15 10:15:25 +0000114 if (ctxt->vstateNr > MAX_RECURSE) {
115 return(-1);
116 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000117 if (ctxt->vstateNr >= ctxt->vstateMax) {
118 ctxt->vstateMax *= 2;
119 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
120 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
121 if (ctxt->vstateTab == NULL) {
122 xmlGenericError(xmlGenericErrorContext,
123 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000124 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000125 }
Daniel Veillard06803992001-04-22 10:35:56 +0000126 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000127 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000128 /*
129 * Don't push on the stack a state already here
130 */
131 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
132 (ctxt->vstateTab[i].node == node) &&
133 (ctxt->vstateTab[i].depth == depth) &&
134 (ctxt->vstateTab[i].occurs == occurs) &&
135 (ctxt->vstateTab[i].state == state))
136 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000137 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
138 ctxt->vstateTab[ctxt->vstateNr].node = node;
139 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
140 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
141 ctxt->vstateTab[ctxt->vstateNr].state = state;
142 return(ctxt->vstateNr++);
143}
144
145static int
146vstateVPop(xmlValidCtxtPtr ctxt) {
147 if (ctxt->vstateNr <= 1) return(-1);
148 ctxt->vstateNr--;
149 ctxt->vstate = &ctxt->vstateTab[0];
150 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
151 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
152 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
153 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
154 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
155 return(ctxt->vstateNr);
156}
157
Owen Taylor3473f882001-02-23 17:55:21 +0000158PUSH_AND_POP(static, xmlNodePtr, node)
159
Owen Taylor3473f882001-02-23 17:55:21 +0000160#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000161static void
162xmlValidPrintNode(xmlNodePtr cur) {
163 if (cur == NULL) {
164 xmlGenericError(xmlGenericErrorContext, "null");
165 return;
166 }
167 switch (cur->type) {
168 case XML_ELEMENT_NODE:
169 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
170 break;
171 case XML_TEXT_NODE:
172 xmlGenericError(xmlGenericErrorContext, "text ");
173 break;
174 case XML_CDATA_SECTION_NODE:
175 xmlGenericError(xmlGenericErrorContext, "cdata ");
176 break;
177 case XML_ENTITY_REF_NODE:
178 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
179 break;
180 case XML_PI_NODE:
181 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
182 break;
183 case XML_COMMENT_NODE:
184 xmlGenericError(xmlGenericErrorContext, "comment ");
185 break;
186 case XML_ATTRIBUTE_NODE:
187 xmlGenericError(xmlGenericErrorContext, "?attr? ");
188 break;
189 case XML_ENTITY_NODE:
190 xmlGenericError(xmlGenericErrorContext, "?ent? ");
191 break;
192 case XML_DOCUMENT_NODE:
193 xmlGenericError(xmlGenericErrorContext, "?doc? ");
194 break;
195 case XML_DOCUMENT_TYPE_NODE:
196 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
197 break;
198 case XML_DOCUMENT_FRAG_NODE:
199 xmlGenericError(xmlGenericErrorContext, "?frag? ");
200 break;
201 case XML_NOTATION_NODE:
202 xmlGenericError(xmlGenericErrorContext, "?nota? ");
203 break;
204 case XML_HTML_DOCUMENT_NODE:
205 xmlGenericError(xmlGenericErrorContext, "?html? ");
206 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000207#ifdef LIBXML_DOCB_ENABLED
208 case XML_DOCB_DOCUMENT_NODE:
209 xmlGenericError(xmlGenericErrorContext, "?docb? ");
210 break;
211#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000212 case XML_DTD_NODE:
213 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
214 break;
215 case XML_ELEMENT_DECL:
216 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
217 break;
218 case XML_ATTRIBUTE_DECL:
219 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
220 break;
221 case XML_ENTITY_DECL:
222 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
223 break;
224 case XML_NAMESPACE_DECL:
225 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
226 break;
227 case XML_XINCLUDE_START:
228 xmlGenericError(xmlGenericErrorContext, "incstart ");
229 break;
230 case XML_XINCLUDE_END:
231 xmlGenericError(xmlGenericErrorContext, "incend ");
232 break;
233 }
234}
235
236static void
237xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000238 if (cur == NULL)
239 xmlGenericError(xmlGenericErrorContext, "null ");
240 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000241 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000242 cur = cur->next;
243 }
244}
245
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000246static void
247xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000248 char expr[1000];
249
250 expr[0] = 0;
251 xmlGenericError(xmlGenericErrorContext, "valid: ");
252 xmlValidPrintNodeList(cur);
253 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000254 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000255 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
256}
257
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000258static void
259xmlValidDebugState(xmlValidStatePtr state) {
260 xmlGenericError(xmlGenericErrorContext, "(");
261 if (state->cont == NULL)
262 xmlGenericError(xmlGenericErrorContext, "null,");
263 else
264 switch (state->cont->type) {
265 case XML_ELEMENT_CONTENT_PCDATA:
266 xmlGenericError(xmlGenericErrorContext, "pcdata,");
267 break;
268 case XML_ELEMENT_CONTENT_ELEMENT:
269 xmlGenericError(xmlGenericErrorContext, "%s,",
270 state->cont->name);
271 break;
272 case XML_ELEMENT_CONTENT_SEQ:
273 xmlGenericError(xmlGenericErrorContext, "seq,");
274 break;
275 case XML_ELEMENT_CONTENT_OR:
276 xmlGenericError(xmlGenericErrorContext, "or,");
277 break;
278 }
279 xmlValidPrintNode(state->node);
280 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
281 state->depth, state->occurs, state->state);
282}
283
284static void
285xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
286 int i, j;
287
288 xmlGenericError(xmlGenericErrorContext, "state: ");
289 xmlValidDebugState(ctxt->vstate);
290 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
291 ctxt->vstateNr - 1);
292 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
293 xmlValidDebugState(&ctxt->vstateTab[j]);
294 xmlGenericError(xmlGenericErrorContext, "\n");
295}
296
297/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000298#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000299 *****/
300
301#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000302#define DEBUG_VALID_MSG(m) \
303 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
304
Owen Taylor3473f882001-02-23 17:55:21 +0000305#else
306#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000307#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000308#endif
309
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000310/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000311
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000312#define VECTXT(ctxt, node) \
313 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000314 (node != NULL)) { \
315 xmlChar *base = xmlNodeGetBase(NULL,node); \
316 if (base != NULL) { \
317 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000318 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000319 xmlFree(base); \
320 } else \
321 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000322 (int) node->content); \
323 }
324
325#define VWCTXT(ctxt, node) \
326 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000327 (node != NULL)) { \
328 xmlChar *base = xmlNodeGetBase(NULL,node); \
329 if (base != NULL) { \
330 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000331 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000332 xmlFree(base); \
333 } else \
334 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000335 (int) node->content); \
336 }
337
Owen Taylor3473f882001-02-23 17:55:21 +0000338#define VERROR \
339 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
340
341#define VWARNING \
342 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
343
344#define CHECK_DTD \
345 if (doc == NULL) return(0); \
346 else if ((doc->intSubset == NULL) && \
347 (doc->extSubset == NULL)) return(0)
348
Daniel Veillarda10efa82001-04-18 13:09:01 +0000349static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
350 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000351xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
352
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000353#ifdef LIBXML_REGEXP_ENABLED
354
355/************************************************************************
356 * *
357 * Content model validation based on the regexps *
358 * *
359 ************************************************************************/
360
361/**
362 * xmlValidBuildAContentModel:
363 * @content: the content model
364 * @ctxt: the schema parser context
365 * @name: the element name whose content is being built
366 *
367 * Generate the automata sequence needed for that type
368 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000369 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000370 */
371static int
372xmlValidBuildAContentModel(xmlElementContentPtr content,
373 xmlValidCtxtPtr ctxt,
374 const xmlChar *name) {
375 if (content == NULL) {
376 VERROR(ctxt->userData,
377 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000378 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000379 }
380 switch (content->type) {
381 case XML_ELEMENT_CONTENT_PCDATA:
382 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
383 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000384 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000385 break;
386 case XML_ELEMENT_CONTENT_ELEMENT: {
387 xmlAutomataStatePtr oldstate = ctxt->state;
388 switch (content->ocur) {
389 case XML_ELEMENT_CONTENT_ONCE:
390 ctxt->state = xmlAutomataNewTransition(ctxt->am,
391 ctxt->state, NULL, content->name, NULL);
392 break;
393 case XML_ELEMENT_CONTENT_OPT:
394 ctxt->state = xmlAutomataNewTransition(ctxt->am,
395 ctxt->state, NULL, content->name, NULL);
396 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
397 break;
398 case XML_ELEMENT_CONTENT_PLUS:
399 ctxt->state = xmlAutomataNewTransition(ctxt->am,
400 ctxt->state, NULL, content->name, NULL);
401 xmlAutomataNewTransition(ctxt->am, ctxt->state,
402 ctxt->state, content->name, NULL);
403 break;
404 case XML_ELEMENT_CONTENT_MULT:
405 xmlAutomataNewTransition(ctxt->am, ctxt->state,
406 ctxt->state, content->name, NULL);
407 break;
408 }
409 break;
410 }
411 case XML_ELEMENT_CONTENT_SEQ: {
412 xmlAutomataStatePtr oldstate;
413 xmlElementContentOccur ocur;
414
415 /*
416 * Simply iterate over the content
417 */
418 oldstate = ctxt->state;
419 ocur = content->ocur;
420 while (content->type == XML_ELEMENT_CONTENT_SEQ) {
421 xmlValidBuildAContentModel(content->c1, ctxt, name);
422 content = content->c2;
423 }
424 xmlValidBuildAContentModel(content->c2, ctxt, name);
425 switch (ocur) {
426 case XML_ELEMENT_CONTENT_ONCE:
427 break;
428 case XML_ELEMENT_CONTENT_OPT:
429 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
430 break;
431 case XML_ELEMENT_CONTENT_MULT:
432 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
433 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate);
434 break;
435 case XML_ELEMENT_CONTENT_PLUS:
436 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate);
437 break;
438 }
439 break;
440 }
441 case XML_ELEMENT_CONTENT_OR: {
442 xmlAutomataStatePtr start, end;
443 xmlElementContentOccur ocur;
444
445 start = ctxt->state;
446 end = xmlAutomataNewState(ctxt->am);
447 ocur = content->ocur;
448
449 /*
450 * iterate over the subtypes and remerge the end with an
451 * epsilon transition
452 */
453 while (content->type == XML_ELEMENT_CONTENT_OR) {
454 ctxt->state = start;
455 xmlValidBuildAContentModel(content->c1, ctxt, name);
456 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, end);
457 content = content->c2;
458 }
459 ctxt->state = start;
460 xmlValidBuildAContentModel(content->c1, ctxt, name);
461 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, end);
462 ctxt->state = end;
463 switch (ocur) {
464 case XML_ELEMENT_CONTENT_ONCE:
465 break;
466 case XML_ELEMENT_CONTENT_OPT:
467 xmlAutomataNewEpsilon(ctxt->am, start, ctxt->state);
468 break;
469 case XML_ELEMENT_CONTENT_MULT:
470 xmlAutomataNewEpsilon(ctxt->am, start, ctxt->state);
471 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, start);
472 break;
473 case XML_ELEMENT_CONTENT_PLUS:
474 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, start);
475 break;
476 }
477 break;
478 }
479 default:
480 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
481 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000482 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000483 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000484 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000485}
486/**
487 * xmlValidBuildContentModel:
488 * @ctxt: a validation context
489 * @elem: an element declaration node
490 *
491 * (Re)Build the automata associated to the content model of this
492 * element
493 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000494 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000495 */
496int
497xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
498 xmlAutomataStatePtr start;
499
500 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000501 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000502 if (elem->type != XML_ELEMENT_DECL)
503 return(0);
504 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
505 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000506 /* TODO: should we rebuild in this case ? */
507 if (elem->contModel != NULL)
Daniel Veillard84d70a42002-09-16 10:51:38 +0000508 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000509
510 ctxt->am = xmlNewAutomata();
511 if (ctxt->am == NULL) {
512 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
513 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000514 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000515 }
516 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
517 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
518 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000519 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000520 if (!xmlAutomataIsDeterminist(ctxt->am)) {
521 VERROR(ctxt->userData, "Content model of %s is not determinist:\n",
522 elem->name);
523 ctxt->valid = 0;
524 }
525 ctxt->state = NULL;
526 xmlFreeAutomata(ctxt->am);
527 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000528 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000529}
530
531#endif /* LIBXML_REGEXP_ENABLED */
532
Owen Taylor3473f882001-02-23 17:55:21 +0000533/************************************************************************
534 * *
535 * QName handling helper *
536 * *
537 ************************************************************************/
538
539/**
540 * xmlSplitQName2:
541 * @name: an XML parser context
542 * @prefix: a xmlChar **
543 *
544 * parse an XML qualified name string
545 *
546 * [NS 5] QName ::= (Prefix ':')? LocalPart
547 *
548 * [NS 6] Prefix ::= NCName
549 *
550 * [NS 7] LocalPart ::= NCName
551 *
552 * Returns NULL if not a QName, otherwise the local part, and prefix
553 * is updated to get the Prefix if any.
554 */
555
556xmlChar *
557xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
558 int len = 0;
559 xmlChar *ret = NULL;
560
561 *prefix = NULL;
562
Daniel Veillardf4309d72001-10-02 09:28:58 +0000563#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000564 /* xml: prefix is not really a namespace */
565 if ((name[0] == 'x') && (name[1] == 'm') &&
566 (name[2] == 'l') && (name[3] == ':'))
567 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000568#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000569
570 /* nasty but valid */
571 if (name[0] == ':')
572 return(NULL);
573
574 /*
575 * we are not trying to validate but just to cut, and yes it will
576 * work even if this is as set of UTF-8 encoded chars
577 */
578 while ((name[len] != 0) && (name[len] != ':'))
579 len++;
580
581 if (name[len] == 0)
582 return(NULL);
583
584 *prefix = xmlStrndup(name, len);
585 ret = xmlStrdup(&name[len + 1]);
586
587 return(ret);
588}
589
590/****************************************************************
591 * *
592 * Util functions for data allocation/deallocation *
593 * *
594 ****************************************************************/
595
596/**
597 * xmlNewElementContent:
598 * @name: the subelement name or NULL
599 * @type: the type of element content decl
600 *
601 * Allocate an element content structure.
602 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000603 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000604 */
605xmlElementContentPtr
606xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
607 xmlElementContentPtr ret;
608
609 switch(type) {
610 case XML_ELEMENT_CONTENT_ELEMENT:
611 if (name == NULL) {
612 xmlGenericError(xmlGenericErrorContext,
613 "xmlNewElementContent : name == NULL !\n");
614 }
615 break;
616 case XML_ELEMENT_CONTENT_PCDATA:
617 case XML_ELEMENT_CONTENT_SEQ:
618 case XML_ELEMENT_CONTENT_OR:
619 if (name != NULL) {
620 xmlGenericError(xmlGenericErrorContext,
621 "xmlNewElementContent : name != NULL !\n");
622 }
623 break;
624 default:
625 xmlGenericError(xmlGenericErrorContext,
626 "xmlNewElementContent: unknown type %d\n", type);
627 return(NULL);
628 }
629 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
630 if (ret == NULL) {
631 xmlGenericError(xmlGenericErrorContext,
632 "xmlNewElementContent : out of memory!\n");
633 return(NULL);
634 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000635 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000636 ret->type = type;
637 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000638 if (name != NULL) {
639 xmlChar *prefix = NULL;
640 ret->name = xmlSplitQName2(name, &prefix);
641 if (ret->name == NULL)
642 ret->name = xmlStrdup(name);
643 ret->prefix = prefix;
644 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000645 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000646 ret->prefix = NULL;
647 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000648 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000649 return(ret);
650}
651
652/**
653 * xmlCopyElementContent:
654 * @content: An element content pointer.
655 *
656 * Build a copy of an element content description.
657 *
658 * Returns the new xmlElementContentPtr or NULL in case of error.
659 */
660xmlElementContentPtr
661xmlCopyElementContent(xmlElementContentPtr cur) {
662 xmlElementContentPtr ret;
663
664 if (cur == NULL) return(NULL);
665 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
666 if (ret == NULL) {
667 xmlGenericError(xmlGenericErrorContext,
668 "xmlCopyElementContent : out of memory\n");
669 return(NULL);
670 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000671 if (cur->prefix != NULL)
672 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000673 ret->ocur = cur->ocur;
674 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000675 if (ret->c1 != NULL)
676 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000677 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000678 if (ret->c2 != NULL)
679 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000680 return(ret);
681}
682
683/**
684 * xmlFreeElementContent:
685 * @cur: the element content tree to free
686 *
687 * Free an element content structure. This is a recursive call !
688 */
689void
690xmlFreeElementContent(xmlElementContentPtr cur) {
691 if (cur == NULL) return;
692 switch (cur->type) {
693 case XML_ELEMENT_CONTENT_PCDATA:
694 case XML_ELEMENT_CONTENT_ELEMENT:
695 case XML_ELEMENT_CONTENT_SEQ:
696 case XML_ELEMENT_CONTENT_OR:
697 break;
698 default:
699 xmlGenericError(xmlGenericErrorContext,
700 "xmlFreeElementContent : type %d\n", cur->type);
701 return;
702 }
703 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
704 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
705 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000706 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000707 xmlFree(cur);
708}
709
710/**
711 * xmlDumpElementContent:
712 * @buf: An XML buffer
713 * @content: An element table
714 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
715 *
716 * This will dump the content of the element table as an XML DTD definition
717 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000718static void
Owen Taylor3473f882001-02-23 17:55:21 +0000719xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
720 if (content == NULL) return;
721
722 if (glob) xmlBufferWriteChar(buf, "(");
723 switch (content->type) {
724 case XML_ELEMENT_CONTENT_PCDATA:
725 xmlBufferWriteChar(buf, "#PCDATA");
726 break;
727 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000728 if (content->prefix != NULL) {
729 xmlBufferWriteCHAR(buf, content->prefix);
730 xmlBufferWriteChar(buf, ":");
731 }
Owen Taylor3473f882001-02-23 17:55:21 +0000732 xmlBufferWriteCHAR(buf, content->name);
733 break;
734 case XML_ELEMENT_CONTENT_SEQ:
735 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
736 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
737 xmlDumpElementContent(buf, content->c1, 1);
738 else
739 xmlDumpElementContent(buf, content->c1, 0);
740 xmlBufferWriteChar(buf, " , ");
741 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
742 xmlDumpElementContent(buf, content->c2, 1);
743 else
744 xmlDumpElementContent(buf, content->c2, 0);
745 break;
746 case XML_ELEMENT_CONTENT_OR:
747 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
748 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
749 xmlDumpElementContent(buf, content->c1, 1);
750 else
751 xmlDumpElementContent(buf, content->c1, 0);
752 xmlBufferWriteChar(buf, " | ");
753 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
754 xmlDumpElementContent(buf, content->c2, 1);
755 else
756 xmlDumpElementContent(buf, content->c2, 0);
757 break;
758 default:
759 xmlGenericError(xmlGenericErrorContext,
760 "xmlDumpElementContent: unknown type %d\n",
761 content->type);
762 }
763 if (glob)
764 xmlBufferWriteChar(buf, ")");
765 switch (content->ocur) {
766 case XML_ELEMENT_CONTENT_ONCE:
767 break;
768 case XML_ELEMENT_CONTENT_OPT:
769 xmlBufferWriteChar(buf, "?");
770 break;
771 case XML_ELEMENT_CONTENT_MULT:
772 xmlBufferWriteChar(buf, "*");
773 break;
774 case XML_ELEMENT_CONTENT_PLUS:
775 xmlBufferWriteChar(buf, "+");
776 break;
777 }
778}
779
780/**
781 * xmlSprintfElementContent:
782 * @buf: an output buffer
783 * @content: An element table
784 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
785 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000786 * Deprecated, unsafe, use xmlSnprintfElementContent
787 */
788void
789xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
790 xmlElementContentPtr content ATTRIBUTE_UNUSED,
791 int glob ATTRIBUTE_UNUSED) {
792}
793
794/**
795 * xmlSnprintfElementContent:
796 * @buf: an output buffer
797 * @size: the buffer size
798 * @content: An element table
799 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
800 *
Owen Taylor3473f882001-02-23 17:55:21 +0000801 * This will dump the content of the element content definition
802 * Intended just for the debug routine
803 */
804void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000805xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
806 int len;
807
Owen Taylor3473f882001-02-23 17:55:21 +0000808 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000809 len = strlen(buf);
810 if (size - len < 50) {
811 if ((size - len > 4) && (buf[len - 1] != '.'))
812 strcat(buf, " ...");
813 return;
814 }
Owen Taylor3473f882001-02-23 17:55:21 +0000815 if (glob) strcat(buf, "(");
816 switch (content->type) {
817 case XML_ELEMENT_CONTENT_PCDATA:
818 strcat(buf, "#PCDATA");
819 break;
820 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000821 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000822 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000823 strcat(buf, " ...");
824 return;
825 }
826 strcat(buf, (char *) content->prefix);
827 strcat(buf, ":");
828 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000829 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000830 strcat(buf, " ...");
831 return;
832 }
Owen Taylor3473f882001-02-23 17:55:21 +0000833 strcat(buf, (char *) content->name);
834 break;
835 case XML_ELEMENT_CONTENT_SEQ:
836 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
837 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000838 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000839 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000840 xmlSnprintfElementContent(buf, size, content->c1, 0);
841 len = strlen(buf);
842 if (size - len < 50) {
843 if ((size - len > 4) && (buf[len - 1] != '.'))
844 strcat(buf, " ...");
845 return;
846 }
Owen Taylor3473f882001-02-23 17:55:21 +0000847 strcat(buf, " , ");
848 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000849 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000850 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000851 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000852 break;
853 case XML_ELEMENT_CONTENT_OR:
854 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
855 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000856 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000857 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000858 xmlSnprintfElementContent(buf, size, content->c1, 0);
859 len = strlen(buf);
860 if (size - len < 50) {
861 if ((size - len > 4) && (buf[len - 1] != '.'))
862 strcat(buf, " ...");
863 return;
864 }
Owen Taylor3473f882001-02-23 17:55:21 +0000865 strcat(buf, " | ");
866 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000867 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000868 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000869 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000870 break;
871 }
872 if (glob)
873 strcat(buf, ")");
874 switch (content->ocur) {
875 case XML_ELEMENT_CONTENT_ONCE:
876 break;
877 case XML_ELEMENT_CONTENT_OPT:
878 strcat(buf, "?");
879 break;
880 case XML_ELEMENT_CONTENT_MULT:
881 strcat(buf, "*");
882 break;
883 case XML_ELEMENT_CONTENT_PLUS:
884 strcat(buf, "+");
885 break;
886 }
887}
888
889/****************************************************************
890 * *
891 * Registration of DTD declarations *
892 * *
893 ****************************************************************/
894
895/**
896 * xmlCreateElementTable:
897 *
898 * create and initialize an empty element hash table.
899 *
900 * Returns the xmlElementTablePtr just created or NULL in case of error.
901 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000902static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000903xmlCreateElementTable(void) {
904 return(xmlHashCreate(0));
905}
906
907/**
908 * xmlFreeElement:
909 * @elem: An element
910 *
911 * Deallocate the memory used by an element definition
912 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000913static void
Owen Taylor3473f882001-02-23 17:55:21 +0000914xmlFreeElement(xmlElementPtr elem) {
915 if (elem == NULL) return;
916 xmlUnlinkNode((xmlNodePtr) elem);
917 xmlFreeElementContent(elem->content);
918 if (elem->name != NULL)
919 xmlFree((xmlChar *) elem->name);
920 if (elem->prefix != NULL)
921 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000922#ifdef LIBXML_REGEXP_ENABLED
923 if (elem->contModel != NULL)
924 xmlRegFreeRegexp(elem->contModel);
925#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000926 xmlFree(elem);
927}
928
929
930/**
931 * xmlAddElementDecl:
932 * @ctxt: the validation context
933 * @dtd: pointer to the DTD
934 * @name: the entity name
935 * @type: the element type
936 * @content: the element content tree or NULL
937 *
938 * Register a new element declaration
939 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000940 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000941 */
942xmlElementPtr
943xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
944 xmlElementTypeVal type,
945 xmlElementContentPtr content) {
946 xmlElementPtr ret;
947 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000948 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000949 xmlChar *ns, *uqname;
950
951 if (dtd == NULL) {
952 xmlGenericError(xmlGenericErrorContext,
953 "xmlAddElementDecl: dtd == NULL\n");
954 return(NULL);
955 }
956 if (name == NULL) {
957 xmlGenericError(xmlGenericErrorContext,
958 "xmlAddElementDecl: name == NULL\n");
959 return(NULL);
960 }
961 switch (type) {
962 case XML_ELEMENT_TYPE_EMPTY:
963 if (content != NULL) {
964 xmlGenericError(xmlGenericErrorContext,
965 "xmlAddElementDecl: content != NULL for EMPTY\n");
966 return(NULL);
967 }
968 break;
969 case XML_ELEMENT_TYPE_ANY:
970 if (content != NULL) {
971 xmlGenericError(xmlGenericErrorContext,
972 "xmlAddElementDecl: content != NULL for ANY\n");
973 return(NULL);
974 }
975 break;
976 case XML_ELEMENT_TYPE_MIXED:
977 if (content == NULL) {
978 xmlGenericError(xmlGenericErrorContext,
979 "xmlAddElementDecl: content == NULL for MIXED\n");
980 return(NULL);
981 }
982 break;
983 case XML_ELEMENT_TYPE_ELEMENT:
984 if (content == NULL) {
985 xmlGenericError(xmlGenericErrorContext,
986 "xmlAddElementDecl: content == NULL for ELEMENT\n");
987 return(NULL);
988 }
989 break;
990 default:
991 xmlGenericError(xmlGenericErrorContext,
992 "xmlAddElementDecl: unknown type %d\n", type);
993 return(NULL);
994 }
995
996 /*
997 * check if name is a QName
998 */
999 uqname = xmlSplitQName2(name, &ns);
1000 if (uqname != NULL)
1001 name = uqname;
1002
1003 /*
1004 * Create the Element table if needed.
1005 */
1006 table = (xmlElementTablePtr) dtd->elements;
1007 if (table == NULL) {
1008 table = xmlCreateElementTable();
1009 dtd->elements = (void *) table;
1010 }
1011 if (table == NULL) {
1012 xmlGenericError(xmlGenericErrorContext,
1013 "xmlAddElementDecl: Table creation failed!\n");
1014 return(NULL);
1015 }
1016
Daniel Veillarda10efa82001-04-18 13:09:01 +00001017 /*
1018 * lookup old attributes inserted on an undefined element in the
1019 * internal subset.
1020 */
1021 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1022 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1023 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1024 oldAttributes = ret->attributes;
1025 ret->attributes = NULL;
1026 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1027 xmlFreeElement(ret);
1028 }
Owen Taylor3473f882001-02-23 17:55:21 +00001029 }
Owen Taylor3473f882001-02-23 17:55:21 +00001030
1031 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001032 * The element may already be present if one of its attribute
1033 * was registered first
1034 */
1035 ret = xmlHashLookup2(table, name, ns);
1036 if (ret != NULL) {
1037 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1038 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001039 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001040 */
1041 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1042 if (uqname != NULL)
1043 xmlFree(uqname);
1044 return(NULL);
1045 }
1046 } else {
1047 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1048 if (ret == NULL) {
1049 xmlGenericError(xmlGenericErrorContext,
1050 "xmlAddElementDecl: out of memory\n");
1051 return(NULL);
1052 }
1053 memset(ret, 0, sizeof(xmlElement));
1054 ret->type = XML_ELEMENT_DECL;
1055
1056 /*
1057 * fill the structure.
1058 */
1059 ret->name = xmlStrdup(name);
1060 ret->prefix = ns;
1061
1062 /*
1063 * Validity Check:
1064 * Insertion must not fail
1065 */
1066 if (xmlHashAddEntry2(table, name, ns, ret)) {
1067 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001068 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001069 */
1070 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1071 xmlFreeElement(ret);
1072 if (uqname != NULL)
1073 xmlFree(uqname);
1074 return(NULL);
1075 }
1076 }
1077
1078 /*
1079 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001080 */
1081 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001082 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001083 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001084
1085 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001086 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001087 */
1088 ret->parent = dtd;
1089 ret->doc = dtd->doc;
1090 if (dtd->last == NULL) {
1091 dtd->children = dtd->last = (xmlNodePtr) ret;
1092 } else {
1093 dtd->last->next = (xmlNodePtr) ret;
1094 ret->prev = dtd->last;
1095 dtd->last = (xmlNodePtr) ret;
1096 }
1097 if (uqname != NULL)
1098 xmlFree(uqname);
1099 return(ret);
1100}
1101
1102/**
1103 * xmlFreeElementTable:
1104 * @table: An element table
1105 *
1106 * Deallocate the memory used by an element hash table.
1107 */
1108void
1109xmlFreeElementTable(xmlElementTablePtr table) {
1110 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1111}
1112
1113/**
1114 * xmlCopyElement:
1115 * @elem: An element
1116 *
1117 * Build a copy of an element.
1118 *
1119 * Returns the new xmlElementPtr or NULL in case of error.
1120 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001121static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001122xmlCopyElement(xmlElementPtr elem) {
1123 xmlElementPtr cur;
1124
1125 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1126 if (cur == NULL) {
1127 xmlGenericError(xmlGenericErrorContext,
1128 "xmlCopyElement: out of memory !\n");
1129 return(NULL);
1130 }
1131 memset(cur, 0, sizeof(xmlElement));
1132 cur->type = XML_ELEMENT_DECL;
1133 cur->etype = elem->etype;
1134 if (elem->name != NULL)
1135 cur->name = xmlStrdup(elem->name);
1136 else
1137 cur->name = NULL;
1138 if (elem->prefix != NULL)
1139 cur->prefix = xmlStrdup(elem->prefix);
1140 else
1141 cur->prefix = NULL;
1142 cur->content = xmlCopyElementContent(elem->content);
1143 /* TODO : rebuild the attribute list on the copy */
1144 cur->attributes = NULL;
1145 return(cur);
1146}
1147
1148/**
1149 * xmlCopyElementTable:
1150 * @table: An element table
1151 *
1152 * Build a copy of an element table.
1153 *
1154 * Returns the new xmlElementTablePtr or NULL in case of error.
1155 */
1156xmlElementTablePtr
1157xmlCopyElementTable(xmlElementTablePtr table) {
1158 return((xmlElementTablePtr) xmlHashCopy(table,
1159 (xmlHashCopier) xmlCopyElement));
1160}
1161
1162/**
1163 * xmlDumpElementDecl:
1164 * @buf: the XML buffer output
1165 * @elem: An element table
1166 *
1167 * This will dump the content of the element declaration as an XML
1168 * DTD definition
1169 */
1170void
1171xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1172 switch (elem->etype) {
1173 case XML_ELEMENT_TYPE_EMPTY:
1174 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001175 if (elem->prefix != NULL) {
1176 xmlBufferWriteCHAR(buf, elem->prefix);
1177 xmlBufferWriteChar(buf, ":");
1178 }
Owen Taylor3473f882001-02-23 17:55:21 +00001179 xmlBufferWriteCHAR(buf, elem->name);
1180 xmlBufferWriteChar(buf, " EMPTY>\n");
1181 break;
1182 case XML_ELEMENT_TYPE_ANY:
1183 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001184 if (elem->prefix != NULL) {
1185 xmlBufferWriteCHAR(buf, elem->prefix);
1186 xmlBufferWriteChar(buf, ":");
1187 }
Owen Taylor3473f882001-02-23 17:55:21 +00001188 xmlBufferWriteCHAR(buf, elem->name);
1189 xmlBufferWriteChar(buf, " ANY>\n");
1190 break;
1191 case XML_ELEMENT_TYPE_MIXED:
1192 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001193 if (elem->prefix != NULL) {
1194 xmlBufferWriteCHAR(buf, elem->prefix);
1195 xmlBufferWriteChar(buf, ":");
1196 }
Owen Taylor3473f882001-02-23 17:55:21 +00001197 xmlBufferWriteCHAR(buf, elem->name);
1198 xmlBufferWriteChar(buf, " ");
1199 xmlDumpElementContent(buf, elem->content, 1);
1200 xmlBufferWriteChar(buf, ">\n");
1201 break;
1202 case XML_ELEMENT_TYPE_ELEMENT:
1203 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001204 if (elem->prefix != NULL) {
1205 xmlBufferWriteCHAR(buf, elem->prefix);
1206 xmlBufferWriteChar(buf, ":");
1207 }
Owen Taylor3473f882001-02-23 17:55:21 +00001208 xmlBufferWriteCHAR(buf, elem->name);
1209 xmlBufferWriteChar(buf, " ");
1210 xmlDumpElementContent(buf, elem->content, 1);
1211 xmlBufferWriteChar(buf, ">\n");
1212 break;
1213 default:
1214 xmlGenericError(xmlGenericErrorContext,
1215 "xmlDumpElementDecl: internal: unknown type %d\n",
1216 elem->etype);
1217 }
1218}
1219
1220/**
1221 * xmlDumpElementTable:
1222 * @buf: the XML buffer output
1223 * @table: An element table
1224 *
1225 * This will dump the content of the element table as an XML DTD definition
1226 */
1227void
1228xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1229 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1230}
1231
1232/**
1233 * xmlCreateEnumeration:
1234 * @name: the enumeration name or NULL
1235 *
1236 * create and initialize an enumeration attribute node.
1237 *
1238 * Returns the xmlEnumerationPtr just created or NULL in case
1239 * of error.
1240 */
1241xmlEnumerationPtr
1242xmlCreateEnumeration(xmlChar *name) {
1243 xmlEnumerationPtr ret;
1244
1245 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1246 if (ret == NULL) {
1247 xmlGenericError(xmlGenericErrorContext,
1248 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1249 (long)sizeof(xmlEnumeration));
1250 return(NULL);
1251 }
1252 memset(ret, 0, sizeof(xmlEnumeration));
1253
1254 if (name != NULL)
1255 ret->name = xmlStrdup(name);
1256 return(ret);
1257}
1258
1259/**
1260 * xmlFreeEnumeration:
1261 * @cur: the tree to free.
1262 *
1263 * free an enumeration attribute node (recursive).
1264 */
1265void
1266xmlFreeEnumeration(xmlEnumerationPtr cur) {
1267 if (cur == NULL) return;
1268
1269 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1270
1271 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001272 xmlFree(cur);
1273}
1274
1275/**
1276 * xmlCopyEnumeration:
1277 * @cur: the tree to copy.
1278 *
1279 * Copy an enumeration attribute node (recursive).
1280 *
1281 * Returns the xmlEnumerationPtr just created or NULL in case
1282 * of error.
1283 */
1284xmlEnumerationPtr
1285xmlCopyEnumeration(xmlEnumerationPtr cur) {
1286 xmlEnumerationPtr ret;
1287
1288 if (cur == NULL) return(NULL);
1289 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1290
1291 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1292 else ret->next = NULL;
1293
1294 return(ret);
1295}
1296
1297/**
1298 * xmlDumpEnumeration:
1299 * @buf: the XML buffer output
1300 * @enum: An enumeration
1301 *
1302 * This will dump the content of the enumeration
1303 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001304static void
Owen Taylor3473f882001-02-23 17:55:21 +00001305xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1306 if (cur == NULL) return;
1307
1308 xmlBufferWriteCHAR(buf, cur->name);
1309 if (cur->next == NULL)
1310 xmlBufferWriteChar(buf, ")");
1311 else {
1312 xmlBufferWriteChar(buf, " | ");
1313 xmlDumpEnumeration(buf, cur->next);
1314 }
1315}
1316
1317/**
1318 * xmlCreateAttributeTable:
1319 *
1320 * create and initialize an empty attribute hash table.
1321 *
1322 * Returns the xmlAttributeTablePtr just created or NULL in case
1323 * of error.
1324 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001325static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001326xmlCreateAttributeTable(void) {
1327 return(xmlHashCreate(0));
1328}
1329
1330/**
1331 * xmlScanAttributeDeclCallback:
1332 * @attr: the attribute decl
1333 * @list: the list to update
1334 *
1335 * Callback called by xmlScanAttributeDecl when a new attribute
1336 * has to be entered in the list.
1337 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001338static void
Owen Taylor3473f882001-02-23 17:55:21 +00001339xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001340 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001341 attr->nexth = *list;
1342 *list = attr;
1343}
1344
1345/**
1346 * xmlScanAttributeDecl:
1347 * @dtd: pointer to the DTD
1348 * @elem: the element name
1349 *
1350 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001351 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001352 *
1353 * Returns the pointer to the first attribute decl in the chain,
1354 * possibly NULL.
1355 */
1356xmlAttributePtr
1357xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1358 xmlAttributePtr ret = NULL;
1359 xmlAttributeTablePtr table;
1360
1361 if (dtd == NULL) {
1362 xmlGenericError(xmlGenericErrorContext,
1363 "xmlScanAttributeDecl: dtd == NULL\n");
1364 return(NULL);
1365 }
1366 if (elem == NULL) {
1367 xmlGenericError(xmlGenericErrorContext,
1368 "xmlScanAttributeDecl: elem == NULL\n");
1369 return(NULL);
1370 }
1371 table = (xmlAttributeTablePtr) dtd->attributes;
1372 if (table == NULL)
1373 return(NULL);
1374
1375 /* WRONG !!! */
1376 xmlHashScan3(table, NULL, NULL, elem,
1377 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1378 return(ret);
1379}
1380
1381/**
1382 * xmlScanIDAttributeDecl:
1383 * @ctxt: the validation context
1384 * @elem: the element name
1385 *
1386 * Verify that the element don't have too many ID attributes
1387 * declared.
1388 *
1389 * Returns the number of ID attributes found.
1390 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001391static int
Owen Taylor3473f882001-02-23 17:55:21 +00001392xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1393 xmlAttributePtr cur;
1394 int ret = 0;
1395
1396 if (elem == NULL) return(0);
1397 cur = elem->attributes;
1398 while (cur != NULL) {
1399 if (cur->atype == XML_ATTRIBUTE_ID) {
1400 ret ++;
1401 if (ret > 1)
1402 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001403 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001404 elem->name, cur->name);
1405 }
1406 cur = cur->nexth;
1407 }
1408 return(ret);
1409}
1410
1411/**
1412 * xmlFreeAttribute:
1413 * @elem: An attribute
1414 *
1415 * Deallocate the memory used by an attribute definition
1416 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001417static void
Owen Taylor3473f882001-02-23 17:55:21 +00001418xmlFreeAttribute(xmlAttributePtr attr) {
1419 if (attr == NULL) return;
1420 xmlUnlinkNode((xmlNodePtr) attr);
1421 if (attr->tree != NULL)
1422 xmlFreeEnumeration(attr->tree);
1423 if (attr->elem != NULL)
1424 xmlFree((xmlChar *) attr->elem);
1425 if (attr->name != NULL)
1426 xmlFree((xmlChar *) attr->name);
1427 if (attr->defaultValue != NULL)
1428 xmlFree((xmlChar *) attr->defaultValue);
1429 if (attr->prefix != NULL)
1430 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001431 xmlFree(attr);
1432}
1433
1434
1435/**
1436 * xmlAddAttributeDecl:
1437 * @ctxt: the validation context
1438 * @dtd: pointer to the DTD
1439 * @elem: the element name
1440 * @name: the attribute name
1441 * @ns: the attribute namespace prefix
1442 * @type: the attribute type
1443 * @def: the attribute default type
1444 * @defaultValue: the attribute default value
1445 * @tree: if it's an enumeration, the associated list
1446 *
1447 * Register a new attribute declaration
1448 * Note that @tree becomes the ownership of the DTD
1449 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001450 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001451 */
1452xmlAttributePtr
1453xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1454 const xmlChar *name, const xmlChar *ns,
1455 xmlAttributeType type, xmlAttributeDefault def,
1456 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1457 xmlAttributePtr ret;
1458 xmlAttributeTablePtr table;
1459 xmlElementPtr elemDef;
1460
1461 if (dtd == NULL) {
1462 xmlGenericError(xmlGenericErrorContext,
1463 "xmlAddAttributeDecl: dtd == NULL\n");
1464 xmlFreeEnumeration(tree);
1465 return(NULL);
1466 }
1467 if (name == NULL) {
1468 xmlGenericError(xmlGenericErrorContext,
1469 "xmlAddAttributeDecl: name == NULL\n");
1470 xmlFreeEnumeration(tree);
1471 return(NULL);
1472 }
1473 if (elem == NULL) {
1474 xmlGenericError(xmlGenericErrorContext,
1475 "xmlAddAttributeDecl: elem == NULL\n");
1476 xmlFreeEnumeration(tree);
1477 return(NULL);
1478 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001479
Owen Taylor3473f882001-02-23 17:55:21 +00001480 /*
1481 * Check the type and possibly the default value.
1482 */
1483 switch (type) {
1484 case XML_ATTRIBUTE_CDATA:
1485 break;
1486 case XML_ATTRIBUTE_ID:
1487 break;
1488 case XML_ATTRIBUTE_IDREF:
1489 break;
1490 case XML_ATTRIBUTE_IDREFS:
1491 break;
1492 case XML_ATTRIBUTE_ENTITY:
1493 break;
1494 case XML_ATTRIBUTE_ENTITIES:
1495 break;
1496 case XML_ATTRIBUTE_NMTOKEN:
1497 break;
1498 case XML_ATTRIBUTE_NMTOKENS:
1499 break;
1500 case XML_ATTRIBUTE_ENUMERATION:
1501 break;
1502 case XML_ATTRIBUTE_NOTATION:
1503 break;
1504 default:
1505 xmlGenericError(xmlGenericErrorContext,
1506 "xmlAddAttributeDecl: unknown type %d\n", type);
1507 xmlFreeEnumeration(tree);
1508 return(NULL);
1509 }
1510 if ((defaultValue != NULL) &&
1511 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001512 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001513 elem, name, defaultValue);
1514 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001515 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001516 }
1517
1518 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001519 * Check first that an attribute defined in the external subset wasn't
1520 * already defined in the internal subset
1521 */
1522 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1523 (dtd->doc->intSubset != NULL) &&
1524 (dtd->doc->intSubset->attributes != NULL)) {
1525 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1526 if (ret != NULL)
1527 return(NULL);
1528 }
1529
1530 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001531 * Create the Attribute table if needed.
1532 */
1533 table = (xmlAttributeTablePtr) dtd->attributes;
1534 if (table == NULL) {
1535 table = xmlCreateAttributeTable();
1536 dtd->attributes = (void *) table;
1537 }
1538 if (table == NULL) {
1539 xmlGenericError(xmlGenericErrorContext,
1540 "xmlAddAttributeDecl: Table creation failed!\n");
1541 return(NULL);
1542 }
1543
1544
1545 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1546 if (ret == NULL) {
1547 xmlGenericError(xmlGenericErrorContext,
1548 "xmlAddAttributeDecl: out of memory\n");
1549 return(NULL);
1550 }
1551 memset(ret, 0, sizeof(xmlAttribute));
1552 ret->type = XML_ATTRIBUTE_DECL;
1553
1554 /*
1555 * fill the structure.
1556 */
1557 ret->atype = type;
1558 ret->name = xmlStrdup(name);
1559 ret->prefix = xmlStrdup(ns);
1560 ret->elem = xmlStrdup(elem);
1561 ret->def = def;
1562 ret->tree = tree;
1563 if (defaultValue != NULL)
1564 ret->defaultValue = xmlStrdup(defaultValue);
1565
1566 /*
1567 * Validity Check:
1568 * Search the DTD for previous declarations of the ATTLIST
1569 */
1570 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1571 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001572 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001573 */
1574 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001575 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001576 name, elem);
1577 xmlFreeAttribute(ret);
1578 return(NULL);
1579 }
1580
1581 /*
1582 * Validity Check:
1583 * Multiple ID per element
1584 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001585 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001586 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001587
Owen Taylor3473f882001-02-23 17:55:21 +00001588 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001589 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001590 VERROR(ctxt->userData,
1591 "Element %s has too may ID attributes defined : %s\n",
1592 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001593 ctxt->valid = 0;
1594 }
1595
Daniel Veillard48da9102001-08-07 01:10:10 +00001596 /*
1597 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001598 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001599 */
1600 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1601 ((ret->prefix != NULL &&
1602 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1603 ret->nexth = elemDef->attributes;
1604 elemDef->attributes = ret;
1605 } else {
1606 xmlAttributePtr tmp = elemDef->attributes;
1607
1608 while ((tmp != NULL) &&
1609 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1610 ((ret->prefix != NULL &&
1611 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1612 if (tmp->nexth == NULL)
1613 break;
1614 tmp = tmp->nexth;
1615 }
1616 if (tmp != NULL) {
1617 ret->nexth = tmp->nexth;
1618 tmp->nexth = ret;
1619 } else {
1620 ret->nexth = elemDef->attributes;
1621 elemDef->attributes = ret;
1622 }
1623 }
Owen Taylor3473f882001-02-23 17:55:21 +00001624 }
1625
1626 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001627 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001628 */
1629 ret->parent = dtd;
1630 ret->doc = dtd->doc;
1631 if (dtd->last == NULL) {
1632 dtd->children = dtd->last = (xmlNodePtr) ret;
1633 } else {
1634 dtd->last->next = (xmlNodePtr) ret;
1635 ret->prev = dtd->last;
1636 dtd->last = (xmlNodePtr) ret;
1637 }
1638 return(ret);
1639}
1640
1641/**
1642 * xmlFreeAttributeTable:
1643 * @table: An attribute table
1644 *
1645 * Deallocate the memory used by an entities hash table.
1646 */
1647void
1648xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1649 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1650}
1651
1652/**
1653 * xmlCopyAttribute:
1654 * @attr: An attribute
1655 *
1656 * Build a copy of an attribute.
1657 *
1658 * Returns the new xmlAttributePtr or NULL in case of error.
1659 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001660static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001661xmlCopyAttribute(xmlAttributePtr attr) {
1662 xmlAttributePtr cur;
1663
1664 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1665 if (cur == NULL) {
1666 xmlGenericError(xmlGenericErrorContext,
1667 "xmlCopyAttribute: out of memory !\n");
1668 return(NULL);
1669 }
1670 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001671 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001672 cur->atype = attr->atype;
1673 cur->def = attr->def;
1674 cur->tree = xmlCopyEnumeration(attr->tree);
1675 if (attr->elem != NULL)
1676 cur->elem = xmlStrdup(attr->elem);
1677 if (attr->name != NULL)
1678 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001679 if (attr->prefix != NULL)
1680 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001681 if (attr->defaultValue != NULL)
1682 cur->defaultValue = xmlStrdup(attr->defaultValue);
1683 return(cur);
1684}
1685
1686/**
1687 * xmlCopyAttributeTable:
1688 * @table: An attribute table
1689 *
1690 * Build a copy of an attribute table.
1691 *
1692 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1693 */
1694xmlAttributeTablePtr
1695xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1696 return((xmlAttributeTablePtr) xmlHashCopy(table,
1697 (xmlHashCopier) xmlCopyAttribute));
1698}
1699
1700/**
1701 * xmlDumpAttributeDecl:
1702 * @buf: the XML buffer output
1703 * @attr: An attribute declaration
1704 *
1705 * This will dump the content of the attribute declaration as an XML
1706 * DTD definition
1707 */
1708void
1709xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1710 xmlBufferWriteChar(buf, "<!ATTLIST ");
1711 xmlBufferWriteCHAR(buf, attr->elem);
1712 xmlBufferWriteChar(buf, " ");
1713 if (attr->prefix != NULL) {
1714 xmlBufferWriteCHAR(buf, attr->prefix);
1715 xmlBufferWriteChar(buf, ":");
1716 }
1717 xmlBufferWriteCHAR(buf, attr->name);
1718 switch (attr->atype) {
1719 case XML_ATTRIBUTE_CDATA:
1720 xmlBufferWriteChar(buf, " CDATA");
1721 break;
1722 case XML_ATTRIBUTE_ID:
1723 xmlBufferWriteChar(buf, " ID");
1724 break;
1725 case XML_ATTRIBUTE_IDREF:
1726 xmlBufferWriteChar(buf, " IDREF");
1727 break;
1728 case XML_ATTRIBUTE_IDREFS:
1729 xmlBufferWriteChar(buf, " IDREFS");
1730 break;
1731 case XML_ATTRIBUTE_ENTITY:
1732 xmlBufferWriteChar(buf, " ENTITY");
1733 break;
1734 case XML_ATTRIBUTE_ENTITIES:
1735 xmlBufferWriteChar(buf, " ENTITIES");
1736 break;
1737 case XML_ATTRIBUTE_NMTOKEN:
1738 xmlBufferWriteChar(buf, " NMTOKEN");
1739 break;
1740 case XML_ATTRIBUTE_NMTOKENS:
1741 xmlBufferWriteChar(buf, " NMTOKENS");
1742 break;
1743 case XML_ATTRIBUTE_ENUMERATION:
1744 xmlBufferWriteChar(buf, " (");
1745 xmlDumpEnumeration(buf, attr->tree);
1746 break;
1747 case XML_ATTRIBUTE_NOTATION:
1748 xmlBufferWriteChar(buf, " NOTATION (");
1749 xmlDumpEnumeration(buf, attr->tree);
1750 break;
1751 default:
1752 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001753 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001754 attr->atype);
1755 }
1756 switch (attr->def) {
1757 case XML_ATTRIBUTE_NONE:
1758 break;
1759 case XML_ATTRIBUTE_REQUIRED:
1760 xmlBufferWriteChar(buf, " #REQUIRED");
1761 break;
1762 case XML_ATTRIBUTE_IMPLIED:
1763 xmlBufferWriteChar(buf, " #IMPLIED");
1764 break;
1765 case XML_ATTRIBUTE_FIXED:
1766 xmlBufferWriteChar(buf, " #FIXED");
1767 break;
1768 default:
1769 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001770 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001771 attr->def);
1772 }
1773 if (attr->defaultValue != NULL) {
1774 xmlBufferWriteChar(buf, " ");
1775 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1776 }
1777 xmlBufferWriteChar(buf, ">\n");
1778}
1779
1780/**
1781 * xmlDumpAttributeTable:
1782 * @buf: the XML buffer output
1783 * @table: An attribute table
1784 *
1785 * This will dump the content of the attribute table as an XML DTD definition
1786 */
1787void
1788xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1789 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1790}
1791
1792/************************************************************************
1793 * *
1794 * NOTATIONs *
1795 * *
1796 ************************************************************************/
1797/**
1798 * xmlCreateNotationTable:
1799 *
1800 * create and initialize an empty notation hash table.
1801 *
1802 * Returns the xmlNotationTablePtr just created or NULL in case
1803 * of error.
1804 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001805static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001806xmlCreateNotationTable(void) {
1807 return(xmlHashCreate(0));
1808}
1809
1810/**
1811 * xmlFreeNotation:
1812 * @not: A notation
1813 *
1814 * Deallocate the memory used by an notation definition
1815 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001816static void
Owen Taylor3473f882001-02-23 17:55:21 +00001817xmlFreeNotation(xmlNotationPtr nota) {
1818 if (nota == NULL) return;
1819 if (nota->name != NULL)
1820 xmlFree((xmlChar *) nota->name);
1821 if (nota->PublicID != NULL)
1822 xmlFree((xmlChar *) nota->PublicID);
1823 if (nota->SystemID != NULL)
1824 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001825 xmlFree(nota);
1826}
1827
1828
1829/**
1830 * xmlAddNotationDecl:
1831 * @dtd: pointer to the DTD
1832 * @ctxt: the validation context
1833 * @name: the entity name
1834 * @PublicID: the public identifier or NULL
1835 * @SystemID: the system identifier or NULL
1836 *
1837 * Register a new notation declaration
1838 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001839 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001840 */
1841xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001842xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001843 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001844 const xmlChar *PublicID, const xmlChar *SystemID) {
1845 xmlNotationPtr ret;
1846 xmlNotationTablePtr table;
1847
1848 if (dtd == NULL) {
1849 xmlGenericError(xmlGenericErrorContext,
1850 "xmlAddNotationDecl: dtd == NULL\n");
1851 return(NULL);
1852 }
1853 if (name == NULL) {
1854 xmlGenericError(xmlGenericErrorContext,
1855 "xmlAddNotationDecl: name == NULL\n");
1856 return(NULL);
1857 }
1858 if ((PublicID == NULL) && (SystemID == NULL)) {
1859 xmlGenericError(xmlGenericErrorContext,
1860 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001861 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001862 }
1863
1864 /*
1865 * Create the Notation table if needed.
1866 */
1867 table = (xmlNotationTablePtr) dtd->notations;
1868 if (table == NULL)
1869 dtd->notations = table = xmlCreateNotationTable();
1870 if (table == NULL) {
1871 xmlGenericError(xmlGenericErrorContext,
1872 "xmlAddNotationDecl: Table creation failed!\n");
1873 return(NULL);
1874 }
1875
1876 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1877 if (ret == NULL) {
1878 xmlGenericError(xmlGenericErrorContext,
1879 "xmlAddNotationDecl: out of memory\n");
1880 return(NULL);
1881 }
1882 memset(ret, 0, sizeof(xmlNotation));
1883
1884 /*
1885 * fill the structure.
1886 */
1887 ret->name = xmlStrdup(name);
1888 if (SystemID != NULL)
1889 ret->SystemID = xmlStrdup(SystemID);
1890 if (PublicID != NULL)
1891 ret->PublicID = xmlStrdup(PublicID);
1892
1893 /*
1894 * Validity Check:
1895 * Check the DTD for previous declarations of the ATTLIST
1896 */
1897 if (xmlHashAddEntry(table, name, ret)) {
1898 xmlGenericError(xmlGenericErrorContext,
1899 "xmlAddNotationDecl: %s already defined\n", name);
1900 xmlFreeNotation(ret);
1901 return(NULL);
1902 }
1903 return(ret);
1904}
1905
1906/**
1907 * xmlFreeNotationTable:
1908 * @table: An notation table
1909 *
1910 * Deallocate the memory used by an entities hash table.
1911 */
1912void
1913xmlFreeNotationTable(xmlNotationTablePtr table) {
1914 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1915}
1916
1917/**
1918 * xmlCopyNotation:
1919 * @nota: A notation
1920 *
1921 * Build a copy of a notation.
1922 *
1923 * Returns the new xmlNotationPtr or NULL in case of error.
1924 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001925static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001926xmlCopyNotation(xmlNotationPtr nota) {
1927 xmlNotationPtr cur;
1928
1929 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1930 if (cur == NULL) {
1931 xmlGenericError(xmlGenericErrorContext,
1932 "xmlCopyNotation: out of memory !\n");
1933 return(NULL);
1934 }
1935 if (nota->name != NULL)
1936 cur->name = xmlStrdup(nota->name);
1937 else
1938 cur->name = NULL;
1939 if (nota->PublicID != NULL)
1940 cur->PublicID = xmlStrdup(nota->PublicID);
1941 else
1942 cur->PublicID = NULL;
1943 if (nota->SystemID != NULL)
1944 cur->SystemID = xmlStrdup(nota->SystemID);
1945 else
1946 cur->SystemID = NULL;
1947 return(cur);
1948}
1949
1950/**
1951 * xmlCopyNotationTable:
1952 * @table: A notation table
1953 *
1954 * Build a copy of a notation table.
1955 *
1956 * Returns the new xmlNotationTablePtr or NULL in case of error.
1957 */
1958xmlNotationTablePtr
1959xmlCopyNotationTable(xmlNotationTablePtr table) {
1960 return((xmlNotationTablePtr) xmlHashCopy(table,
1961 (xmlHashCopier) xmlCopyNotation));
1962}
1963
1964/**
1965 * xmlDumpNotationDecl:
1966 * @buf: the XML buffer output
1967 * @nota: A notation declaration
1968 *
1969 * This will dump the content the notation declaration as an XML DTD definition
1970 */
1971void
1972xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1973 xmlBufferWriteChar(buf, "<!NOTATION ");
1974 xmlBufferWriteCHAR(buf, nota->name);
1975 if (nota->PublicID != NULL) {
1976 xmlBufferWriteChar(buf, " PUBLIC ");
1977 xmlBufferWriteQuotedString(buf, nota->PublicID);
1978 if (nota->SystemID != NULL) {
1979 xmlBufferWriteChar(buf, " ");
1980 xmlBufferWriteCHAR(buf, nota->SystemID);
1981 }
1982 } else {
1983 xmlBufferWriteChar(buf, " SYSTEM ");
1984 xmlBufferWriteCHAR(buf, nota->SystemID);
1985 }
1986 xmlBufferWriteChar(buf, " >\n");
1987}
1988
1989/**
1990 * xmlDumpNotationTable:
1991 * @buf: the XML buffer output
1992 * @table: A notation table
1993 *
1994 * This will dump the content of the notation table as an XML DTD definition
1995 */
1996void
1997xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1998 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1999}
2000
2001/************************************************************************
2002 * *
2003 * IDs *
2004 * *
2005 ************************************************************************/
2006/**
2007 * xmlCreateIDTable:
2008 *
2009 * create and initialize an empty id hash table.
2010 *
2011 * Returns the xmlIDTablePtr just created or NULL in case
2012 * of error.
2013 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002014static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002015xmlCreateIDTable(void) {
2016 return(xmlHashCreate(0));
2017}
2018
2019/**
2020 * xmlFreeID:
2021 * @not: A id
2022 *
2023 * Deallocate the memory used by an id definition
2024 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002025static void
Owen Taylor3473f882001-02-23 17:55:21 +00002026xmlFreeID(xmlIDPtr id) {
2027 if (id == NULL) return;
2028 if (id->value != NULL)
2029 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00002030 xmlFree(id);
2031}
2032
2033/**
2034 * xmlAddID:
2035 * @ctxt: the validation context
2036 * @doc: pointer to the document
2037 * @value: the value name
2038 * @attr: the attribute holding the ID
2039 *
2040 * Register a new id declaration
2041 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002042 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002043 */
2044xmlIDPtr
2045xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2046 xmlAttrPtr attr) {
2047 xmlIDPtr ret;
2048 xmlIDTablePtr table;
2049
2050 if (doc == NULL) {
2051 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002052 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002053 return(NULL);
2054 }
2055 if (value == NULL) {
2056 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002057 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002058 return(NULL);
2059 }
2060 if (attr == NULL) {
2061 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002062 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002063 return(NULL);
2064 }
2065
2066 /*
2067 * Create the ID table if needed.
2068 */
2069 table = (xmlIDTablePtr) doc->ids;
2070 if (table == NULL)
2071 doc->ids = table = xmlCreateIDTable();
2072 if (table == NULL) {
2073 xmlGenericError(xmlGenericErrorContext,
2074 "xmlAddID: Table creation failed!\n");
2075 return(NULL);
2076 }
2077
2078 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2079 if (ret == NULL) {
2080 xmlGenericError(xmlGenericErrorContext,
2081 "xmlAddID: out of memory\n");
2082 return(NULL);
2083 }
2084
2085 /*
2086 * fill the structure.
2087 */
2088 ret->value = xmlStrdup(value);
2089 ret->attr = attr;
2090
2091 if (xmlHashAddEntry(table, value, ret) < 0) {
2092 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002093 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002094 */
Daniel Veillard76575762002-09-05 14:21:15 +00002095 if (ctxt != NULL) {
2096 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002097 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002098 }
Owen Taylor3473f882001-02-23 17:55:21 +00002099 xmlFreeID(ret);
2100 return(NULL);
2101 }
2102 return(ret);
2103}
2104
2105/**
2106 * xmlFreeIDTable:
2107 * @table: An id table
2108 *
2109 * Deallocate the memory used by an ID hash table.
2110 */
2111void
2112xmlFreeIDTable(xmlIDTablePtr table) {
2113 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2114}
2115
2116/**
2117 * xmlIsID:
2118 * @doc: the document
2119 * @elem: the element carrying the attribute
2120 * @attr: the attribute
2121 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002122 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002123 * then this is simple, otherwise we use an heuristic: name ID (upper
2124 * or lowercase).
2125 *
2126 * Returns 0 or 1 depending on the lookup result
2127 */
2128int
2129xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2130 if (doc == NULL) return(0);
2131 if (attr == NULL) return(0);
2132 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2133 return(0);
2134 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2135 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2136 (xmlStrEqual(BAD_CAST "name", attr->name)))
2137 return(1);
2138 return(0);
2139 } else {
2140 xmlAttributePtr attrDecl;
2141
2142 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002143 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2144 /*
2145 * TODO: this sucks ... recomputing this every time is stupid
2146 */
2147 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2148 xmlChar *fullname;
2149
2150 fullname = xmlMalloc(len);
2151 if (fullname == NULL)
2152 return(0);
2153 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2154 (char *) elem->name);
2155 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2156 attr->name);
2157 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2158 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2159 attr->name);
2160 xmlFree(fullname);
2161 } else {
2162 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2163 attr->name);
2164 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2165 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2166 attr->name);
2167 }
Owen Taylor3473f882001-02-23 17:55:21 +00002168
2169 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2170 return(1);
2171 }
2172 return(0);
2173}
2174
2175/**
2176 * xmlRemoveID
2177 * @doc: the document
2178 * @attr: the attribute
2179 *
2180 * Remove the given attribute from the ID table maintained internally.
2181 *
2182 * Returns -1 if the lookup failed and 0 otherwise
2183 */
2184int
2185xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2186 xmlAttrPtr cur;
2187 xmlIDTablePtr table;
2188 xmlChar *ID;
2189
2190 if (doc == NULL) return(-1);
2191 if (attr == NULL) return(-1);
2192 table = (xmlIDTablePtr) doc->ids;
2193 if (table == NULL)
2194 return(-1);
2195
2196 if (attr == NULL)
2197 return(-1);
2198 ID = xmlNodeListGetString(doc, attr->children, 1);
2199 if (ID == NULL)
2200 return(-1);
2201 cur = xmlHashLookup(table, ID);
2202 if (cur != attr) {
2203 xmlFree(ID);
2204 return(-1);
2205 }
2206 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2207 xmlFree(ID);
2208 return(0);
2209}
2210
2211/**
2212 * xmlGetID:
2213 * @doc: pointer to the document
2214 * @ID: the ID value
2215 *
2216 * Search the attribute declaring the given ID
2217 *
2218 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2219 */
2220xmlAttrPtr
2221xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2222 xmlIDTablePtr table;
2223 xmlIDPtr id;
2224
2225 if (doc == NULL) {
2226 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2227 return(NULL);
2228 }
2229
2230 if (ID == NULL) {
2231 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2232 return(NULL);
2233 }
2234
2235 table = (xmlIDTablePtr) doc->ids;
2236 if (table == NULL)
2237 return(NULL);
2238
2239 id = xmlHashLookup(table, ID);
2240 if (id == NULL)
2241 return(NULL);
2242 return(id->attr);
2243}
2244
2245/************************************************************************
2246 * *
2247 * Refs *
2248 * *
2249 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002250typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002251{
2252 xmlListPtr l;
2253 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002254} xmlRemoveMemo;
2255
2256typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2257
2258typedef struct xmlValidateMemo_t
2259{
2260 xmlValidCtxtPtr ctxt;
2261 const xmlChar *name;
2262} xmlValidateMemo;
2263
2264typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002265
2266/**
2267 * xmlCreateRefTable:
2268 *
2269 * create and initialize an empty ref hash table.
2270 *
2271 * Returns the xmlRefTablePtr just created or NULL in case
2272 * of error.
2273 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002274static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002275xmlCreateRefTable(void) {
2276 return(xmlHashCreate(0));
2277}
2278
2279/**
2280 * xmlFreeRef:
2281 * @lk: A list link
2282 *
2283 * Deallocate the memory used by a ref definition
2284 */
2285static void
2286xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002287 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2288 if (ref == NULL) return;
2289 if (ref->value != NULL)
2290 xmlFree((xmlChar *)ref->value);
2291 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002292}
2293
2294/**
2295 * xmlFreeRefList:
2296 * @list_ref: A list of references.
2297 *
2298 * Deallocate the memory used by a list of references
2299 */
2300static void
2301xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002302 if (list_ref == NULL) return;
2303 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002304}
2305
2306/**
2307 * xmlWalkRemoveRef:
2308 * @data: Contents of current link
2309 * @user: Value supplied by the user
2310 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002311 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002312 */
2313static int
2314xmlWalkRemoveRef(const void *data, const void *user)
2315{
Daniel Veillard37721922001-05-04 15:21:12 +00002316 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2317 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2318 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002319
Daniel Veillard37721922001-05-04 15:21:12 +00002320 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2321 xmlListRemoveFirst(ref_list, (void *)data);
2322 return 0;
2323 }
2324 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002325}
2326
2327/**
2328 * xmlAddRef:
2329 * @ctxt: the validation context
2330 * @doc: pointer to the document
2331 * @value: the value name
2332 * @attr: the attribute holding the Ref
2333 *
2334 * Register a new ref declaration
2335 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002336 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002337 */
2338xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002339xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002340 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002341 xmlRefPtr ret;
2342 xmlRefTablePtr table;
2343 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002344
Daniel Veillard37721922001-05-04 15:21:12 +00002345 if (doc == NULL) {
2346 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002347 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002348 return(NULL);
2349 }
2350 if (value == NULL) {
2351 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002352 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002353 return(NULL);
2354 }
2355 if (attr == NULL) {
2356 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002357 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002358 return(NULL);
2359 }
Owen Taylor3473f882001-02-23 17:55:21 +00002360
Daniel Veillard37721922001-05-04 15:21:12 +00002361 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002362 * Create the Ref table if needed.
2363 */
Daniel Veillard37721922001-05-04 15:21:12 +00002364 table = (xmlRefTablePtr) doc->refs;
2365 if (table == NULL)
2366 doc->refs = table = xmlCreateRefTable();
2367 if (table == NULL) {
2368 xmlGenericError(xmlGenericErrorContext,
2369 "xmlAddRef: Table creation failed!\n");
2370 return(NULL);
2371 }
Owen Taylor3473f882001-02-23 17:55:21 +00002372
Daniel Veillard37721922001-05-04 15:21:12 +00002373 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2374 if (ret == NULL) {
2375 xmlGenericError(xmlGenericErrorContext,
2376 "xmlAddRef: out of memory\n");
2377 return(NULL);
2378 }
Owen Taylor3473f882001-02-23 17:55:21 +00002379
Daniel Veillard37721922001-05-04 15:21:12 +00002380 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002381 * fill the structure.
2382 */
Daniel Veillard37721922001-05-04 15:21:12 +00002383 ret->value = xmlStrdup(value);
2384 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002385
Daniel Veillard37721922001-05-04 15:21:12 +00002386 /* To add a reference :-
2387 * References are maintained as a list of references,
2388 * Lookup the entry, if no entry create new nodelist
2389 * Add the owning node to the NodeList
2390 * Return the ref
2391 */
Owen Taylor3473f882001-02-23 17:55:21 +00002392
Daniel Veillard37721922001-05-04 15:21:12 +00002393 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2394 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2395 xmlGenericError(xmlGenericErrorContext,
2396 "xmlAddRef: Reference list creation failed!\n");
2397 return(NULL);
2398 }
2399 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2400 xmlListDelete(ref_list);
2401 xmlGenericError(xmlGenericErrorContext,
2402 "xmlAddRef: Reference list insertion failed!\n");
2403 return(NULL);
2404 }
2405 }
2406 xmlListInsert(ref_list, ret);
2407 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002408}
2409
2410/**
2411 * xmlFreeRefTable:
2412 * @table: An ref table
2413 *
2414 * Deallocate the memory used by an Ref hash table.
2415 */
2416void
2417xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002418 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002419}
2420
2421/**
2422 * xmlIsRef:
2423 * @doc: the document
2424 * @elem: the element carrying the attribute
2425 * @attr: the attribute
2426 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002427 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002428 * then this is simple, otherwise we use an heuristic: name Ref (upper
2429 * or lowercase).
2430 *
2431 * Returns 0 or 1 depending on the lookup result
2432 */
2433int
2434xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002435 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2436 return(0);
2437 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2438 /* TODO @@@ */
2439 return(0);
2440 } else {
2441 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002442
Daniel Veillard37721922001-05-04 15:21:12 +00002443 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2444 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2445 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2446 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002447
Daniel Veillard37721922001-05-04 15:21:12 +00002448 if ((attrDecl != NULL) &&
2449 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2450 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2451 return(1);
2452 }
2453 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002454}
2455
2456/**
2457 * xmlRemoveRef
2458 * @doc: the document
2459 * @attr: the attribute
2460 *
2461 * Remove the given attribute from the Ref table maintained internally.
2462 *
2463 * Returns -1 if the lookup failed and 0 otherwise
2464 */
2465int
2466xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002467 xmlListPtr ref_list;
2468 xmlRefTablePtr table;
2469 xmlChar *ID;
2470 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002471
Daniel Veillard37721922001-05-04 15:21:12 +00002472 if (doc == NULL) return(-1);
2473 if (attr == NULL) return(-1);
2474 table = (xmlRefTablePtr) doc->refs;
2475 if (table == NULL)
2476 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002477
Daniel Veillard37721922001-05-04 15:21:12 +00002478 if (attr == NULL)
2479 return(-1);
2480 ID = xmlNodeListGetString(doc, attr->children, 1);
2481 if (ID == NULL)
2482 return(-1);
2483 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002484
Daniel Veillard37721922001-05-04 15:21:12 +00002485 if(ref_list == NULL) {
2486 xmlFree(ID);
2487 return (-1);
2488 }
2489 /* At this point, ref_list refers to a list of references which
2490 * have the same key as the supplied attr. Our list of references
2491 * is ordered by reference address and we don't have that information
2492 * here to use when removing. We'll have to walk the list and
2493 * check for a matching attribute, when we find one stop the walk
2494 * and remove the entry.
2495 * The list is ordered by reference, so that means we don't have the
2496 * key. Passing the list and the reference to the walker means we
2497 * will have enough data to be able to remove the entry.
2498 */
2499 target.l = ref_list;
2500 target.ap = attr;
2501
2502 /* Remove the supplied attr from our list */
2503 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002504
Daniel Veillard37721922001-05-04 15:21:12 +00002505 /*If the list is empty then remove the list entry in the hash */
2506 if (xmlListEmpty(ref_list))
2507 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2508 xmlFreeRefList);
2509 xmlFree(ID);
2510 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002511}
2512
2513/**
2514 * xmlGetRefs:
2515 * @doc: pointer to the document
2516 * @ID: the ID value
2517 *
2518 * Find the set of references for the supplied ID.
2519 *
2520 * Returns NULL if not found, otherwise node set for the ID.
2521 */
2522xmlListPtr
2523xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002524 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002525
Daniel Veillard37721922001-05-04 15:21:12 +00002526 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002527 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002528 return(NULL);
2529 }
Owen Taylor3473f882001-02-23 17:55:21 +00002530
Daniel Veillard37721922001-05-04 15:21:12 +00002531 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002532 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002533 return(NULL);
2534 }
Owen Taylor3473f882001-02-23 17:55:21 +00002535
Daniel Veillard37721922001-05-04 15:21:12 +00002536 table = (xmlRefTablePtr) doc->refs;
2537 if (table == NULL)
2538 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002539
Daniel Veillard37721922001-05-04 15:21:12 +00002540 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002541}
2542
2543/************************************************************************
2544 * *
2545 * Routines for validity checking *
2546 * *
2547 ************************************************************************/
2548
2549/**
2550 * xmlGetDtdElementDesc:
2551 * @dtd: a pointer to the DtD to search
2552 * @name: the element name
2553 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002554 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002555 *
2556 * returns the xmlElementPtr if found or NULL
2557 */
2558
2559xmlElementPtr
2560xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2561 xmlElementTablePtr table;
2562 xmlElementPtr cur;
2563 xmlChar *uqname = NULL, *prefix = NULL;
2564
2565 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002566 if (dtd->elements == NULL)
2567 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002568 table = (xmlElementTablePtr) dtd->elements;
2569
2570 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002571 if (uqname != NULL)
2572 name = uqname;
2573 cur = xmlHashLookup2(table, name, prefix);
2574 if (prefix != NULL) xmlFree(prefix);
2575 if (uqname != NULL) xmlFree(uqname);
2576 return(cur);
2577}
2578/**
2579 * xmlGetDtdElementDesc2:
2580 * @dtd: a pointer to the DtD to search
2581 * @name: the element name
2582 * @create: create an empty description if not found
2583 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002584 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002585 *
2586 * returns the xmlElementPtr if found or NULL
2587 */
2588
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002589static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002590xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2591 xmlElementTablePtr table;
2592 xmlElementPtr cur;
2593 xmlChar *uqname = NULL, *prefix = NULL;
2594
2595 if (dtd == NULL) return(NULL);
2596 if (dtd->elements == NULL) {
2597 if (!create)
2598 return(NULL);
2599 /*
2600 * Create the Element table if needed.
2601 */
2602 table = (xmlElementTablePtr) dtd->elements;
2603 if (table == NULL) {
2604 table = xmlCreateElementTable();
2605 dtd->elements = (void *) table;
2606 }
2607 if (table == NULL) {
2608 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002609 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002610 return(NULL);
2611 }
2612 }
2613 table = (xmlElementTablePtr) dtd->elements;
2614
2615 uqname = xmlSplitQName2(name, &prefix);
2616 if (uqname != NULL)
2617 name = uqname;
2618 cur = xmlHashLookup2(table, name, prefix);
2619 if ((cur == NULL) && (create)) {
2620 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2621 if (cur == NULL) {
2622 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002623 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002624 return(NULL);
2625 }
2626 memset(cur, 0, sizeof(xmlElement));
2627 cur->type = XML_ELEMENT_DECL;
2628
2629 /*
2630 * fill the structure.
2631 */
2632 cur->name = xmlStrdup(name);
2633 cur->prefix = xmlStrdup(prefix);
2634 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2635
2636 xmlHashAddEntry2(table, name, prefix, cur);
2637 }
2638 if (prefix != NULL) xmlFree(prefix);
2639 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002640 return(cur);
2641}
2642
2643/**
2644 * xmlGetDtdQElementDesc:
2645 * @dtd: a pointer to the DtD to search
2646 * @name: the element name
2647 * @prefix: the element namespace prefix
2648 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002649 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002650 *
2651 * returns the xmlElementPtr if found or NULL
2652 */
2653
Daniel Veillard48da9102001-08-07 01:10:10 +00002654xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002655xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2656 const xmlChar *prefix) {
2657 xmlElementTablePtr table;
2658
2659 if (dtd == NULL) return(NULL);
2660 if (dtd->elements == NULL) return(NULL);
2661 table = (xmlElementTablePtr) dtd->elements;
2662
2663 return(xmlHashLookup2(table, name, prefix));
2664}
2665
2666/**
2667 * xmlGetDtdAttrDesc:
2668 * @dtd: a pointer to the DtD to search
2669 * @elem: the element name
2670 * @name: the attribute name
2671 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002672 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002673 * this element.
2674 *
2675 * returns the xmlAttributePtr if found or NULL
2676 */
2677
2678xmlAttributePtr
2679xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2680 xmlAttributeTablePtr table;
2681 xmlAttributePtr cur;
2682 xmlChar *uqname = NULL, *prefix = NULL;
2683
2684 if (dtd == NULL) return(NULL);
2685 if (dtd->attributes == NULL) return(NULL);
2686
2687 table = (xmlAttributeTablePtr) dtd->attributes;
2688 if (table == NULL)
2689 return(NULL);
2690
2691 uqname = xmlSplitQName2(name, &prefix);
2692
2693 if (uqname != NULL) {
2694 cur = xmlHashLookup3(table, uqname, prefix, elem);
2695 if (prefix != NULL) xmlFree(prefix);
2696 if (uqname != NULL) xmlFree(uqname);
2697 } else
2698 cur = xmlHashLookup3(table, name, NULL, elem);
2699 return(cur);
2700}
2701
2702/**
2703 * xmlGetDtdQAttrDesc:
2704 * @dtd: a pointer to the DtD to search
2705 * @elem: the element name
2706 * @name: the attribute name
2707 * @prefix: the attribute namespace prefix
2708 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002709 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002710 * this element.
2711 *
2712 * returns the xmlAttributePtr if found or NULL
2713 */
2714
Daniel Veillard48da9102001-08-07 01:10:10 +00002715xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002716xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2717 const xmlChar *prefix) {
2718 xmlAttributeTablePtr table;
2719
2720 if (dtd == NULL) return(NULL);
2721 if (dtd->attributes == NULL) return(NULL);
2722 table = (xmlAttributeTablePtr) dtd->attributes;
2723
2724 return(xmlHashLookup3(table, name, prefix, elem));
2725}
2726
2727/**
2728 * xmlGetDtdNotationDesc:
2729 * @dtd: a pointer to the DtD to search
2730 * @name: the notation name
2731 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002732 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002733 *
2734 * returns the xmlNotationPtr if found or NULL
2735 */
2736
2737xmlNotationPtr
2738xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2739 xmlNotationTablePtr table;
2740
2741 if (dtd == NULL) return(NULL);
2742 if (dtd->notations == NULL) return(NULL);
2743 table = (xmlNotationTablePtr) dtd->notations;
2744
2745 return(xmlHashLookup(table, name));
2746}
2747
2748/**
2749 * xmlValidateNotationUse:
2750 * @ctxt: the validation context
2751 * @doc: the document
2752 * @notationName: the notation name to check
2753 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002754 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002755 * - [ VC: Notation Declared ]
2756 *
2757 * returns 1 if valid or 0 otherwise
2758 */
2759
2760int
2761xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2762 const xmlChar *notationName) {
2763 xmlNotationPtr notaDecl;
2764 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2765
2766 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2767 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2768 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2769
2770 if (notaDecl == NULL) {
2771 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2772 notationName);
2773 return(0);
2774 }
2775 return(1);
2776}
2777
2778/**
2779 * xmlIsMixedElement
2780 * @doc: the document
2781 * @name: the element name
2782 *
2783 * Search in the DtDs whether an element accept Mixed content (or ANY)
2784 * basically if it is supposed to accept text childs
2785 *
2786 * returns 0 if no, 1 if yes, and -1 if no element description is available
2787 */
2788
2789int
2790xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2791 xmlElementPtr elemDecl;
2792
2793 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2794
2795 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2796 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2797 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2798 if (elemDecl == NULL) return(-1);
2799 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002800 case XML_ELEMENT_TYPE_UNDEFINED:
2801 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002802 case XML_ELEMENT_TYPE_ELEMENT:
2803 return(0);
2804 case XML_ELEMENT_TYPE_EMPTY:
2805 /*
2806 * return 1 for EMPTY since we want VC error to pop up
2807 * on <empty> </empty> for example
2808 */
2809 case XML_ELEMENT_TYPE_ANY:
2810 case XML_ELEMENT_TYPE_MIXED:
2811 return(1);
2812 }
2813 return(1);
2814}
2815
2816/**
2817 * xmlValidateNameValue:
2818 * @value: an Name value
2819 *
2820 * Validate that the given value match Name production
2821 *
2822 * returns 1 if valid or 0 otherwise
2823 */
2824
Daniel Veillard9b731d72002-04-14 12:56:08 +00002825int
Owen Taylor3473f882001-02-23 17:55:21 +00002826xmlValidateNameValue(const xmlChar *value) {
2827 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002828 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002829
2830 if (value == NULL) return(0);
2831 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002832 val = xmlStringCurrentChar(NULL, cur, &len);
2833 cur += len;
2834 if (!IS_LETTER(val) && (val != '_') &&
2835 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002836 return(0);
2837 }
2838
Daniel Veillardd8224e02002-01-13 15:43:22 +00002839 val = xmlStringCurrentChar(NULL, cur, &len);
2840 cur += len;
2841 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2842 (val == '.') || (val == '-') ||
2843 (val == '_') || (val == ':') ||
2844 (IS_COMBINING(val)) ||
2845 (IS_EXTENDER(val))) {
2846 val = xmlStringCurrentChar(NULL, cur, &len);
2847 cur += len;
2848 }
Owen Taylor3473f882001-02-23 17:55:21 +00002849
Daniel Veillardd8224e02002-01-13 15:43:22 +00002850 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002851
2852 return(1);
2853}
2854
2855/**
2856 * xmlValidateNamesValue:
2857 * @value: an Names value
2858 *
2859 * Validate that the given value match Names production
2860 *
2861 * returns 1 if valid or 0 otherwise
2862 */
2863
Daniel Veillard9b731d72002-04-14 12:56:08 +00002864int
Owen Taylor3473f882001-02-23 17:55:21 +00002865xmlValidateNamesValue(const xmlChar *value) {
2866 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002867 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002868
2869 if (value == NULL) return(0);
2870 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002871 val = xmlStringCurrentChar(NULL, cur, &len);
2872 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002873
Daniel Veillardd8224e02002-01-13 15:43:22 +00002874 if (!IS_LETTER(val) && (val != '_') &&
2875 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002876 return(0);
2877 }
2878
Daniel Veillardd8224e02002-01-13 15:43:22 +00002879 val = xmlStringCurrentChar(NULL, cur, &len);
2880 cur += len;
2881 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2882 (val == '.') || (val == '-') ||
2883 (val == '_') || (val == ':') ||
2884 (IS_COMBINING(val)) ||
2885 (IS_EXTENDER(val))) {
2886 val = xmlStringCurrentChar(NULL, cur, &len);
2887 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002888 }
2889
Daniel Veillardd8224e02002-01-13 15:43:22 +00002890 while (IS_BLANK(val)) {
2891 while (IS_BLANK(val)) {
2892 val = xmlStringCurrentChar(NULL, cur, &len);
2893 cur += len;
2894 }
2895
2896 if (!IS_LETTER(val) && (val != '_') &&
2897 (val != ':')) {
2898 return(0);
2899 }
2900 val = xmlStringCurrentChar(NULL, cur, &len);
2901 cur += len;
2902
2903 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2904 (val == '.') || (val == '-') ||
2905 (val == '_') || (val == ':') ||
2906 (IS_COMBINING(val)) ||
2907 (IS_EXTENDER(val))) {
2908 val = xmlStringCurrentChar(NULL, cur, &len);
2909 cur += len;
2910 }
2911 }
2912
2913 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002914
2915 return(1);
2916}
2917
2918/**
2919 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002920 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002921 *
2922 * Validate that the given value match Nmtoken production
2923 *
2924 * [ VC: Name Token ]
2925 *
2926 * returns 1 if valid or 0 otherwise
2927 */
2928
Daniel Veillard9b731d72002-04-14 12:56:08 +00002929int
Owen Taylor3473f882001-02-23 17:55:21 +00002930xmlValidateNmtokenValue(const xmlChar *value) {
2931 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002932 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002933
2934 if (value == NULL) return(0);
2935 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002936 val = xmlStringCurrentChar(NULL, cur, &len);
2937 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002938
Daniel Veillardd8224e02002-01-13 15:43:22 +00002939 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2940 (val != '.') && (val != '-') &&
2941 (val != '_') && (val != ':') &&
2942 (!IS_COMBINING(val)) &&
2943 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002944 return(0);
2945
Daniel Veillardd8224e02002-01-13 15:43:22 +00002946 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2947 (val == '.') || (val == '-') ||
2948 (val == '_') || (val == ':') ||
2949 (IS_COMBINING(val)) ||
2950 (IS_EXTENDER(val))) {
2951 val = xmlStringCurrentChar(NULL, cur, &len);
2952 cur += len;
2953 }
Owen Taylor3473f882001-02-23 17:55:21 +00002954
Daniel Veillardd8224e02002-01-13 15:43:22 +00002955 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002956
2957 return(1);
2958}
2959
2960/**
2961 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002962 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002963 *
2964 * Validate that the given value match Nmtokens production
2965 *
2966 * [ VC: Name Token ]
2967 *
2968 * returns 1 if valid or 0 otherwise
2969 */
2970
Daniel Veillard9b731d72002-04-14 12:56:08 +00002971int
Owen Taylor3473f882001-02-23 17:55:21 +00002972xmlValidateNmtokensValue(const xmlChar *value) {
2973 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002974 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002975
2976 if (value == NULL) return(0);
2977 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002978 val = xmlStringCurrentChar(NULL, cur, &len);
2979 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002980
Daniel Veillardd8224e02002-01-13 15:43:22 +00002981 while (IS_BLANK(val)) {
2982 val = xmlStringCurrentChar(NULL, cur, &len);
2983 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002984 }
2985
Daniel Veillardd8224e02002-01-13 15:43:22 +00002986 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2987 (val != '.') && (val != '-') &&
2988 (val != '_') && (val != ':') &&
2989 (!IS_COMBINING(val)) &&
2990 (!IS_EXTENDER(val)))
2991 return(0);
2992
2993 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2994 (val == '.') || (val == '-') ||
2995 (val == '_') || (val == ':') ||
2996 (IS_COMBINING(val)) ||
2997 (IS_EXTENDER(val))) {
2998 val = xmlStringCurrentChar(NULL, cur, &len);
2999 cur += len;
3000 }
3001
3002 while (IS_BLANK(val)) {
3003 while (IS_BLANK(val)) {
3004 val = xmlStringCurrentChar(NULL, cur, &len);
3005 cur += len;
3006 }
3007 if (val == 0) return(1);
3008
3009 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3010 (val != '.') && (val != '-') &&
3011 (val != '_') && (val != ':') &&
3012 (!IS_COMBINING(val)) &&
3013 (!IS_EXTENDER(val)))
3014 return(0);
3015
3016 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3017 (val == '.') || (val == '-') ||
3018 (val == '_') || (val == ':') ||
3019 (IS_COMBINING(val)) ||
3020 (IS_EXTENDER(val))) {
3021 val = xmlStringCurrentChar(NULL, cur, &len);
3022 cur += len;
3023 }
3024 }
3025
3026 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003027
3028 return(1);
3029}
3030
3031/**
3032 * xmlValidateNotationDecl:
3033 * @ctxt: the validation context
3034 * @doc: a document instance
3035 * @nota: a notation definition
3036 *
3037 * Try to validate a single notation definition
3038 * basically it does the following checks as described by the
3039 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003040 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003041 * But this function get called anyway ...
3042 *
3043 * returns 1 if valid or 0 otherwise
3044 */
3045
3046int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003047xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3048 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003049 int ret = 1;
3050
3051 return(ret);
3052}
3053
3054/**
3055 * xmlValidateAttributeValue:
3056 * @type: an attribute type
3057 * @value: an attribute value
3058 *
3059 * Validate that the given attribute value match the proper production
3060 *
3061 * [ VC: ID ]
3062 * Values of type ID must match the Name production....
3063 *
3064 * [ VC: IDREF ]
3065 * Values of type IDREF must match the Name production, and values
3066 * of type IDREFS must match Names ...
3067 *
3068 * [ VC: Entity Name ]
3069 * Values of type ENTITY must match the Name production, values
3070 * of type ENTITIES must match Names ...
3071 *
3072 * [ VC: Name Token ]
3073 * Values of type NMTOKEN must match the Nmtoken production; values
3074 * of type NMTOKENS must match Nmtokens.
3075 *
3076 * returns 1 if valid or 0 otherwise
3077 */
3078
3079int
3080xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3081 switch (type) {
3082 case XML_ATTRIBUTE_ENTITIES:
3083 case XML_ATTRIBUTE_IDREFS:
3084 return(xmlValidateNamesValue(value));
3085 case XML_ATTRIBUTE_ENTITY:
3086 case XML_ATTRIBUTE_IDREF:
3087 case XML_ATTRIBUTE_ID:
3088 case XML_ATTRIBUTE_NOTATION:
3089 return(xmlValidateNameValue(value));
3090 case XML_ATTRIBUTE_NMTOKENS:
3091 case XML_ATTRIBUTE_ENUMERATION:
3092 return(xmlValidateNmtokensValue(value));
3093 case XML_ATTRIBUTE_NMTOKEN:
3094 return(xmlValidateNmtokenValue(value));
3095 case XML_ATTRIBUTE_CDATA:
3096 break;
3097 }
3098 return(1);
3099}
3100
3101/**
3102 * xmlValidateAttributeValue2:
3103 * @ctxt: the validation context
3104 * @doc: the document
3105 * @name: the attribute name (used for error reporting only)
3106 * @type: the attribute type
3107 * @value: the attribute value
3108 *
3109 * Validate that the given attribute value match a given type.
3110 * This typically cannot be done before having finished parsing
3111 * the subsets.
3112 *
3113 * [ VC: IDREF ]
3114 * Values of type IDREF must match one of the declared IDs
3115 * Values of type IDREFS must match a sequence of the declared IDs
3116 * each Name must match the value of an ID attribute on some element
3117 * in the XML document; i.e. IDREF values must match the value of
3118 * some ID attribute
3119 *
3120 * [ VC: Entity Name ]
3121 * Values of type ENTITY must match one declared entity
3122 * Values of type ENTITIES must match a sequence of declared entities
3123 *
3124 * [ VC: Notation Attributes ]
3125 * all notation names in the declaration must be declared.
3126 *
3127 * returns 1 if valid or 0 otherwise
3128 */
3129
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003130static int
Owen Taylor3473f882001-02-23 17:55:21 +00003131xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3132 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3133 int ret = 1;
3134 switch (type) {
3135 case XML_ATTRIBUTE_IDREFS:
3136 case XML_ATTRIBUTE_IDREF:
3137 case XML_ATTRIBUTE_ID:
3138 case XML_ATTRIBUTE_NMTOKENS:
3139 case XML_ATTRIBUTE_ENUMERATION:
3140 case XML_ATTRIBUTE_NMTOKEN:
3141 case XML_ATTRIBUTE_CDATA:
3142 break;
3143 case XML_ATTRIBUTE_ENTITY: {
3144 xmlEntityPtr ent;
3145
3146 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003147 if ((ent == NULL) && (doc->standalone == 1)) {
3148 doc->standalone = 0;
3149 ent = xmlGetDocEntity(doc, value);
3150 if (ent != NULL) {
3151 VERROR(ctxt->userData,
3152"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3153 name, value);
3154 /* WAIT to get answer from the Core WG on this
3155 ret = 0;
3156 */
3157 }
3158 }
Owen Taylor3473f882001-02-23 17:55:21 +00003159 if (ent == NULL) {
3160 VERROR(ctxt->userData,
3161 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3162 name, value);
3163 ret = 0;
3164 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3165 VERROR(ctxt->userData,
3166 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3167 name, value);
3168 ret = 0;
3169 }
3170 break;
3171 }
3172 case XML_ATTRIBUTE_ENTITIES: {
3173 xmlChar *dup, *nam = NULL, *cur, save;
3174 xmlEntityPtr ent;
3175
3176 dup = xmlStrdup(value);
3177 if (dup == NULL)
3178 return(0);
3179 cur = dup;
3180 while (*cur != 0) {
3181 nam = cur;
3182 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3183 save = *cur;
3184 *cur = 0;
3185 ent = xmlGetDocEntity(doc, nam);
3186 if (ent == NULL) {
3187 VERROR(ctxt->userData,
3188 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3189 name, nam);
3190 ret = 0;
3191 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3192 VERROR(ctxt->userData,
3193 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3194 name, nam);
3195 ret = 0;
3196 }
3197 if (save == 0)
3198 break;
3199 *cur = save;
3200 while (IS_BLANK(*cur)) cur++;
3201 }
3202 xmlFree(dup);
3203 break;
3204 }
3205 case XML_ATTRIBUTE_NOTATION: {
3206 xmlNotationPtr nota;
3207
3208 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3209 if ((nota == NULL) && (doc->extSubset != NULL))
3210 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3211
3212 if (nota == NULL) {
3213 VERROR(ctxt->userData,
3214 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3215 name, value);
3216 ret = 0;
3217 }
3218 break;
3219 }
3220 }
3221 return(ret);
3222}
3223
3224/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003225 * xmlValidCtxtNormalizeAttributeValue:
3226 * @ctxt: the validation context
3227 * @doc: the document
3228 * @elem: the parent
3229 * @name: the attribute name
3230 * @value: the attribute value
3231 * @ctxt: the validation context or NULL
3232 *
3233 * Does the validation related extra step of the normalization of attribute
3234 * values:
3235 *
3236 * If the declared value is not CDATA, then the XML processor must further
3237 * process the normalized attribute value by discarding any leading and
3238 * trailing space (#x20) characters, and by replacing sequences of space
3239 * (#x20) characters by single space (#x20) character.
3240 *
3241 * Also check VC: Standalone Document Declaration in P32, and update
3242 * ctxt->valid accordingly
3243 *
3244 * returns a new normalized string if normalization is needed, NULL otherwise
3245 * the caller must free the returned value.
3246 */
3247
3248xmlChar *
3249xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3250 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3251 xmlChar *ret, *dst;
3252 const xmlChar *src;
3253 xmlAttributePtr attrDecl = NULL;
3254 int extsubset = 0;
3255
3256 if (doc == NULL) return(NULL);
3257 if (elem == NULL) return(NULL);
3258 if (name == NULL) return(NULL);
3259 if (value == NULL) return(NULL);
3260
3261 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3262 xmlChar qname[500];
3263 snprintf((char *) qname, sizeof(qname), "%s:%s",
3264 elem->ns->prefix, elem->name);
3265 qname[sizeof(qname) - 1] = 0;
3266 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3267 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3268 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3269 if (attrDecl != NULL)
3270 extsubset = 1;
3271 }
3272 }
3273 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3274 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3275 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3276 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3277 if (attrDecl != NULL)
3278 extsubset = 1;
3279 }
3280
3281 if (attrDecl == NULL)
3282 return(NULL);
3283 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3284 return(NULL);
3285
3286 ret = xmlStrdup(value);
3287 if (ret == NULL)
3288 return(NULL);
3289 src = value;
3290 dst = ret;
3291 while (*src == 0x20) src++;
3292 while (*src != 0) {
3293 if (*src == 0x20) {
3294 while (*src == 0x20) src++;
3295 if (*src != 0)
3296 *dst++ = 0x20;
3297 } else {
3298 *dst++ = *src++;
3299 }
3300 }
3301 *dst = 0;
3302 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3303 VERROR(ctxt->userData,
3304"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3305 name, elem->name);
3306 ctxt->valid = 0;
3307 }
3308 return(ret);
3309}
3310
3311/**
Owen Taylor3473f882001-02-23 17:55:21 +00003312 * xmlValidNormalizeAttributeValue:
3313 * @doc: the document
3314 * @elem: the parent
3315 * @name: the attribute name
3316 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003317 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003318 *
3319 * Does the validation related extra step of the normalization of attribute
3320 * values:
3321 *
3322 * If the declared value is not CDATA, then the XML processor must further
3323 * process the normalized attribute value by discarding any leading and
3324 * trailing space (#x20) characters, and by replacing sequences of space
3325 * (#x20) characters by single space (#x20) character.
3326 *
3327 * returns a new normalized string if normalization is needed, NULL otherwise
3328 * the caller must free the returned value.
3329 */
3330
3331xmlChar *
3332xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3333 const xmlChar *name, const xmlChar *value) {
3334 xmlChar *ret, *dst;
3335 const xmlChar *src;
3336 xmlAttributePtr attrDecl = NULL;
3337
3338 if (doc == NULL) return(NULL);
3339 if (elem == NULL) return(NULL);
3340 if (name == NULL) return(NULL);
3341 if (value == NULL) return(NULL);
3342
3343 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3344 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003345 snprintf((char *) qname, sizeof(qname), "%s:%s",
3346 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003347 qname[sizeof(qname) - 1] = 0;
3348 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3349 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3350 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3351 }
3352 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3353 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3354 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3355
3356 if (attrDecl == NULL)
3357 return(NULL);
3358 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3359 return(NULL);
3360
3361 ret = xmlStrdup(value);
3362 if (ret == NULL)
3363 return(NULL);
3364 src = value;
3365 dst = ret;
3366 while (*src == 0x20) src++;
3367 while (*src != 0) {
3368 if (*src == 0x20) {
3369 while (*src == 0x20) src++;
3370 if (*src != 0)
3371 *dst++ = 0x20;
3372 } else {
3373 *dst++ = *src++;
3374 }
3375 }
3376 *dst = 0;
3377 return(ret);
3378}
3379
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003380static void
Owen Taylor3473f882001-02-23 17:55:21 +00003381xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003382 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003383 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3384}
3385
3386/**
3387 * xmlValidateAttributeDecl:
3388 * @ctxt: the validation context
3389 * @doc: a document instance
3390 * @attr: an attribute definition
3391 *
3392 * Try to validate a single attribute definition
3393 * basically it does the following checks as described by the
3394 * XML-1.0 recommendation:
3395 * - [ VC: Attribute Default Legal ]
3396 * - [ VC: Enumeration ]
3397 * - [ VC: ID Attribute Default ]
3398 *
3399 * The ID/IDREF uniqueness and matching are done separately
3400 *
3401 * returns 1 if valid or 0 otherwise
3402 */
3403
3404int
3405xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3406 xmlAttributePtr attr) {
3407 int ret = 1;
3408 int val;
3409 CHECK_DTD;
3410 if(attr == NULL) return(1);
3411
3412 /* Attribute Default Legal */
3413 /* Enumeration */
3414 if (attr->defaultValue != NULL) {
3415 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3416 if (val == 0) {
3417 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003418 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003419 attr->name, attr->elem);
3420 }
3421 ret &= val;
3422 }
3423
3424 /* ID Attribute Default */
3425 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3426 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3427 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3428 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003429 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003430 attr->name, attr->elem);
3431 ret = 0;
3432 }
3433
3434 /* One ID per Element Type */
3435 if (attr->atype == XML_ATTRIBUTE_ID) {
3436 int nbId;
3437
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003438 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003439 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3440 attr->elem);
3441 if (elem != NULL) {
3442 nbId = xmlScanIDAttributeDecl(NULL, elem);
3443 } else {
3444 xmlAttributeTablePtr table;
3445
3446 /*
3447 * The attribute may be declared in the internal subset and the
3448 * element in the external subset.
3449 */
3450 nbId = 0;
3451 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3452 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3453 xmlValidateAttributeIdCallback, &nbId);
3454 }
3455 if (nbId > 1) {
3456 VERROR(ctxt->userData,
3457 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3458 attr->elem, nbId, attr->name);
3459 } else if (doc->extSubset != NULL) {
3460 int extId = 0;
3461 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3462 if (elem != NULL) {
3463 extId = xmlScanIDAttributeDecl(NULL, elem);
3464 }
3465 if (extId > 1) {
3466 VERROR(ctxt->userData,
3467 "Element %s has %d ID attribute defined in the external subset : %s\n",
3468 attr->elem, extId, attr->name);
3469 } else if (extId + nbId > 1) {
3470 VERROR(ctxt->userData,
3471"Element %s has ID attributes defined in the internal and external subset : %s\n",
3472 attr->elem, attr->name);
3473 }
3474 }
3475 }
3476
3477 /* Validity Constraint: Enumeration */
3478 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3479 xmlEnumerationPtr tree = attr->tree;
3480 while (tree != NULL) {
3481 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3482 tree = tree->next;
3483 }
3484 if (tree == NULL) {
3485 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003486"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003487 attr->defaultValue, attr->name, attr->elem);
3488 ret = 0;
3489 }
3490 }
3491
3492 return(ret);
3493}
3494
3495/**
3496 * xmlValidateElementDecl:
3497 * @ctxt: the validation context
3498 * @doc: a document instance
3499 * @elem: an element definition
3500 *
3501 * Try to validate a single element definition
3502 * basically it does the following checks as described by the
3503 * XML-1.0 recommendation:
3504 * - [ VC: One ID per Element Type ]
3505 * - [ VC: No Duplicate Types ]
3506 * - [ VC: Unique Element Type Declaration ]
3507 *
3508 * returns 1 if valid or 0 otherwise
3509 */
3510
3511int
3512xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3513 xmlElementPtr elem) {
3514 int ret = 1;
3515 xmlElementPtr tst;
3516
3517 CHECK_DTD;
3518
3519 if (elem == NULL) return(1);
3520
Daniel Veillard84d70a42002-09-16 10:51:38 +00003521#ifdef LIBXML_REGEXP_ENABLED
3522 /* Build the regexp associated to the content model */
3523 ret = xmlValidBuildContentModel(ctxt, elem);
3524#endif
3525
Owen Taylor3473f882001-02-23 17:55:21 +00003526 /* No Duplicate Types */
3527 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3528 xmlElementContentPtr cur, next;
3529 const xmlChar *name;
3530
3531 cur = elem->content;
3532 while (cur != NULL) {
3533 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3534 if (cur->c1 == NULL) break;
3535 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3536 name = cur->c1->name;
3537 next = cur->c2;
3538 while (next != NULL) {
3539 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3540 if (xmlStrEqual(next->name, name)) {
3541 VERROR(ctxt->userData,
3542 "Definition of %s has duplicate references of %s\n",
3543 elem->name, name);
3544 ret = 0;
3545 }
3546 break;
3547 }
3548 if (next->c1 == NULL) break;
3549 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3550 if (xmlStrEqual(next->c1->name, name)) {
3551 VERROR(ctxt->userData,
3552 "Definition of %s has duplicate references of %s\n",
3553 elem->name, name);
3554 ret = 0;
3555 }
3556 next = next->c2;
3557 }
3558 }
3559 cur = cur->c2;
3560 }
3561 }
3562
3563 /* VC: Unique Element Type Declaration */
3564 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003565 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003566 ((tst->prefix == elem->prefix) ||
3567 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003568 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003569 VERROR(ctxt->userData, "Redefinition of element %s\n",
3570 elem->name);
3571 ret = 0;
3572 }
3573 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003574 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003575 ((tst->prefix == elem->prefix) ||
3576 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003577 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003578 VERROR(ctxt->userData, "Redefinition of element %s\n",
3579 elem->name);
3580 ret = 0;
3581 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003582 /* One ID per Element Type
3583 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003584 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3585 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003586 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003587 return(ret);
3588}
3589
3590/**
3591 * xmlValidateOneAttribute:
3592 * @ctxt: the validation context
3593 * @doc: a document instance
3594 * @elem: an element instance
3595 * @attr: an attribute instance
3596 * @value: the attribute value (without entities processing)
3597 *
3598 * Try to validate a single attribute for an element
3599 * basically it does the following checks as described by the
3600 * XML-1.0 recommendation:
3601 * - [ VC: Attribute Value Type ]
3602 * - [ VC: Fixed Attribute Default ]
3603 * - [ VC: Entity Name ]
3604 * - [ VC: Name Token ]
3605 * - [ VC: ID ]
3606 * - [ VC: IDREF ]
3607 * - [ VC: Entity Name ]
3608 * - [ VC: Notation Attributes ]
3609 *
3610 * The ID/IDREF uniqueness and matching are done separately
3611 *
3612 * returns 1 if valid or 0 otherwise
3613 */
3614
3615int
3616xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3617 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3618 /* xmlElementPtr elemDecl; */
3619 xmlAttributePtr attrDecl = NULL;
3620 int val;
3621 int ret = 1;
3622
3623 CHECK_DTD;
3624 if ((elem == NULL) || (elem->name == NULL)) return(0);
3625 if ((attr == NULL) || (attr->name == NULL)) return(0);
3626
3627 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3628 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003629 snprintf((char *) qname, sizeof(qname), "%s:%s",
3630 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003631 qname[sizeof(qname) - 1] = 0;
3632 if (attr->ns != NULL) {
3633 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3634 attr->name, attr->ns->prefix);
3635 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3636 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3637 attr->name, attr->ns->prefix);
3638 } else {
3639 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3640 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3641 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3642 qname, attr->name);
3643 }
3644 }
3645 if (attrDecl == NULL) {
3646 if (attr->ns != NULL) {
3647 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3648 attr->name, attr->ns->prefix);
3649 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3650 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3651 attr->name, attr->ns->prefix);
3652 } else {
3653 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3654 elem->name, attr->name);
3655 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3656 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3657 elem->name, attr->name);
3658 }
3659 }
3660
3661
3662 /* Validity Constraint: Attribute Value Type */
3663 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003664 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003665 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003666 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003667 attr->name, elem->name);
3668 return(0);
3669 }
3670 attr->atype = attrDecl->atype;
3671
3672 val = xmlValidateAttributeValue(attrDecl->atype, value);
3673 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003674 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003675 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003676 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003677 attr->name, elem->name);
3678 ret = 0;
3679 }
3680
3681 /* Validity constraint: Fixed Attribute Default */
3682 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3683 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003684 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003685 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003686 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003687 attr->name, elem->name, attrDecl->defaultValue);
3688 ret = 0;
3689 }
3690 }
3691
3692 /* Validity Constraint: ID uniqueness */
3693 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3694 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3695 ret = 0;
3696 }
3697
3698 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3699 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3700 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3701 ret = 0;
3702 }
3703
3704 /* Validity Constraint: Notation Attributes */
3705 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3706 xmlEnumerationPtr tree = attrDecl->tree;
3707 xmlNotationPtr nota;
3708
3709 /* First check that the given NOTATION was declared */
3710 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3711 if (nota == NULL)
3712 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3713
3714 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003715 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003716 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003717 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003718 value, attr->name, elem->name);
3719 ret = 0;
3720 }
3721
3722 /* Second, verify that it's among the list */
3723 while (tree != NULL) {
3724 if (xmlStrEqual(tree->name, value)) break;
3725 tree = tree->next;
3726 }
3727 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003728 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003729 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003730"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003731 value, attr->name, elem->name);
3732 ret = 0;
3733 }
3734 }
3735
3736 /* Validity Constraint: Enumeration */
3737 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3738 xmlEnumerationPtr tree = attrDecl->tree;
3739 while (tree != NULL) {
3740 if (xmlStrEqual(tree->name, value)) break;
3741 tree = tree->next;
3742 }
3743 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003744 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003745 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003746 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003747 value, attr->name, elem->name);
3748 ret = 0;
3749 }
3750 }
3751
3752 /* Fixed Attribute Default */
3753 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3754 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003755 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003756 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003757 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003758 attr->name, elem->name, attrDecl->defaultValue);
3759 ret = 0;
3760 }
3761
3762 /* Extra check for the attribute value */
3763 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3764 attrDecl->atype, value);
3765
3766 return(ret);
3767}
3768
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003769/**
3770 * xmlValidateSkipIgnorable:
3771 * @ctxt: the validation context
3772 * @child: the child list
3773 *
3774 * Skip ignorable elements w.r.t. the validation process
3775 *
3776 * returns the first element to consider for validation of the content model
3777 */
3778
3779static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003780xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003781 while (child != NULL) {
3782 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003783 /* These things are ignored (skipped) during validation. */
3784 case XML_PI_NODE:
3785 case XML_COMMENT_NODE:
3786 case XML_XINCLUDE_START:
3787 case XML_XINCLUDE_END:
3788 child = child->next;
3789 break;
3790 case XML_TEXT_NODE:
3791 if (xmlIsBlankNode(child))
3792 child = child->next;
3793 else
3794 return(child);
3795 break;
3796 /* keep current node */
3797 default:
3798 return(child);
3799 }
3800 }
3801 return(child);
3802}
3803
3804/**
3805 * xmlValidateElementType:
3806 * @ctxt: the validation context
3807 *
3808 * Try to validate the content model of an element internal function
3809 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003810 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3811 * reference is found and -3 if the validation succeeded but
3812 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003813 */
3814
3815static int
3816xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003817 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003818 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003819
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003820 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003821 if ((NODE == NULL) && (CONT == NULL))
3822 return(1);
3823 if ((NODE == NULL) &&
3824 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3825 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3826 return(1);
3827 }
3828 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003829 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003830 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003831
3832 /*
3833 * We arrive here when more states need to be examined
3834 */
3835cont:
3836
3837 /*
3838 * We just recovered from a rollback generated by a possible
3839 * epsilon transition, go directly to the analysis phase
3840 */
3841 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003842 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003843 DEBUG_VALID_STATE(NODE, CONT)
3844 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003845 goto analyze;
3846 }
3847
3848 DEBUG_VALID_STATE(NODE, CONT)
3849 /*
3850 * we may have to save a backup state here. This is the equivalent
3851 * of handling epsilon transition in NFAs.
3852 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003853 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003854 ((CONT->parent == NULL) ||
3855 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003856 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003857 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003858 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003859 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003860 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3861 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003862 }
3863
3864
3865 /*
3866 * Check first if the content matches
3867 */
3868 switch (CONT->type) {
3869 case XML_ELEMENT_CONTENT_PCDATA:
3870 if (NODE == NULL) {
3871 DEBUG_VALID_MSG("pcdata failed no node");
3872 ret = 0;
3873 break;
3874 }
3875 if (NODE->type == XML_TEXT_NODE) {
3876 DEBUG_VALID_MSG("pcdata found, skip to next");
3877 /*
3878 * go to next element in the content model
3879 * skipping ignorable elems
3880 */
3881 do {
3882 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003883 NODE = xmlValidateSkipIgnorable(NODE);
3884 if ((NODE != NULL) &&
3885 (NODE->type == XML_ENTITY_REF_NODE))
3886 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003887 } while ((NODE != NULL) &&
3888 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003889 (NODE->type != XML_TEXT_NODE) &&
3890 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003891 ret = 1;
3892 break;
3893 } else {
3894 DEBUG_VALID_MSG("pcdata failed");
3895 ret = 0;
3896 break;
3897 }
3898 break;
3899 case XML_ELEMENT_CONTENT_ELEMENT:
3900 if (NODE == NULL) {
3901 DEBUG_VALID_MSG("element failed no node");
3902 ret = 0;
3903 break;
3904 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003905 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3906 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003907 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003908 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3909 ret = (CONT->prefix == NULL);
3910 } else if (CONT->prefix == NULL) {
3911 ret = 0;
3912 } else {
3913 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3914 }
3915 }
3916 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003917 DEBUG_VALID_MSG("element found, skip to next");
3918 /*
3919 * go to next element in the content model
3920 * skipping ignorable elems
3921 */
3922 do {
3923 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003924 NODE = xmlValidateSkipIgnorable(NODE);
3925 if ((NODE != NULL) &&
3926 (NODE->type == XML_ENTITY_REF_NODE))
3927 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003928 } while ((NODE != NULL) &&
3929 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003930 (NODE->type != XML_TEXT_NODE) &&
3931 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003932 } else {
3933 DEBUG_VALID_MSG("element failed");
3934 ret = 0;
3935 break;
3936 }
3937 break;
3938 case XML_ELEMENT_CONTENT_OR:
3939 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003940 * Small optimization.
3941 */
3942 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3943 if ((NODE == NULL) ||
3944 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3945 DEPTH++;
3946 CONT = CONT->c2;
3947 goto cont;
3948 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003949 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3950 ret = (CONT->c1->prefix == NULL);
3951 } else if (CONT->c1->prefix == NULL) {
3952 ret = 0;
3953 } else {
3954 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3955 }
3956 if (ret == 0) {
3957 DEPTH++;
3958 CONT = CONT->c2;
3959 goto cont;
3960 }
Daniel Veillard85349052001-04-20 13:48:21 +00003961 }
3962
3963 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003964 * save the second branch 'or' branch
3965 */
3966 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003967 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3968 OCCURS, ROLLBACK_OR) < 0)
3969 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003970 DEPTH++;
3971 CONT = CONT->c1;
3972 goto cont;
3973 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003974 /*
3975 * Small optimization.
3976 */
3977 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3978 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3979 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3980 if ((NODE == NULL) ||
3981 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3982 DEPTH++;
3983 CONT = CONT->c2;
3984 goto cont;
3985 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003986 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3987 ret = (CONT->c1->prefix == NULL);
3988 } else if (CONT->c1->prefix == NULL) {
3989 ret = 0;
3990 } else {
3991 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3992 }
3993 if (ret == 0) {
3994 DEPTH++;
3995 CONT = CONT->c2;
3996 goto cont;
3997 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003998 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003999 DEPTH++;
4000 CONT = CONT->c1;
4001 goto cont;
4002 }
4003
4004 /*
4005 * At this point handle going up in the tree
4006 */
4007 if (ret == -1) {
4008 DEBUG_VALID_MSG("error found returning");
4009 return(ret);
4010 }
4011analyze:
4012 while (CONT != NULL) {
4013 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004014 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004015 * this level.
4016 */
4017 if (ret == 0) {
4018 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004019 xmlNodePtr cur;
4020
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004021 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004022 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004023 DEBUG_VALID_MSG("Once branch failed, rollback");
4024 if (vstateVPop(ctxt) < 0 ) {
4025 DEBUG_VALID_MSG("exhaustion, failed");
4026 return(0);
4027 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004028 if (cur != ctxt->vstate->node)
4029 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004030 goto cont;
4031 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004032 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004033 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004034 DEBUG_VALID_MSG("Plus branch failed, rollback");
4035 if (vstateVPop(ctxt) < 0 ) {
4036 DEBUG_VALID_MSG("exhaustion, failed");
4037 return(0);
4038 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004039 if (cur != ctxt->vstate->node)
4040 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004041 goto cont;
4042 }
4043 DEBUG_VALID_MSG("Plus branch found");
4044 ret = 1;
4045 break;
4046 case XML_ELEMENT_CONTENT_MULT:
4047#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004048 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004049 DEBUG_VALID_MSG("Mult branch failed");
4050 } else {
4051 DEBUG_VALID_MSG("Mult branch found");
4052 }
4053#endif
4054 ret = 1;
4055 break;
4056 case XML_ELEMENT_CONTENT_OPT:
4057 DEBUG_VALID_MSG("Option branch failed");
4058 ret = 1;
4059 break;
4060 }
4061 } else {
4062 switch (CONT->ocur) {
4063 case XML_ELEMENT_CONTENT_OPT:
4064 DEBUG_VALID_MSG("Option branch succeeded");
4065 ret = 1;
4066 break;
4067 case XML_ELEMENT_CONTENT_ONCE:
4068 DEBUG_VALID_MSG("Once branch succeeded");
4069 ret = 1;
4070 break;
4071 case XML_ELEMENT_CONTENT_PLUS:
4072 if (STATE == ROLLBACK_PARENT) {
4073 DEBUG_VALID_MSG("Plus branch rollback");
4074 ret = 1;
4075 break;
4076 }
4077 if (NODE == NULL) {
4078 DEBUG_VALID_MSG("Plus branch exhausted");
4079 ret = 1;
4080 break;
4081 }
4082 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004083 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004084 goto cont;
4085 case XML_ELEMENT_CONTENT_MULT:
4086 if (STATE == ROLLBACK_PARENT) {
4087 DEBUG_VALID_MSG("Mult branch rollback");
4088 ret = 1;
4089 break;
4090 }
4091 if (NODE == NULL) {
4092 DEBUG_VALID_MSG("Mult branch exhausted");
4093 ret = 1;
4094 break;
4095 }
4096 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004097 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004098 goto cont;
4099 }
4100 }
4101 STATE = 0;
4102
4103 /*
4104 * Then act accordingly at the parent level
4105 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004106 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004107 if (CONT->parent == NULL)
4108 break;
4109
4110 switch (CONT->parent->type) {
4111 case XML_ELEMENT_CONTENT_PCDATA:
4112 DEBUG_VALID_MSG("Error: parent pcdata");
4113 return(-1);
4114 case XML_ELEMENT_CONTENT_ELEMENT:
4115 DEBUG_VALID_MSG("Error: parent element");
4116 return(-1);
4117 case XML_ELEMENT_CONTENT_OR:
4118 if (ret == 1) {
4119 DEBUG_VALID_MSG("Or succeeded");
4120 CONT = CONT->parent;
4121 DEPTH--;
4122 } else {
4123 DEBUG_VALID_MSG("Or failed");
4124 CONT = CONT->parent;
4125 DEPTH--;
4126 }
4127 break;
4128 case XML_ELEMENT_CONTENT_SEQ:
4129 if (ret == 0) {
4130 DEBUG_VALID_MSG("Sequence failed");
4131 CONT = CONT->parent;
4132 DEPTH--;
4133 } else if (CONT == CONT->parent->c1) {
4134 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4135 CONT = CONT->parent->c2;
4136 goto cont;
4137 } else {
4138 DEBUG_VALID_MSG("Sequence succeeded");
4139 CONT = CONT->parent;
4140 DEPTH--;
4141 }
4142 }
4143 }
4144 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004145 xmlNodePtr cur;
4146
4147 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004148 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4149 if (vstateVPop(ctxt) < 0 ) {
4150 DEBUG_VALID_MSG("exhaustion, failed");
4151 return(0);
4152 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004153 if (cur != ctxt->vstate->node)
4154 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004155 goto cont;
4156 }
4157 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004158 xmlNodePtr cur;
4159
4160 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004161 DEBUG_VALID_MSG("Failure, rollback");
4162 if (vstateVPop(ctxt) < 0 ) {
4163 DEBUG_VALID_MSG("exhaustion, failed");
4164 return(0);
4165 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004166 if (cur != ctxt->vstate->node)
4167 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004168 goto cont;
4169 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004170 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004171}
4172
4173/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004174 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004175 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004176 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004177 * @content: An element
4178 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4179 *
4180 * This will dump the list of elements to the buffer
4181 * Intended just for the debug routine
4182 */
4183static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004184xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004185 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004186 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004187
4188 if (node == NULL) return;
4189 if (glob) strcat(buf, "(");
4190 cur = node;
4191 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004192 len = strlen(buf);
4193 if (size - len < 50) {
4194 if ((size - len > 4) && (buf[len - 1] != '.'))
4195 strcat(buf, " ...");
4196 return;
4197 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004198 switch (cur->type) {
4199 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004200 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004201 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004202 if ((size - len > 4) && (buf[len - 1] != '.'))
4203 strcat(buf, " ...");
4204 return;
4205 }
4206 strcat(buf, (char *) cur->ns->prefix);
4207 strcat(buf, ":");
4208 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004209 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004210 if ((size - len > 4) && (buf[len - 1] != '.'))
4211 strcat(buf, " ...");
4212 return;
4213 }
4214 strcat(buf, (char *) cur->name);
4215 if (cur->next != NULL)
4216 strcat(buf, " ");
4217 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004218 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004219 if (xmlIsBlankNode(cur))
4220 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004221 case XML_CDATA_SECTION_NODE:
4222 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004223 strcat(buf, "CDATA");
4224 if (cur->next != NULL)
4225 strcat(buf, " ");
4226 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004227 case XML_ATTRIBUTE_NODE:
4228 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004229#ifdef LIBXML_DOCB_ENABLED
4230 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004231#endif
4232 case XML_HTML_DOCUMENT_NODE:
4233 case XML_DOCUMENT_TYPE_NODE:
4234 case XML_DOCUMENT_FRAG_NODE:
4235 case XML_NOTATION_NODE:
4236 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004237 strcat(buf, "???");
4238 if (cur->next != NULL)
4239 strcat(buf, " ");
4240 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004241 case XML_ENTITY_NODE:
4242 case XML_PI_NODE:
4243 case XML_DTD_NODE:
4244 case XML_COMMENT_NODE:
4245 case XML_ELEMENT_DECL:
4246 case XML_ATTRIBUTE_DECL:
4247 case XML_ENTITY_DECL:
4248 case XML_XINCLUDE_START:
4249 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004250 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004251 }
4252 cur = cur->next;
4253 }
4254 if (glob) strcat(buf, ")");
4255}
4256
4257/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004258 * xmlValidateElementContent:
4259 * @ctxt: the validation context
4260 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004261 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004262 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004263 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004264 *
4265 * Try to validate the content model of an element
4266 *
4267 * returns 1 if valid or 0 if not and -1 in case of error
4268 */
4269
4270static int
4271xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004272 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004273 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004274 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004275 xmlElementContentPtr cont;
4276 const xmlChar *name;
4277
4278 if (elemDecl == NULL)
4279 return(-1);
4280 cont = elemDecl->content;
4281 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004282
4283 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004284 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004285 */
4286 ctxt->vstateMax = 8;
4287 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4288 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4289 if (ctxt->vstateTab == NULL) {
4290 xmlGenericError(xmlGenericErrorContext,
4291 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004292 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004293 }
4294 /*
4295 * The first entry in the stack is reserved to the current state
4296 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004297 ctxt->nodeMax = 0;
4298 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004299 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004300 ctxt->vstate = &ctxt->vstateTab[0];
4301 ctxt->vstateNr = 1;
4302 CONT = cont;
4303 NODE = child;
4304 DEPTH = 0;
4305 OCCURS = 0;
4306 STATE = 0;
4307 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004308 if ((ret == -3) && (warn)) {
4309 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004310 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004311 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004312 /*
4313 * An entities reference appeared at this level.
4314 * Buid a minimal representation of this node content
4315 * sufficient to run the validation process on it
4316 */
4317 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004318 cur = child;
4319 while (cur != NULL) {
4320 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004321 case XML_ENTITY_REF_NODE:
4322 /*
4323 * Push the current node to be able to roll back
4324 * and process within the entity
4325 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004326 if ((cur->children != NULL) &&
4327 (cur->children->children != NULL)) {
4328 nodeVPush(ctxt, cur);
4329 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004330 continue;
4331 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004332 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004333 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004334 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004335 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004336 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004337 case XML_CDATA_SECTION_NODE:
4338 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004339 case XML_ELEMENT_NODE:
4340 /*
4341 * Allocate a new node and minimally fills in
4342 * what's required
4343 */
4344 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4345 if (tmp == NULL) {
4346 xmlGenericError(xmlGenericErrorContext,
4347 "xmlValidateElementContent : malloc failed\n");
4348 xmlFreeNodeList(repl);
4349 ret = -1;
4350 goto done;
4351 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004352 tmp->type = cur->type;
4353 tmp->name = cur->name;
4354 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004355 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004356 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004357 if (repl == NULL)
4358 repl = last = tmp;
4359 else {
4360 last->next = tmp;
4361 last = tmp;
4362 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004363 if (cur->type == XML_CDATA_SECTION_NODE) {
4364 /*
4365 * E59 spaces in CDATA does not match the
4366 * nonterminal S
4367 */
4368 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4369 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004370 break;
4371 default:
4372 break;
4373 }
4374 /*
4375 * Switch to next element
4376 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004377 cur = cur->next;
4378 while (cur == NULL) {
4379 cur = nodeVPop(ctxt);
4380 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004381 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004382 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004383 }
4384 }
4385
4386 /*
4387 * Relaunch the validation
4388 */
4389 ctxt->vstate = &ctxt->vstateTab[0];
4390 ctxt->vstateNr = 1;
4391 CONT = cont;
4392 NODE = repl;
4393 DEPTH = 0;
4394 OCCURS = 0;
4395 STATE = 0;
4396 ret = xmlValidateElementType(ctxt);
4397 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004398 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004399 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4400 char expr[5000];
4401 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004402
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004403 expr[0] = 0;
4404 xmlSnprintfElementContent(expr, 5000, cont, 1);
4405 list[0] = 0;
4406 if (repl != NULL)
4407 xmlSnprintfElements(list, 5000, repl, 1);
4408 else
4409 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004410
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004411 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004412 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004413 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004414 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004415 name, expr, list);
4416 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004417 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004418 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004419 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004420 expr, list);
4421 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004422 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004423 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004424 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004425 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004426 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004427 name);
4428 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004429 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004430 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004431 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004432 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004433 }
4434 ret = 0;
4435 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004436 if (ret == -3)
4437 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004438
4439
4440done:
4441 /*
4442 * Deallocate the copy if done, and free up the validation stack
4443 */
4444 while (repl != NULL) {
4445 tmp = repl->next;
4446 xmlFree(repl);
4447 repl = tmp;
4448 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004449 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004450 if (ctxt->vstateTab != NULL) {
4451 xmlFree(ctxt->vstateTab);
4452 ctxt->vstateTab = NULL;
4453 }
4454 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004455 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004456 if (ctxt->nodeTab != NULL) {
4457 xmlFree(ctxt->nodeTab);
4458 ctxt->nodeTab = NULL;
4459 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004460 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004461
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004462}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004463
Owen Taylor3473f882001-02-23 17:55:21 +00004464/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004465 * xmlValidateCdataElement:
4466 * @ctxt: the validation context
4467 * @doc: a document instance
4468 * @elem: an element instance
4469 *
4470 * Check that an element follows #CDATA
4471 *
4472 * returns 1 if valid or 0 otherwise
4473 */
4474static int
4475xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4476 xmlNodePtr elem) {
4477 int ret = 1;
4478 xmlNodePtr cur, child;
4479
4480 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4481 return(0);
4482
4483 child = elem->children;
4484
4485 cur = child;
4486 while (cur != NULL) {
4487 switch (cur->type) {
4488 case XML_ENTITY_REF_NODE:
4489 /*
4490 * Push the current node to be able to roll back
4491 * and process within the entity
4492 */
4493 if ((cur->children != NULL) &&
4494 (cur->children->children != NULL)) {
4495 nodeVPush(ctxt, cur);
4496 cur = cur->children->children;
4497 continue;
4498 }
4499 break;
4500 case XML_COMMENT_NODE:
4501 case XML_PI_NODE:
4502 case XML_TEXT_NODE:
4503 case XML_CDATA_SECTION_NODE:
4504 break;
4505 default:
4506 ret = 0;
4507 goto done;
4508 }
4509 /*
4510 * Switch to next element
4511 */
4512 cur = cur->next;
4513 while (cur == NULL) {
4514 cur = nodeVPop(ctxt);
4515 if (cur == NULL)
4516 break;
4517 cur = cur->next;
4518 }
4519 }
4520done:
4521 ctxt->nodeMax = 0;
4522 ctxt->nodeNr = 0;
4523 if (ctxt->nodeTab != NULL) {
4524 xmlFree(ctxt->nodeTab);
4525 ctxt->nodeTab = NULL;
4526 }
4527 return(ret);
4528}
4529
4530/**
Owen Taylor3473f882001-02-23 17:55:21 +00004531 * xmlValidateOneElement:
4532 * @ctxt: the validation context
4533 * @doc: a document instance
4534 * @elem: an element instance
4535 *
4536 * Try to validate a single element and it's attributes,
4537 * basically it does the following checks as described by the
4538 * XML-1.0 recommendation:
4539 * - [ VC: Element Valid ]
4540 * - [ VC: Required Attribute ]
4541 * Then call xmlValidateOneAttribute() for each attribute present.
4542 *
4543 * The ID/IDREF checkings are done separately
4544 *
4545 * returns 1 if valid or 0 otherwise
4546 */
4547
4548int
4549xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4550 xmlNodePtr elem) {
4551 xmlElementPtr elemDecl = NULL;
4552 xmlElementContentPtr cont;
4553 xmlAttributePtr attr;
4554 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004555 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004556 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004557 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004558 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004559
4560 CHECK_DTD;
4561
4562 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004563 switch (elem->type) {
4564 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004565 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004566 VERROR(ctxt->userData,
4567 "Attribute element not expected here\n");
4568 return(0);
4569 case XML_TEXT_NODE:
4570 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004571 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004572 VERROR(ctxt->userData, "Text element has childs !\n");
4573 return(0);
4574 }
4575 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004576 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004577 VERROR(ctxt->userData, "Text element has attributes !\n");
4578 return(0);
4579 }
4580 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004581 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004582 VERROR(ctxt->userData, "Text element has namespace !\n");
4583 return(0);
4584 }
4585 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004586 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004587 VERROR(ctxt->userData,
4588 "Text element carries namespace definitions !\n");
4589 return(0);
4590 }
4591 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004592 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004593 VERROR(ctxt->userData,
4594 "Text element has no content !\n");
4595 return(0);
4596 }
4597 return(1);
4598 case XML_XINCLUDE_START:
4599 case XML_XINCLUDE_END:
4600 return(1);
4601 case XML_CDATA_SECTION_NODE:
4602 case XML_ENTITY_REF_NODE:
4603 case XML_PI_NODE:
4604 case XML_COMMENT_NODE:
4605 return(1);
4606 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004607 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004608 VERROR(ctxt->userData,
4609 "Entity element not expected here\n");
4610 return(0);
4611 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004612 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004613 VERROR(ctxt->userData,
4614 "Notation element not expected here\n");
4615 return(0);
4616 case XML_DOCUMENT_NODE:
4617 case XML_DOCUMENT_TYPE_NODE:
4618 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004619 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004620 VERROR(ctxt->userData,
4621 "Document element not expected here\n");
4622 return(0);
4623 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004624 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004625 VERROR(ctxt->userData,
4626 "\n");
4627 return(0);
4628 case XML_ELEMENT_NODE:
4629 break;
4630 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004631 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004632 VERROR(ctxt->userData,
4633 "unknown element type %d\n", elem->type);
4634 return(0);
4635 }
4636 if (elem->name == NULL) return(0);
4637
4638 /*
4639 * Fetch the declaration for the qualified name
4640 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004641 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4642 prefix = elem->ns->prefix;
4643
4644 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004645 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004646 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004647 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004648 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004649 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004650 if (elemDecl != NULL)
4651 extsubset = 1;
4652 }
Owen Taylor3473f882001-02-23 17:55:21 +00004653 }
4654
4655 /*
4656 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004657 * This is "non-strict" validation should be done on the
4658 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004659 */
4660 if (elemDecl == NULL) {
4661 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004662 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004663 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004664 if (elemDecl != NULL)
4665 extsubset = 1;
4666 }
Owen Taylor3473f882001-02-23 17:55:21 +00004667 }
4668 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004669 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004670 VERROR(ctxt->userData, "No declaration for element %s\n",
4671 elem->name);
4672 return(0);
4673 }
4674
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004675 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004676 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004677 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004678 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004679 VERROR(ctxt->userData, "No declaration for element %s\n",
4680 elem->name);
4681 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004682 case XML_ELEMENT_TYPE_EMPTY:
4683 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004684 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004685 VERROR(ctxt->userData,
4686 "Element %s was declared EMPTY this one has content\n",
4687 elem->name);
4688 ret = 0;
4689 }
4690 break;
4691 case XML_ELEMENT_TYPE_ANY:
4692 /* I don't think anything is required then */
4693 break;
4694 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004695
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004696 /* simple case of declared as #PCDATA */
4697 if ((elemDecl->content != NULL) &&
4698 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4699 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4700 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004701 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004702 VERROR(ctxt->userData,
4703 "Element %s was declared #PCDATA but contains non text nodes\n",
4704 elem->name);
4705 }
4706 break;
4707 }
Owen Taylor3473f882001-02-23 17:55:21 +00004708 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004709 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004710 while (child != NULL) {
4711 if (child->type == XML_ELEMENT_NODE) {
4712 name = child->name;
4713 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4714 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004715 snprintf((char *) qname, sizeof(qname), "%s:%s",
4716 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004717 qname[sizeof(qname) - 1] = 0;
4718 cont = elemDecl->content;
4719 while (cont != NULL) {
4720 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4721 if (xmlStrEqual(cont->name, qname)) break;
4722 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4723 (cont->c1 != NULL) &&
4724 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4725 if (xmlStrEqual(cont->c1->name, qname)) break;
4726 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4727 (cont->c1 == NULL) ||
4728 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4729 /* Internal error !!! */
4730 xmlGenericError(xmlGenericErrorContext,
4731 "Internal: MIXED struct bad\n");
4732 break;
4733 }
4734 cont = cont->c2;
4735 }
4736 if (cont != NULL)
4737 goto child_ok;
4738 }
4739 cont = elemDecl->content;
4740 while (cont != NULL) {
4741 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4742 if (xmlStrEqual(cont->name, name)) break;
4743 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4744 (cont->c1 != NULL) &&
4745 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4746 if (xmlStrEqual(cont->c1->name, name)) break;
4747 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4748 (cont->c1 == NULL) ||
4749 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4750 /* Internal error !!! */
4751 xmlGenericError(xmlGenericErrorContext,
4752 "Internal: MIXED struct bad\n");
4753 break;
4754 }
4755 cont = cont->c2;
4756 }
4757 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004758 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004759 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004760 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004761 name, elem->name);
4762 ret = 0;
4763 }
4764 }
4765child_ok:
4766 child = child->next;
4767 }
4768 break;
4769 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004770 if ((doc->standalone == 1) && (extsubset == 1)) {
4771 /*
4772 * VC: Standalone Document Declaration
4773 * - element types with element content, if white space
4774 * occurs directly within any instance of those types.
4775 */
4776 child = elem->children;
4777 while (child != NULL) {
4778 if (child->type == XML_TEXT_NODE) {
4779 const xmlChar *content = child->content;
4780
4781 while (IS_BLANK(*content))
4782 content++;
4783 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004784 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004785 VERROR(ctxt->userData,
4786"standalone: %s declared in the external subset contains white spaces nodes\n",
4787 elem->name);
4788 ret = 0;
4789 break;
4790 }
4791 }
4792 child =child->next;
4793 }
4794 }
Owen Taylor3473f882001-02-23 17:55:21 +00004795 child = elem->children;
4796 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004797 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004798 if (tmp <= 0)
4799 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004800 break;
4801 }
4802
4803 /* [ VC: Required Attribute ] */
4804 attr = elemDecl->attributes;
4805 while (attr != NULL) {
4806 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004807 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004808
Daniel Veillarde4301c82002-02-13 13:32:35 +00004809 if ((attr->prefix == NULL) &&
4810 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4811 xmlNsPtr ns;
4812
4813 ns = elem->nsDef;
4814 while (ns != NULL) {
4815 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004816 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004817 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004818 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004819 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4820 xmlNsPtr ns;
4821
4822 ns = elem->nsDef;
4823 while (ns != NULL) {
4824 if (xmlStrEqual(attr->name, ns->prefix))
4825 goto found;
4826 ns = ns->next;
4827 }
4828 } else {
4829 xmlAttrPtr attrib;
4830
4831 attrib = elem->properties;
4832 while (attrib != NULL) {
4833 if (xmlStrEqual(attrib->name, attr->name)) {
4834 if (attr->prefix != NULL) {
4835 xmlNsPtr nameSpace = attrib->ns;
4836
4837 if (nameSpace == NULL)
4838 nameSpace = elem->ns;
4839 /*
4840 * qualified names handling is problematic, having a
4841 * different prefix should be possible but DTDs don't
4842 * allow to define the URI instead of the prefix :-(
4843 */
4844 if (nameSpace == NULL) {
4845 if (qualified < 0)
4846 qualified = 0;
4847 } else if (!xmlStrEqual(nameSpace->prefix,
4848 attr->prefix)) {
4849 if (qualified < 1)
4850 qualified = 1;
4851 } else
4852 goto found;
4853 } else {
4854 /*
4855 * We should allow applications to define namespaces
4856 * for their application even if the DTD doesn't
4857 * carry one, otherwise, basically we would always
4858 * break.
4859 */
4860 goto found;
4861 }
4862 }
4863 attrib = attrib->next;
4864 }
Owen Taylor3473f882001-02-23 17:55:21 +00004865 }
4866 if (qualified == -1) {
4867 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004868 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004869 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004870 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004871 elem->name, attr->name);
4872 ret = 0;
4873 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004874 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004875 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004876 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004877 elem->name, attr->prefix,attr->name);
4878 ret = 0;
4879 }
4880 } else if (qualified == 0) {
4881 VWARNING(ctxt->userData,
4882 "Element %s required attribute %s:%s has no prefix\n",
4883 elem->name, attr->prefix,attr->name);
4884 } else if (qualified == 1) {
4885 VWARNING(ctxt->userData,
4886 "Element %s required attribute %s:%s has different prefix\n",
4887 elem->name, attr->prefix,attr->name);
4888 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004889 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4890 /*
4891 * Special tests checking #FIXED namespace declarations
4892 * have the right value since this is not done as an
4893 * attribute checking
4894 */
4895 if ((attr->prefix == NULL) &&
4896 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4897 xmlNsPtr ns;
4898
4899 ns = elem->nsDef;
4900 while (ns != NULL) {
4901 if (ns->prefix == NULL) {
4902 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004903 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00004904 VERROR(ctxt->userData,
4905 "Element %s namespace name for default namespace does not match the DTD\n",
4906 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004907 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004908 }
4909 goto found;
4910 }
4911 ns = ns->next;
4912 }
4913 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4914 xmlNsPtr ns;
4915
4916 ns = elem->nsDef;
4917 while (ns != NULL) {
4918 if (xmlStrEqual(attr->name, ns->prefix)) {
4919 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004920 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00004921 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004922 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00004923 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004924 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004925 }
4926 goto found;
4927 }
4928 ns = ns->next;
4929 }
4930 }
Owen Taylor3473f882001-02-23 17:55:21 +00004931 }
4932found:
4933 attr = attr->nexth;
4934 }
4935 return(ret);
4936}
4937
4938/**
4939 * xmlValidateRoot:
4940 * @ctxt: the validation context
4941 * @doc: a document instance
4942 *
4943 * Try to validate a the root element
4944 * basically it does the following check as described by the
4945 * XML-1.0 recommendation:
4946 * - [ VC: Root Element Type ]
4947 * it doesn't try to recurse or apply other check to the element
4948 *
4949 * returns 1 if valid or 0 otherwise
4950 */
4951
4952int
4953xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4954 xmlNodePtr root;
4955 if (doc == NULL) return(0);
4956
4957 root = xmlDocGetRootElement(doc);
4958 if ((root == NULL) || (root->name == NULL)) {
4959 VERROR(ctxt->userData, "Not valid: no root element\n");
4960 return(0);
4961 }
4962
4963 /*
4964 * When doing post validation against a separate DTD, those may
4965 * no internal subset has been generated
4966 */
4967 if ((doc->intSubset != NULL) &&
4968 (doc->intSubset->name != NULL)) {
4969 /*
4970 * Check first the document root against the NQName
4971 */
4972 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4973 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4974 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004975 snprintf((char *) qname, sizeof(qname), "%s:%s",
4976 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004977 qname[sizeof(qname) - 1] = 0;
4978 if (xmlStrEqual(doc->intSubset->name, qname))
4979 goto name_ok;
4980 }
4981 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4982 (xmlStrEqual(root->name, BAD_CAST "html")))
4983 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00004984 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00004985 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004986 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004987 root->name, doc->intSubset->name);
4988 return(0);
4989
4990 }
4991 }
4992name_ok:
4993 return(1);
4994}
4995
4996
4997/**
4998 * xmlValidateElement:
4999 * @ctxt: the validation context
5000 * @doc: a document instance
5001 * @elem: an element instance
5002 *
5003 * Try to validate the subtree under an element
5004 *
5005 * returns 1 if valid or 0 otherwise
5006 */
5007
5008int
5009xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5010 xmlNodePtr child;
5011 xmlAttrPtr attr;
5012 xmlChar *value;
5013 int ret = 1;
5014
5015 if (elem == NULL) return(0);
5016
5017 /*
5018 * XInclude elements were added after parsing in the infoset,
5019 * they don't really mean anything validation wise.
5020 */
5021 if ((elem->type == XML_XINCLUDE_START) ||
5022 (elem->type == XML_XINCLUDE_END))
5023 return(1);
5024
5025 CHECK_DTD;
5026
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005027 /*
5028 * Entities references have to be handled separately
5029 */
5030 if (elem->type == XML_ENTITY_REF_NODE) {
5031 return(1);
5032 }
5033
Owen Taylor3473f882001-02-23 17:55:21 +00005034 ret &= xmlValidateOneElement(ctxt, doc, elem);
5035 attr = elem->properties;
5036 while(attr != NULL) {
5037 value = xmlNodeListGetString(doc, attr->children, 0);
5038 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5039 if (value != NULL)
5040 xmlFree(value);
5041 attr= attr->next;
5042 }
5043 child = elem->children;
5044 while (child != NULL) {
5045 ret &= xmlValidateElement(ctxt, doc, child);
5046 child = child->next;
5047 }
5048
5049 return(ret);
5050}
5051
Daniel Veillard8730c562001-02-26 10:49:57 +00005052/**
5053 * xmlValidateRef:
5054 * @ref: A reference to be validated
5055 * @ctxt: Validation context
5056 * @name: Name of ID we are searching for
5057 *
5058 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005059static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005060xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005061 const xmlChar *name) {
5062 xmlAttrPtr id;
5063 xmlAttrPtr attr;
5064
5065 if (ref == NULL)
5066 return;
5067 attr = ref->attr;
5068 if (attr == NULL)
5069 return;
5070 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5071 id = xmlGetID(ctxt->doc, name);
5072 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005073 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005074 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005075 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005076 attr->name, name);
5077 ctxt->valid = 0;
5078 }
5079 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5080 xmlChar *dup, *str = NULL, *cur, save;
5081
5082 dup = xmlStrdup(name);
5083 if (dup == NULL) {
5084 ctxt->valid = 0;
5085 return;
5086 }
5087 cur = dup;
5088 while (*cur != 0) {
5089 str = cur;
5090 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5091 save = *cur;
5092 *cur = 0;
5093 id = xmlGetID(ctxt->doc, str);
5094 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005095 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005096 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005097 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005098 attr->name, str);
5099 ctxt->valid = 0;
5100 }
5101 if (save == 0)
5102 break;
5103 *cur = save;
5104 while (IS_BLANK(*cur)) cur++;
5105 }
5106 xmlFree(dup);
5107 }
5108}
5109
5110/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005111 * xmlWalkValidateList:
5112 * @data: Contents of current link
5113 * @user: Value supplied by the user
5114 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005115 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005116 */
5117static int
5118xmlWalkValidateList(const void *data, const void *user)
5119{
5120 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5121 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5122 return 1;
5123}
5124
5125/**
5126 * xmlValidateCheckRefCallback:
5127 * @ref_list: List of references
5128 * @ctxt: Validation context
5129 * @name: Name of ID we are searching for
5130 *
5131 */
5132static void
5133xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5134 const xmlChar *name) {
5135 xmlValidateMemo memo;
5136
5137 if (ref_list == NULL)
5138 return;
5139 memo.ctxt = ctxt;
5140 memo.name = name;
5141
5142 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5143
5144}
5145
5146/**
Owen Taylor3473f882001-02-23 17:55:21 +00005147 * xmlValidateDocumentFinal:
5148 * @ctxt: the validation context
5149 * @doc: a document instance
5150 *
5151 * Does the final step for the document validation once all the
5152 * incremental validation steps have been completed
5153 *
5154 * basically it does the following checks described by the XML Rec
5155 *
5156 *
5157 * returns 1 if valid or 0 otherwise
5158 */
5159
5160int
5161xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5162 xmlRefTablePtr table;
5163
5164 if (doc == NULL) {
5165 xmlGenericError(xmlGenericErrorContext,
5166 "xmlValidateDocumentFinal: doc == NULL\n");
5167 return(0);
5168 }
5169
5170 /*
5171 * Check all the NOTATION/NOTATIONS attributes
5172 */
5173 /*
5174 * Check all the ENTITY/ENTITIES attributes definition for validity
5175 */
5176 /*
5177 * Check all the IDREF/IDREFS attributes definition for validity
5178 */
5179 table = (xmlRefTablePtr) doc->refs;
5180 ctxt->doc = doc;
5181 ctxt->valid = 1;
5182 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5183 return(ctxt->valid);
5184}
5185
5186/**
5187 * xmlValidateDtd:
5188 * @ctxt: the validation context
5189 * @doc: a document instance
5190 * @dtd: a dtd instance
5191 *
5192 * Try to validate the document against the dtd instance
5193 *
5194 * basically it does check all the definitions in the DtD.
5195 *
5196 * returns 1 if valid or 0 otherwise
5197 */
5198
5199int
5200xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5201 int ret;
5202 xmlDtdPtr oldExt;
5203 xmlNodePtr root;
5204
5205 if (dtd == NULL) return(0);
5206 if (doc == NULL) return(0);
5207 oldExt = doc->extSubset;
5208 doc->extSubset = dtd;
5209 ret = xmlValidateRoot(ctxt, doc);
5210 if (ret == 0) {
5211 doc->extSubset = oldExt;
5212 return(ret);
5213 }
5214 if (doc->ids != NULL) {
5215 xmlFreeIDTable(doc->ids);
5216 doc->ids = NULL;
5217 }
5218 if (doc->refs != NULL) {
5219 xmlFreeRefTable(doc->refs);
5220 doc->refs = NULL;
5221 }
5222 root = xmlDocGetRootElement(doc);
5223 ret = xmlValidateElement(ctxt, doc, root);
5224 ret &= xmlValidateDocumentFinal(ctxt, doc);
5225 doc->extSubset = oldExt;
5226 return(ret);
5227}
5228
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005229static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005230xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5231 const xmlChar *name ATTRIBUTE_UNUSED) {
5232 if (cur == NULL)
5233 return;
5234 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5235 xmlChar *notation = cur->content;
5236
Daniel Veillard878eab02002-02-19 13:46:09 +00005237 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005238 int ret;
5239
5240 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5241 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005242 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005243 }
5244 }
5245 }
5246}
5247
5248static void
Owen Taylor3473f882001-02-23 17:55:21 +00005249xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005250 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005251 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005252 xmlDocPtr doc;
5253 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005254
Owen Taylor3473f882001-02-23 17:55:21 +00005255 if (cur == NULL)
5256 return;
5257 switch (cur->atype) {
5258 case XML_ATTRIBUTE_CDATA:
5259 case XML_ATTRIBUTE_ID:
5260 case XML_ATTRIBUTE_IDREF :
5261 case XML_ATTRIBUTE_IDREFS:
5262 case XML_ATTRIBUTE_NMTOKEN:
5263 case XML_ATTRIBUTE_NMTOKENS:
5264 case XML_ATTRIBUTE_ENUMERATION:
5265 break;
5266 case XML_ATTRIBUTE_ENTITY:
5267 case XML_ATTRIBUTE_ENTITIES:
5268 case XML_ATTRIBUTE_NOTATION:
5269 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005270
5271 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5272 cur->atype, cur->defaultValue);
5273 if ((ret == 0) && (ctxt->valid == 1))
5274 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005275 }
5276 if (cur->tree != NULL) {
5277 xmlEnumerationPtr tree = cur->tree;
5278 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005279 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005280 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005281 if ((ret == 0) && (ctxt->valid == 1))
5282 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005283 tree = tree->next;
5284 }
5285 }
5286 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005287 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5288 doc = cur->doc;
5289 if ((doc == NULL) || (cur->elem == NULL)) {
5290 VERROR(ctxt->userData,
5291 "xmlValidateAttributeCallback(%s): internal error\n",
5292 cur->name);
5293 return;
5294 }
5295 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5296 if (elem == NULL)
5297 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5298 if (elem == NULL) {
5299 VERROR(ctxt->userData,
5300 "attribute %s: could not find decl for element %s\n",
5301 cur->name, cur->elem);
5302 return;
5303 }
5304 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5305 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005306 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005307 cur->name, cur->elem);
5308 ctxt->valid = 0;
5309 }
5310 }
Owen Taylor3473f882001-02-23 17:55:21 +00005311}
5312
5313/**
5314 * xmlValidateDtdFinal:
5315 * @ctxt: the validation context
5316 * @doc: a document instance
5317 *
5318 * Does the final step for the dtds validation once all the
5319 * subsets have been parsed
5320 *
5321 * basically it does the following checks described by the XML Rec
5322 * - check that ENTITY and ENTITIES type attributes default or
5323 * possible values matches one of the defined entities.
5324 * - check that NOTATION type attributes default or
5325 * possible values matches one of the defined notations.
5326 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005327 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005328 */
5329
5330int
5331xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005332 xmlDtdPtr dtd;
5333 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005334 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005335
5336 if (doc == NULL) return(0);
5337 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5338 return(0);
5339 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005340 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005341 dtd = doc->intSubset;
5342 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5343 table = (xmlAttributeTablePtr) dtd->attributes;
5344 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005345 }
5346 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005347 entities = (xmlEntitiesTablePtr) dtd->entities;
5348 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5349 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005350 }
5351 dtd = doc->extSubset;
5352 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5353 table = (xmlAttributeTablePtr) dtd->attributes;
5354 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005355 }
5356 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005357 entities = (xmlEntitiesTablePtr) dtd->entities;
5358 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5359 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005360 }
5361 return(ctxt->valid);
5362}
5363
5364/**
5365 * xmlValidateDocument:
5366 * @ctxt: the validation context
5367 * @doc: a document instance
5368 *
5369 * Try to validate the document instance
5370 *
5371 * basically it does the all the checks described by the XML Rec
5372 * i.e. validates the internal and external subset (if present)
5373 * and validate the document tree.
5374 *
5375 * returns 1 if valid or 0 otherwise
5376 */
5377
5378int
5379xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5380 int ret;
5381 xmlNodePtr root;
5382
5383 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5384 return(0);
5385 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5386 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5387 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5388 doc->intSubset->SystemID);
5389 if (doc->extSubset == NULL) {
5390 if (doc->intSubset->SystemID != NULL) {
5391 VERROR(ctxt->userData,
5392 "Could not load the external subset \"%s\"\n",
5393 doc->intSubset->SystemID);
5394 } else {
5395 VERROR(ctxt->userData,
5396 "Could not load the external subset \"%s\"\n",
5397 doc->intSubset->ExternalID);
5398 }
5399 return(0);
5400 }
5401 }
5402
5403 if (doc->ids != NULL) {
5404 xmlFreeIDTable(doc->ids);
5405 doc->ids = NULL;
5406 }
5407 if (doc->refs != NULL) {
5408 xmlFreeRefTable(doc->refs);
5409 doc->refs = NULL;
5410 }
5411 ret = xmlValidateDtdFinal(ctxt, doc);
5412 if (!xmlValidateRoot(ctxt, doc)) return(0);
5413
5414 root = xmlDocGetRootElement(doc);
5415 ret &= xmlValidateElement(ctxt, doc, root);
5416 ret &= xmlValidateDocumentFinal(ctxt, doc);
5417 return(ret);
5418}
5419
5420
5421/************************************************************************
5422 * *
5423 * Routines for dynamic validation editing *
5424 * *
5425 ************************************************************************/
5426
5427/**
5428 * xmlValidGetPotentialChildren:
5429 * @ctree: an element content tree
5430 * @list: an array to store the list of child names
5431 * @len: a pointer to the number of element in the list
5432 * @max: the size of the array
5433 *
5434 * Build/extend a list of potential children allowed by the content tree
5435 *
5436 * returns the number of element in the list, or -1 in case of error.
5437 */
5438
5439int
5440xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5441 int *len, int max) {
5442 int i;
5443
5444 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5445 return(-1);
5446 if (*len >= max) return(*len);
5447
5448 switch (ctree->type) {
5449 case XML_ELEMENT_CONTENT_PCDATA:
5450 for (i = 0; i < *len;i++)
5451 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5452 list[(*len)++] = BAD_CAST "#PCDATA";
5453 break;
5454 case XML_ELEMENT_CONTENT_ELEMENT:
5455 for (i = 0; i < *len;i++)
5456 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5457 list[(*len)++] = ctree->name;
5458 break;
5459 case XML_ELEMENT_CONTENT_SEQ:
5460 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5461 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5462 break;
5463 case XML_ELEMENT_CONTENT_OR:
5464 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5465 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5466 break;
5467 }
5468
5469 return(*len);
5470}
5471
5472/**
5473 * xmlValidGetValidElements:
5474 * @prev: an element to insert after
5475 * @next: an element to insert next
5476 * @list: an array to store the list of child names
5477 * @max: the size of the array
5478 *
5479 * This function returns the list of authorized children to insert
5480 * within an existing tree while respecting the validity constraints
5481 * forced by the Dtd. The insertion point is defined using @prev and
5482 * @next in the following ways:
5483 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5484 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5485 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5486 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5487 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5488 *
5489 * pointers to the element names are inserted at the beginning of the array
5490 * and do not need to be freed.
5491 *
5492 * returns the number of element in the list, or -1 in case of error. If
5493 * the function returns the value @max the caller is invited to grow the
5494 * receiving array and retry.
5495 */
5496
5497int
5498xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5499 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005500 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005501 int nb_valid_elements = 0;
5502 const xmlChar *elements[256];
5503 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005504 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005505
5506 xmlNode *ref_node;
5507 xmlNode *parent;
5508 xmlNode *test_node;
5509
5510 xmlNode *prev_next;
5511 xmlNode *next_prev;
5512 xmlNode *parent_childs;
5513 xmlNode *parent_last;
5514
5515 xmlElement *element_desc;
5516
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005517 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005518
Owen Taylor3473f882001-02-23 17:55:21 +00005519 if (prev == NULL && next == NULL)
5520 return(-1);
5521
5522 if (list == NULL) return(-1);
5523 if (max <= 0) return(-1);
5524
5525 nb_valid_elements = 0;
5526 ref_node = prev ? prev : next;
5527 parent = ref_node->parent;
5528
5529 /*
5530 * Retrieves the parent element declaration
5531 */
5532 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5533 parent->name);
5534 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5535 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5536 parent->name);
5537 if (element_desc == NULL) return(-1);
5538
5539 /*
5540 * Do a backup of the current tree structure
5541 */
5542 prev_next = prev ? prev->next : NULL;
5543 next_prev = next ? next->prev : NULL;
5544 parent_childs = parent->children;
5545 parent_last = parent->last;
5546
5547 /*
5548 * Creates a dummy node and insert it into the tree
5549 */
5550 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5551 test_node->doc = ref_node->doc;
5552 test_node->parent = parent;
5553 test_node->prev = prev;
5554 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005555 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005556
5557 if (prev) prev->next = test_node;
5558 else parent->children = test_node;
5559
5560 if (next) next->prev = test_node;
5561 else parent->last = test_node;
5562
5563 /*
5564 * Insert each potential child node and check if the parent is
5565 * still valid
5566 */
5567 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5568 elements, &nb_elements, 256);
5569
5570 for (i = 0;i < nb_elements;i++) {
5571 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005572 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005573 int j;
5574
5575 for (j = 0; j < nb_valid_elements;j++)
5576 if (xmlStrEqual(elements[i], list[j])) break;
5577 list[nb_valid_elements++] = elements[i];
5578 if (nb_valid_elements >= max) break;
5579 }
5580 }
5581
5582 /*
5583 * Restore the tree structure
5584 */
5585 if (prev) prev->next = prev_next;
5586 if (next) next->prev = next_prev;
5587 parent->children = parent_childs;
5588 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005589
5590 /*
5591 * Free up the dummy node
5592 */
5593 test_node->name = name;
5594 xmlFreeNode(test_node);
5595
Owen Taylor3473f882001-02-23 17:55:21 +00005596 return(nb_valid_elements);
5597}