blob: 4578e1446f294e84202529da066d812a319b9de9 [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 *
369 * Returns 0 if successful or -1 in case of error.
370 */
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);
378 return(-1);
379 }
380 switch (content->type) {
381 case XML_ELEMENT_CONTENT_PCDATA:
382 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
383 name);
384 return(-1);
385 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);
482 return(-1);
483 }
484 return(0);
485}
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 *
494 * Returns 0 in case of success, -1 in case of error
495 */
496int
497xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
498 xmlAutomataStatePtr start;
499
500 if ((ctxt == NULL) || (elem == NULL))
501 return(-1);
502 if (elem->type != XML_ELEMENT_DECL)
503 return(-1);
504 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
505 return(0);
506 /* TODO: should we rebuild in this case ? */
507 if (elem->contModel != NULL)
508 return(0);
509
510 ctxt->am = xmlNewAutomata();
511 if (ctxt->am == NULL) {
512 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
513 elem->name);
514 return(-1);
515 }
516 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
517 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
518 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
519 if (!xmlAutomataIsDeterminist(ctxt->am)) {
520 VERROR(ctxt->userData, "Content model of %s is not determinist:\n",
521 elem->name);
522 ctxt->valid = 0;
523 }
524 ctxt->state = NULL;
525 xmlFreeAutomata(ctxt->am);
526 ctxt->am = NULL;
527 return(0);
528}
529
530#endif /* LIBXML_REGEXP_ENABLED */
531
Owen Taylor3473f882001-02-23 17:55:21 +0000532/************************************************************************
533 * *
534 * QName handling helper *
535 * *
536 ************************************************************************/
537
538/**
539 * xmlSplitQName2:
540 * @name: an XML parser context
541 * @prefix: a xmlChar **
542 *
543 * parse an XML qualified name string
544 *
545 * [NS 5] QName ::= (Prefix ':')? LocalPart
546 *
547 * [NS 6] Prefix ::= NCName
548 *
549 * [NS 7] LocalPart ::= NCName
550 *
551 * Returns NULL if not a QName, otherwise the local part, and prefix
552 * is updated to get the Prefix if any.
553 */
554
555xmlChar *
556xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
557 int len = 0;
558 xmlChar *ret = NULL;
559
560 *prefix = NULL;
561
Daniel Veillardf4309d72001-10-02 09:28:58 +0000562#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000563 /* xml: prefix is not really a namespace */
564 if ((name[0] == 'x') && (name[1] == 'm') &&
565 (name[2] == 'l') && (name[3] == ':'))
566 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000567#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000568
569 /* nasty but valid */
570 if (name[0] == ':')
571 return(NULL);
572
573 /*
574 * we are not trying to validate but just to cut, and yes it will
575 * work even if this is as set of UTF-8 encoded chars
576 */
577 while ((name[len] != 0) && (name[len] != ':'))
578 len++;
579
580 if (name[len] == 0)
581 return(NULL);
582
583 *prefix = xmlStrndup(name, len);
584 ret = xmlStrdup(&name[len + 1]);
585
586 return(ret);
587}
588
589/****************************************************************
590 * *
591 * Util functions for data allocation/deallocation *
592 * *
593 ****************************************************************/
594
595/**
596 * xmlNewElementContent:
597 * @name: the subelement name or NULL
598 * @type: the type of element content decl
599 *
600 * Allocate an element content structure.
601 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000602 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000603 */
604xmlElementContentPtr
605xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
606 xmlElementContentPtr ret;
607
608 switch(type) {
609 case XML_ELEMENT_CONTENT_ELEMENT:
610 if (name == NULL) {
611 xmlGenericError(xmlGenericErrorContext,
612 "xmlNewElementContent : name == NULL !\n");
613 }
614 break;
615 case XML_ELEMENT_CONTENT_PCDATA:
616 case XML_ELEMENT_CONTENT_SEQ:
617 case XML_ELEMENT_CONTENT_OR:
618 if (name != NULL) {
619 xmlGenericError(xmlGenericErrorContext,
620 "xmlNewElementContent : name != NULL !\n");
621 }
622 break;
623 default:
624 xmlGenericError(xmlGenericErrorContext,
625 "xmlNewElementContent: unknown type %d\n", type);
626 return(NULL);
627 }
628 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
629 if (ret == NULL) {
630 xmlGenericError(xmlGenericErrorContext,
631 "xmlNewElementContent : out of memory!\n");
632 return(NULL);
633 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000634 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000635 ret->type = type;
636 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000637 if (name != NULL) {
638 xmlChar *prefix = NULL;
639 ret->name = xmlSplitQName2(name, &prefix);
640 if (ret->name == NULL)
641 ret->name = xmlStrdup(name);
642 ret->prefix = prefix;
643 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000644 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000645 ret->prefix = NULL;
646 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000647 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000648 return(ret);
649}
650
651/**
652 * xmlCopyElementContent:
653 * @content: An element content pointer.
654 *
655 * Build a copy of an element content description.
656 *
657 * Returns the new xmlElementContentPtr or NULL in case of error.
658 */
659xmlElementContentPtr
660xmlCopyElementContent(xmlElementContentPtr cur) {
661 xmlElementContentPtr ret;
662
663 if (cur == NULL) return(NULL);
664 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
665 if (ret == NULL) {
666 xmlGenericError(xmlGenericErrorContext,
667 "xmlCopyElementContent : out of memory\n");
668 return(NULL);
669 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000670 if (cur->prefix != NULL)
671 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000672 ret->ocur = cur->ocur;
673 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000674 if (ret->c1 != NULL)
675 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000676 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000677 if (ret->c2 != NULL)
678 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000679 return(ret);
680}
681
682/**
683 * xmlFreeElementContent:
684 * @cur: the element content tree to free
685 *
686 * Free an element content structure. This is a recursive call !
687 */
688void
689xmlFreeElementContent(xmlElementContentPtr cur) {
690 if (cur == NULL) return;
691 switch (cur->type) {
692 case XML_ELEMENT_CONTENT_PCDATA:
693 case XML_ELEMENT_CONTENT_ELEMENT:
694 case XML_ELEMENT_CONTENT_SEQ:
695 case XML_ELEMENT_CONTENT_OR:
696 break;
697 default:
698 xmlGenericError(xmlGenericErrorContext,
699 "xmlFreeElementContent : type %d\n", cur->type);
700 return;
701 }
702 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
703 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
704 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000705 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000706 xmlFree(cur);
707}
708
709/**
710 * xmlDumpElementContent:
711 * @buf: An XML buffer
712 * @content: An element table
713 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
714 *
715 * This will dump the content of the element table as an XML DTD definition
716 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000717static void
Owen Taylor3473f882001-02-23 17:55:21 +0000718xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
719 if (content == NULL) return;
720
721 if (glob) xmlBufferWriteChar(buf, "(");
722 switch (content->type) {
723 case XML_ELEMENT_CONTENT_PCDATA:
724 xmlBufferWriteChar(buf, "#PCDATA");
725 break;
726 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000727 if (content->prefix != NULL) {
728 xmlBufferWriteCHAR(buf, content->prefix);
729 xmlBufferWriteChar(buf, ":");
730 }
Owen Taylor3473f882001-02-23 17:55:21 +0000731 xmlBufferWriteCHAR(buf, content->name);
732 break;
733 case XML_ELEMENT_CONTENT_SEQ:
734 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
735 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
736 xmlDumpElementContent(buf, content->c1, 1);
737 else
738 xmlDumpElementContent(buf, content->c1, 0);
739 xmlBufferWriteChar(buf, " , ");
740 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
741 xmlDumpElementContent(buf, content->c2, 1);
742 else
743 xmlDumpElementContent(buf, content->c2, 0);
744 break;
745 case XML_ELEMENT_CONTENT_OR:
746 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
747 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
748 xmlDumpElementContent(buf, content->c1, 1);
749 else
750 xmlDumpElementContent(buf, content->c1, 0);
751 xmlBufferWriteChar(buf, " | ");
752 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
753 xmlDumpElementContent(buf, content->c2, 1);
754 else
755 xmlDumpElementContent(buf, content->c2, 0);
756 break;
757 default:
758 xmlGenericError(xmlGenericErrorContext,
759 "xmlDumpElementContent: unknown type %d\n",
760 content->type);
761 }
762 if (glob)
763 xmlBufferWriteChar(buf, ")");
764 switch (content->ocur) {
765 case XML_ELEMENT_CONTENT_ONCE:
766 break;
767 case XML_ELEMENT_CONTENT_OPT:
768 xmlBufferWriteChar(buf, "?");
769 break;
770 case XML_ELEMENT_CONTENT_MULT:
771 xmlBufferWriteChar(buf, "*");
772 break;
773 case XML_ELEMENT_CONTENT_PLUS:
774 xmlBufferWriteChar(buf, "+");
775 break;
776 }
777}
778
779/**
780 * xmlSprintfElementContent:
781 * @buf: an output buffer
782 * @content: An element table
783 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
784 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000785 * Deprecated, unsafe, use xmlSnprintfElementContent
786 */
787void
788xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
789 xmlElementContentPtr content ATTRIBUTE_UNUSED,
790 int glob ATTRIBUTE_UNUSED) {
791}
792
793/**
794 * xmlSnprintfElementContent:
795 * @buf: an output buffer
796 * @size: the buffer size
797 * @content: An element table
798 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
799 *
Owen Taylor3473f882001-02-23 17:55:21 +0000800 * This will dump the content of the element content definition
801 * Intended just for the debug routine
802 */
803void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000804xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
805 int len;
806
Owen Taylor3473f882001-02-23 17:55:21 +0000807 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000808 len = strlen(buf);
809 if (size - len < 50) {
810 if ((size - len > 4) && (buf[len - 1] != '.'))
811 strcat(buf, " ...");
812 return;
813 }
Owen Taylor3473f882001-02-23 17:55:21 +0000814 if (glob) strcat(buf, "(");
815 switch (content->type) {
816 case XML_ELEMENT_CONTENT_PCDATA:
817 strcat(buf, "#PCDATA");
818 break;
819 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000820 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000821 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000822 strcat(buf, " ...");
823 return;
824 }
825 strcat(buf, (char *) content->prefix);
826 strcat(buf, ":");
827 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000828 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000829 strcat(buf, " ...");
830 return;
831 }
Owen Taylor3473f882001-02-23 17:55:21 +0000832 strcat(buf, (char *) content->name);
833 break;
834 case XML_ELEMENT_CONTENT_SEQ:
835 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
836 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000837 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000838 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000839 xmlSnprintfElementContent(buf, size, content->c1, 0);
840 len = strlen(buf);
841 if (size - len < 50) {
842 if ((size - len > 4) && (buf[len - 1] != '.'))
843 strcat(buf, " ...");
844 return;
845 }
Owen Taylor3473f882001-02-23 17:55:21 +0000846 strcat(buf, " , ");
847 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000848 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000849 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000850 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000851 break;
852 case XML_ELEMENT_CONTENT_OR:
853 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
854 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000855 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000856 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000857 xmlSnprintfElementContent(buf, size, content->c1, 0);
858 len = strlen(buf);
859 if (size - len < 50) {
860 if ((size - len > 4) && (buf[len - 1] != '.'))
861 strcat(buf, " ...");
862 return;
863 }
Owen Taylor3473f882001-02-23 17:55:21 +0000864 strcat(buf, " | ");
865 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000866 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000867 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000868 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000869 break;
870 }
871 if (glob)
872 strcat(buf, ")");
873 switch (content->ocur) {
874 case XML_ELEMENT_CONTENT_ONCE:
875 break;
876 case XML_ELEMENT_CONTENT_OPT:
877 strcat(buf, "?");
878 break;
879 case XML_ELEMENT_CONTENT_MULT:
880 strcat(buf, "*");
881 break;
882 case XML_ELEMENT_CONTENT_PLUS:
883 strcat(buf, "+");
884 break;
885 }
886}
887
888/****************************************************************
889 * *
890 * Registration of DTD declarations *
891 * *
892 ****************************************************************/
893
894/**
895 * xmlCreateElementTable:
896 *
897 * create and initialize an empty element hash table.
898 *
899 * Returns the xmlElementTablePtr just created or NULL in case of error.
900 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000901static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000902xmlCreateElementTable(void) {
903 return(xmlHashCreate(0));
904}
905
906/**
907 * xmlFreeElement:
908 * @elem: An element
909 *
910 * Deallocate the memory used by an element definition
911 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000912static void
Owen Taylor3473f882001-02-23 17:55:21 +0000913xmlFreeElement(xmlElementPtr elem) {
914 if (elem == NULL) return;
915 xmlUnlinkNode((xmlNodePtr) elem);
916 xmlFreeElementContent(elem->content);
917 if (elem->name != NULL)
918 xmlFree((xmlChar *) elem->name);
919 if (elem->prefix != NULL)
920 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000921 xmlFree(elem);
922}
923
924
925/**
926 * xmlAddElementDecl:
927 * @ctxt: the validation context
928 * @dtd: pointer to the DTD
929 * @name: the entity name
930 * @type: the element type
931 * @content: the element content tree or NULL
932 *
933 * Register a new element declaration
934 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000935 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000936 */
937xmlElementPtr
938xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
939 xmlElementTypeVal type,
940 xmlElementContentPtr content) {
941 xmlElementPtr ret;
942 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000943 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000944 xmlChar *ns, *uqname;
945
946 if (dtd == NULL) {
947 xmlGenericError(xmlGenericErrorContext,
948 "xmlAddElementDecl: dtd == NULL\n");
949 return(NULL);
950 }
951 if (name == NULL) {
952 xmlGenericError(xmlGenericErrorContext,
953 "xmlAddElementDecl: name == NULL\n");
954 return(NULL);
955 }
956 switch (type) {
957 case XML_ELEMENT_TYPE_EMPTY:
958 if (content != NULL) {
959 xmlGenericError(xmlGenericErrorContext,
960 "xmlAddElementDecl: content != NULL for EMPTY\n");
961 return(NULL);
962 }
963 break;
964 case XML_ELEMENT_TYPE_ANY:
965 if (content != NULL) {
966 xmlGenericError(xmlGenericErrorContext,
967 "xmlAddElementDecl: content != NULL for ANY\n");
968 return(NULL);
969 }
970 break;
971 case XML_ELEMENT_TYPE_MIXED:
972 if (content == NULL) {
973 xmlGenericError(xmlGenericErrorContext,
974 "xmlAddElementDecl: content == NULL for MIXED\n");
975 return(NULL);
976 }
977 break;
978 case XML_ELEMENT_TYPE_ELEMENT:
979 if (content == NULL) {
980 xmlGenericError(xmlGenericErrorContext,
981 "xmlAddElementDecl: content == NULL for ELEMENT\n");
982 return(NULL);
983 }
984 break;
985 default:
986 xmlGenericError(xmlGenericErrorContext,
987 "xmlAddElementDecl: unknown type %d\n", type);
988 return(NULL);
989 }
990
991 /*
992 * check if name is a QName
993 */
994 uqname = xmlSplitQName2(name, &ns);
995 if (uqname != NULL)
996 name = uqname;
997
998 /*
999 * Create the Element table if needed.
1000 */
1001 table = (xmlElementTablePtr) dtd->elements;
1002 if (table == NULL) {
1003 table = xmlCreateElementTable();
1004 dtd->elements = (void *) table;
1005 }
1006 if (table == NULL) {
1007 xmlGenericError(xmlGenericErrorContext,
1008 "xmlAddElementDecl: Table creation failed!\n");
1009 return(NULL);
1010 }
1011
Daniel Veillarda10efa82001-04-18 13:09:01 +00001012 /*
1013 * lookup old attributes inserted on an undefined element in the
1014 * internal subset.
1015 */
1016 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1017 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1018 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1019 oldAttributes = ret->attributes;
1020 ret->attributes = NULL;
1021 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1022 xmlFreeElement(ret);
1023 }
Owen Taylor3473f882001-02-23 17:55:21 +00001024 }
Owen Taylor3473f882001-02-23 17:55:21 +00001025
1026 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001027 * The element may already be present if one of its attribute
1028 * was registered first
1029 */
1030 ret = xmlHashLookup2(table, name, ns);
1031 if (ret != NULL) {
1032 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1033 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001034 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001035 */
1036 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1037 if (uqname != NULL)
1038 xmlFree(uqname);
1039 return(NULL);
1040 }
1041 } else {
1042 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1043 if (ret == NULL) {
1044 xmlGenericError(xmlGenericErrorContext,
1045 "xmlAddElementDecl: out of memory\n");
1046 return(NULL);
1047 }
1048 memset(ret, 0, sizeof(xmlElement));
1049 ret->type = XML_ELEMENT_DECL;
1050
1051 /*
1052 * fill the structure.
1053 */
1054 ret->name = xmlStrdup(name);
1055 ret->prefix = ns;
1056
1057 /*
1058 * Validity Check:
1059 * Insertion must not fail
1060 */
1061 if (xmlHashAddEntry2(table, name, ns, ret)) {
1062 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001063 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001064 */
1065 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1066 xmlFreeElement(ret);
1067 if (uqname != NULL)
1068 xmlFree(uqname);
1069 return(NULL);
1070 }
1071 }
1072
1073 /*
1074 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001075 */
1076 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001077 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001078 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001079
1080 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001081 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001082 */
1083 ret->parent = dtd;
1084 ret->doc = dtd->doc;
1085 if (dtd->last == NULL) {
1086 dtd->children = dtd->last = (xmlNodePtr) ret;
1087 } else {
1088 dtd->last->next = (xmlNodePtr) ret;
1089 ret->prev = dtd->last;
1090 dtd->last = (xmlNodePtr) ret;
1091 }
1092 if (uqname != NULL)
1093 xmlFree(uqname);
1094 return(ret);
1095}
1096
1097/**
1098 * xmlFreeElementTable:
1099 * @table: An element table
1100 *
1101 * Deallocate the memory used by an element hash table.
1102 */
1103void
1104xmlFreeElementTable(xmlElementTablePtr table) {
1105 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1106}
1107
1108/**
1109 * xmlCopyElement:
1110 * @elem: An element
1111 *
1112 * Build a copy of an element.
1113 *
1114 * Returns the new xmlElementPtr or NULL in case of error.
1115 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001116static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001117xmlCopyElement(xmlElementPtr elem) {
1118 xmlElementPtr cur;
1119
1120 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1121 if (cur == NULL) {
1122 xmlGenericError(xmlGenericErrorContext,
1123 "xmlCopyElement: out of memory !\n");
1124 return(NULL);
1125 }
1126 memset(cur, 0, sizeof(xmlElement));
1127 cur->type = XML_ELEMENT_DECL;
1128 cur->etype = elem->etype;
1129 if (elem->name != NULL)
1130 cur->name = xmlStrdup(elem->name);
1131 else
1132 cur->name = NULL;
1133 if (elem->prefix != NULL)
1134 cur->prefix = xmlStrdup(elem->prefix);
1135 else
1136 cur->prefix = NULL;
1137 cur->content = xmlCopyElementContent(elem->content);
1138 /* TODO : rebuild the attribute list on the copy */
1139 cur->attributes = NULL;
1140 return(cur);
1141}
1142
1143/**
1144 * xmlCopyElementTable:
1145 * @table: An element table
1146 *
1147 * Build a copy of an element table.
1148 *
1149 * Returns the new xmlElementTablePtr or NULL in case of error.
1150 */
1151xmlElementTablePtr
1152xmlCopyElementTable(xmlElementTablePtr table) {
1153 return((xmlElementTablePtr) xmlHashCopy(table,
1154 (xmlHashCopier) xmlCopyElement));
1155}
1156
1157/**
1158 * xmlDumpElementDecl:
1159 * @buf: the XML buffer output
1160 * @elem: An element table
1161 *
1162 * This will dump the content of the element declaration as an XML
1163 * DTD definition
1164 */
1165void
1166xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1167 switch (elem->etype) {
1168 case XML_ELEMENT_TYPE_EMPTY:
1169 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001170 if (elem->prefix != NULL) {
1171 xmlBufferWriteCHAR(buf, elem->prefix);
1172 xmlBufferWriteChar(buf, ":");
1173 }
Owen Taylor3473f882001-02-23 17:55:21 +00001174 xmlBufferWriteCHAR(buf, elem->name);
1175 xmlBufferWriteChar(buf, " EMPTY>\n");
1176 break;
1177 case XML_ELEMENT_TYPE_ANY:
1178 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001179 if (elem->prefix != NULL) {
1180 xmlBufferWriteCHAR(buf, elem->prefix);
1181 xmlBufferWriteChar(buf, ":");
1182 }
Owen Taylor3473f882001-02-23 17:55:21 +00001183 xmlBufferWriteCHAR(buf, elem->name);
1184 xmlBufferWriteChar(buf, " ANY>\n");
1185 break;
1186 case XML_ELEMENT_TYPE_MIXED:
1187 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001188 if (elem->prefix != NULL) {
1189 xmlBufferWriteCHAR(buf, elem->prefix);
1190 xmlBufferWriteChar(buf, ":");
1191 }
Owen Taylor3473f882001-02-23 17:55:21 +00001192 xmlBufferWriteCHAR(buf, elem->name);
1193 xmlBufferWriteChar(buf, " ");
1194 xmlDumpElementContent(buf, elem->content, 1);
1195 xmlBufferWriteChar(buf, ">\n");
1196 break;
1197 case XML_ELEMENT_TYPE_ELEMENT:
1198 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001199 if (elem->prefix != NULL) {
1200 xmlBufferWriteCHAR(buf, elem->prefix);
1201 xmlBufferWriteChar(buf, ":");
1202 }
Owen Taylor3473f882001-02-23 17:55:21 +00001203 xmlBufferWriteCHAR(buf, elem->name);
1204 xmlBufferWriteChar(buf, " ");
1205 xmlDumpElementContent(buf, elem->content, 1);
1206 xmlBufferWriteChar(buf, ">\n");
1207 break;
1208 default:
1209 xmlGenericError(xmlGenericErrorContext,
1210 "xmlDumpElementDecl: internal: unknown type %d\n",
1211 elem->etype);
1212 }
1213}
1214
1215/**
1216 * xmlDumpElementTable:
1217 * @buf: the XML buffer output
1218 * @table: An element table
1219 *
1220 * This will dump the content of the element table as an XML DTD definition
1221 */
1222void
1223xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1224 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1225}
1226
1227/**
1228 * xmlCreateEnumeration:
1229 * @name: the enumeration name or NULL
1230 *
1231 * create and initialize an enumeration attribute node.
1232 *
1233 * Returns the xmlEnumerationPtr just created or NULL in case
1234 * of error.
1235 */
1236xmlEnumerationPtr
1237xmlCreateEnumeration(xmlChar *name) {
1238 xmlEnumerationPtr ret;
1239
1240 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1241 if (ret == NULL) {
1242 xmlGenericError(xmlGenericErrorContext,
1243 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1244 (long)sizeof(xmlEnumeration));
1245 return(NULL);
1246 }
1247 memset(ret, 0, sizeof(xmlEnumeration));
1248
1249 if (name != NULL)
1250 ret->name = xmlStrdup(name);
1251 return(ret);
1252}
1253
1254/**
1255 * xmlFreeEnumeration:
1256 * @cur: the tree to free.
1257 *
1258 * free an enumeration attribute node (recursive).
1259 */
1260void
1261xmlFreeEnumeration(xmlEnumerationPtr cur) {
1262 if (cur == NULL) return;
1263
1264 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1265
1266 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001267 xmlFree(cur);
1268}
1269
1270/**
1271 * xmlCopyEnumeration:
1272 * @cur: the tree to copy.
1273 *
1274 * Copy an enumeration attribute node (recursive).
1275 *
1276 * Returns the xmlEnumerationPtr just created or NULL in case
1277 * of error.
1278 */
1279xmlEnumerationPtr
1280xmlCopyEnumeration(xmlEnumerationPtr cur) {
1281 xmlEnumerationPtr ret;
1282
1283 if (cur == NULL) return(NULL);
1284 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1285
1286 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1287 else ret->next = NULL;
1288
1289 return(ret);
1290}
1291
1292/**
1293 * xmlDumpEnumeration:
1294 * @buf: the XML buffer output
1295 * @enum: An enumeration
1296 *
1297 * This will dump the content of the enumeration
1298 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001299static void
Owen Taylor3473f882001-02-23 17:55:21 +00001300xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1301 if (cur == NULL) return;
1302
1303 xmlBufferWriteCHAR(buf, cur->name);
1304 if (cur->next == NULL)
1305 xmlBufferWriteChar(buf, ")");
1306 else {
1307 xmlBufferWriteChar(buf, " | ");
1308 xmlDumpEnumeration(buf, cur->next);
1309 }
1310}
1311
1312/**
1313 * xmlCreateAttributeTable:
1314 *
1315 * create and initialize an empty attribute hash table.
1316 *
1317 * Returns the xmlAttributeTablePtr just created or NULL in case
1318 * of error.
1319 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001320static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001321xmlCreateAttributeTable(void) {
1322 return(xmlHashCreate(0));
1323}
1324
1325/**
1326 * xmlScanAttributeDeclCallback:
1327 * @attr: the attribute decl
1328 * @list: the list to update
1329 *
1330 * Callback called by xmlScanAttributeDecl when a new attribute
1331 * has to be entered in the list.
1332 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001333static void
Owen Taylor3473f882001-02-23 17:55:21 +00001334xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001335 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001336 attr->nexth = *list;
1337 *list = attr;
1338}
1339
1340/**
1341 * xmlScanAttributeDecl:
1342 * @dtd: pointer to the DTD
1343 * @elem: the element name
1344 *
1345 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001346 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001347 *
1348 * Returns the pointer to the first attribute decl in the chain,
1349 * possibly NULL.
1350 */
1351xmlAttributePtr
1352xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1353 xmlAttributePtr ret = NULL;
1354 xmlAttributeTablePtr table;
1355
1356 if (dtd == NULL) {
1357 xmlGenericError(xmlGenericErrorContext,
1358 "xmlScanAttributeDecl: dtd == NULL\n");
1359 return(NULL);
1360 }
1361 if (elem == NULL) {
1362 xmlGenericError(xmlGenericErrorContext,
1363 "xmlScanAttributeDecl: elem == NULL\n");
1364 return(NULL);
1365 }
1366 table = (xmlAttributeTablePtr) dtd->attributes;
1367 if (table == NULL)
1368 return(NULL);
1369
1370 /* WRONG !!! */
1371 xmlHashScan3(table, NULL, NULL, elem,
1372 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1373 return(ret);
1374}
1375
1376/**
1377 * xmlScanIDAttributeDecl:
1378 * @ctxt: the validation context
1379 * @elem: the element name
1380 *
1381 * Verify that the element don't have too many ID attributes
1382 * declared.
1383 *
1384 * Returns the number of ID attributes found.
1385 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001386static int
Owen Taylor3473f882001-02-23 17:55:21 +00001387xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1388 xmlAttributePtr cur;
1389 int ret = 0;
1390
1391 if (elem == NULL) return(0);
1392 cur = elem->attributes;
1393 while (cur != NULL) {
1394 if (cur->atype == XML_ATTRIBUTE_ID) {
1395 ret ++;
1396 if (ret > 1)
1397 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001398 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001399 elem->name, cur->name);
1400 }
1401 cur = cur->nexth;
1402 }
1403 return(ret);
1404}
1405
1406/**
1407 * xmlFreeAttribute:
1408 * @elem: An attribute
1409 *
1410 * Deallocate the memory used by an attribute definition
1411 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001412static void
Owen Taylor3473f882001-02-23 17:55:21 +00001413xmlFreeAttribute(xmlAttributePtr attr) {
1414 if (attr == NULL) return;
1415 xmlUnlinkNode((xmlNodePtr) attr);
1416 if (attr->tree != NULL)
1417 xmlFreeEnumeration(attr->tree);
1418 if (attr->elem != NULL)
1419 xmlFree((xmlChar *) attr->elem);
1420 if (attr->name != NULL)
1421 xmlFree((xmlChar *) attr->name);
1422 if (attr->defaultValue != NULL)
1423 xmlFree((xmlChar *) attr->defaultValue);
1424 if (attr->prefix != NULL)
1425 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001426 xmlFree(attr);
1427}
1428
1429
1430/**
1431 * xmlAddAttributeDecl:
1432 * @ctxt: the validation context
1433 * @dtd: pointer to the DTD
1434 * @elem: the element name
1435 * @name: the attribute name
1436 * @ns: the attribute namespace prefix
1437 * @type: the attribute type
1438 * @def: the attribute default type
1439 * @defaultValue: the attribute default value
1440 * @tree: if it's an enumeration, the associated list
1441 *
1442 * Register a new attribute declaration
1443 * Note that @tree becomes the ownership of the DTD
1444 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001445 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001446 */
1447xmlAttributePtr
1448xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1449 const xmlChar *name, const xmlChar *ns,
1450 xmlAttributeType type, xmlAttributeDefault def,
1451 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1452 xmlAttributePtr ret;
1453 xmlAttributeTablePtr table;
1454 xmlElementPtr elemDef;
1455
1456 if (dtd == NULL) {
1457 xmlGenericError(xmlGenericErrorContext,
1458 "xmlAddAttributeDecl: dtd == NULL\n");
1459 xmlFreeEnumeration(tree);
1460 return(NULL);
1461 }
1462 if (name == NULL) {
1463 xmlGenericError(xmlGenericErrorContext,
1464 "xmlAddAttributeDecl: name == NULL\n");
1465 xmlFreeEnumeration(tree);
1466 return(NULL);
1467 }
1468 if (elem == NULL) {
1469 xmlGenericError(xmlGenericErrorContext,
1470 "xmlAddAttributeDecl: elem == NULL\n");
1471 xmlFreeEnumeration(tree);
1472 return(NULL);
1473 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001474
Owen Taylor3473f882001-02-23 17:55:21 +00001475 /*
1476 * Check the type and possibly the default value.
1477 */
1478 switch (type) {
1479 case XML_ATTRIBUTE_CDATA:
1480 break;
1481 case XML_ATTRIBUTE_ID:
1482 break;
1483 case XML_ATTRIBUTE_IDREF:
1484 break;
1485 case XML_ATTRIBUTE_IDREFS:
1486 break;
1487 case XML_ATTRIBUTE_ENTITY:
1488 break;
1489 case XML_ATTRIBUTE_ENTITIES:
1490 break;
1491 case XML_ATTRIBUTE_NMTOKEN:
1492 break;
1493 case XML_ATTRIBUTE_NMTOKENS:
1494 break;
1495 case XML_ATTRIBUTE_ENUMERATION:
1496 break;
1497 case XML_ATTRIBUTE_NOTATION:
1498 break;
1499 default:
1500 xmlGenericError(xmlGenericErrorContext,
1501 "xmlAddAttributeDecl: unknown type %d\n", type);
1502 xmlFreeEnumeration(tree);
1503 return(NULL);
1504 }
1505 if ((defaultValue != NULL) &&
1506 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001507 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001508 elem, name, defaultValue);
1509 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001510 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001511 }
1512
1513 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001514 * Check first that an attribute defined in the external subset wasn't
1515 * already defined in the internal subset
1516 */
1517 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1518 (dtd->doc->intSubset != NULL) &&
1519 (dtd->doc->intSubset->attributes != NULL)) {
1520 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1521 if (ret != NULL)
1522 return(NULL);
1523 }
1524
1525 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001526 * Create the Attribute table if needed.
1527 */
1528 table = (xmlAttributeTablePtr) dtd->attributes;
1529 if (table == NULL) {
1530 table = xmlCreateAttributeTable();
1531 dtd->attributes = (void *) table;
1532 }
1533 if (table == NULL) {
1534 xmlGenericError(xmlGenericErrorContext,
1535 "xmlAddAttributeDecl: Table creation failed!\n");
1536 return(NULL);
1537 }
1538
1539
1540 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1541 if (ret == NULL) {
1542 xmlGenericError(xmlGenericErrorContext,
1543 "xmlAddAttributeDecl: out of memory\n");
1544 return(NULL);
1545 }
1546 memset(ret, 0, sizeof(xmlAttribute));
1547 ret->type = XML_ATTRIBUTE_DECL;
1548
1549 /*
1550 * fill the structure.
1551 */
1552 ret->atype = type;
1553 ret->name = xmlStrdup(name);
1554 ret->prefix = xmlStrdup(ns);
1555 ret->elem = xmlStrdup(elem);
1556 ret->def = def;
1557 ret->tree = tree;
1558 if (defaultValue != NULL)
1559 ret->defaultValue = xmlStrdup(defaultValue);
1560
1561 /*
1562 * Validity Check:
1563 * Search the DTD for previous declarations of the ATTLIST
1564 */
1565 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1566 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001567 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001568 */
1569 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001570 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001571 name, elem);
1572 xmlFreeAttribute(ret);
1573 return(NULL);
1574 }
1575
1576 /*
1577 * Validity Check:
1578 * Multiple ID per element
1579 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001580 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001581 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001582
Owen Taylor3473f882001-02-23 17:55:21 +00001583 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001584 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001585 VERROR(ctxt->userData,
1586 "Element %s has too may ID attributes defined : %s\n",
1587 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001588 ctxt->valid = 0;
1589 }
1590
Daniel Veillard48da9102001-08-07 01:10:10 +00001591 /*
1592 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001593 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001594 */
1595 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1596 ((ret->prefix != NULL &&
1597 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1598 ret->nexth = elemDef->attributes;
1599 elemDef->attributes = ret;
1600 } else {
1601 xmlAttributePtr tmp = elemDef->attributes;
1602
1603 while ((tmp != NULL) &&
1604 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1605 ((ret->prefix != NULL &&
1606 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1607 if (tmp->nexth == NULL)
1608 break;
1609 tmp = tmp->nexth;
1610 }
1611 if (tmp != NULL) {
1612 ret->nexth = tmp->nexth;
1613 tmp->nexth = ret;
1614 } else {
1615 ret->nexth = elemDef->attributes;
1616 elemDef->attributes = ret;
1617 }
1618 }
Owen Taylor3473f882001-02-23 17:55:21 +00001619 }
1620
1621 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001622 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001623 */
1624 ret->parent = dtd;
1625 ret->doc = dtd->doc;
1626 if (dtd->last == NULL) {
1627 dtd->children = dtd->last = (xmlNodePtr) ret;
1628 } else {
1629 dtd->last->next = (xmlNodePtr) ret;
1630 ret->prev = dtd->last;
1631 dtd->last = (xmlNodePtr) ret;
1632 }
1633 return(ret);
1634}
1635
1636/**
1637 * xmlFreeAttributeTable:
1638 * @table: An attribute table
1639 *
1640 * Deallocate the memory used by an entities hash table.
1641 */
1642void
1643xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1644 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1645}
1646
1647/**
1648 * xmlCopyAttribute:
1649 * @attr: An attribute
1650 *
1651 * Build a copy of an attribute.
1652 *
1653 * Returns the new xmlAttributePtr or NULL in case of error.
1654 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001655static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001656xmlCopyAttribute(xmlAttributePtr attr) {
1657 xmlAttributePtr cur;
1658
1659 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1660 if (cur == NULL) {
1661 xmlGenericError(xmlGenericErrorContext,
1662 "xmlCopyAttribute: out of memory !\n");
1663 return(NULL);
1664 }
1665 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001666 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001667 cur->atype = attr->atype;
1668 cur->def = attr->def;
1669 cur->tree = xmlCopyEnumeration(attr->tree);
1670 if (attr->elem != NULL)
1671 cur->elem = xmlStrdup(attr->elem);
1672 if (attr->name != NULL)
1673 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001674 if (attr->prefix != NULL)
1675 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001676 if (attr->defaultValue != NULL)
1677 cur->defaultValue = xmlStrdup(attr->defaultValue);
1678 return(cur);
1679}
1680
1681/**
1682 * xmlCopyAttributeTable:
1683 * @table: An attribute table
1684 *
1685 * Build a copy of an attribute table.
1686 *
1687 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1688 */
1689xmlAttributeTablePtr
1690xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1691 return((xmlAttributeTablePtr) xmlHashCopy(table,
1692 (xmlHashCopier) xmlCopyAttribute));
1693}
1694
1695/**
1696 * xmlDumpAttributeDecl:
1697 * @buf: the XML buffer output
1698 * @attr: An attribute declaration
1699 *
1700 * This will dump the content of the attribute declaration as an XML
1701 * DTD definition
1702 */
1703void
1704xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1705 xmlBufferWriteChar(buf, "<!ATTLIST ");
1706 xmlBufferWriteCHAR(buf, attr->elem);
1707 xmlBufferWriteChar(buf, " ");
1708 if (attr->prefix != NULL) {
1709 xmlBufferWriteCHAR(buf, attr->prefix);
1710 xmlBufferWriteChar(buf, ":");
1711 }
1712 xmlBufferWriteCHAR(buf, attr->name);
1713 switch (attr->atype) {
1714 case XML_ATTRIBUTE_CDATA:
1715 xmlBufferWriteChar(buf, " CDATA");
1716 break;
1717 case XML_ATTRIBUTE_ID:
1718 xmlBufferWriteChar(buf, " ID");
1719 break;
1720 case XML_ATTRIBUTE_IDREF:
1721 xmlBufferWriteChar(buf, " IDREF");
1722 break;
1723 case XML_ATTRIBUTE_IDREFS:
1724 xmlBufferWriteChar(buf, " IDREFS");
1725 break;
1726 case XML_ATTRIBUTE_ENTITY:
1727 xmlBufferWriteChar(buf, " ENTITY");
1728 break;
1729 case XML_ATTRIBUTE_ENTITIES:
1730 xmlBufferWriteChar(buf, " ENTITIES");
1731 break;
1732 case XML_ATTRIBUTE_NMTOKEN:
1733 xmlBufferWriteChar(buf, " NMTOKEN");
1734 break;
1735 case XML_ATTRIBUTE_NMTOKENS:
1736 xmlBufferWriteChar(buf, " NMTOKENS");
1737 break;
1738 case XML_ATTRIBUTE_ENUMERATION:
1739 xmlBufferWriteChar(buf, " (");
1740 xmlDumpEnumeration(buf, attr->tree);
1741 break;
1742 case XML_ATTRIBUTE_NOTATION:
1743 xmlBufferWriteChar(buf, " NOTATION (");
1744 xmlDumpEnumeration(buf, attr->tree);
1745 break;
1746 default:
1747 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001748 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001749 attr->atype);
1750 }
1751 switch (attr->def) {
1752 case XML_ATTRIBUTE_NONE:
1753 break;
1754 case XML_ATTRIBUTE_REQUIRED:
1755 xmlBufferWriteChar(buf, " #REQUIRED");
1756 break;
1757 case XML_ATTRIBUTE_IMPLIED:
1758 xmlBufferWriteChar(buf, " #IMPLIED");
1759 break;
1760 case XML_ATTRIBUTE_FIXED:
1761 xmlBufferWriteChar(buf, " #FIXED");
1762 break;
1763 default:
1764 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001765 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001766 attr->def);
1767 }
1768 if (attr->defaultValue != NULL) {
1769 xmlBufferWriteChar(buf, " ");
1770 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1771 }
1772 xmlBufferWriteChar(buf, ">\n");
1773}
1774
1775/**
1776 * xmlDumpAttributeTable:
1777 * @buf: the XML buffer output
1778 * @table: An attribute table
1779 *
1780 * This will dump the content of the attribute table as an XML DTD definition
1781 */
1782void
1783xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1784 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1785}
1786
1787/************************************************************************
1788 * *
1789 * NOTATIONs *
1790 * *
1791 ************************************************************************/
1792/**
1793 * xmlCreateNotationTable:
1794 *
1795 * create and initialize an empty notation hash table.
1796 *
1797 * Returns the xmlNotationTablePtr just created or NULL in case
1798 * of error.
1799 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001800static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001801xmlCreateNotationTable(void) {
1802 return(xmlHashCreate(0));
1803}
1804
1805/**
1806 * xmlFreeNotation:
1807 * @not: A notation
1808 *
1809 * Deallocate the memory used by an notation definition
1810 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001811static void
Owen Taylor3473f882001-02-23 17:55:21 +00001812xmlFreeNotation(xmlNotationPtr nota) {
1813 if (nota == NULL) return;
1814 if (nota->name != NULL)
1815 xmlFree((xmlChar *) nota->name);
1816 if (nota->PublicID != NULL)
1817 xmlFree((xmlChar *) nota->PublicID);
1818 if (nota->SystemID != NULL)
1819 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001820 xmlFree(nota);
1821}
1822
1823
1824/**
1825 * xmlAddNotationDecl:
1826 * @dtd: pointer to the DTD
1827 * @ctxt: the validation context
1828 * @name: the entity name
1829 * @PublicID: the public identifier or NULL
1830 * @SystemID: the system identifier or NULL
1831 *
1832 * Register a new notation declaration
1833 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001834 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001835 */
1836xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001837xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001838 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001839 const xmlChar *PublicID, const xmlChar *SystemID) {
1840 xmlNotationPtr ret;
1841 xmlNotationTablePtr table;
1842
1843 if (dtd == NULL) {
1844 xmlGenericError(xmlGenericErrorContext,
1845 "xmlAddNotationDecl: dtd == NULL\n");
1846 return(NULL);
1847 }
1848 if (name == NULL) {
1849 xmlGenericError(xmlGenericErrorContext,
1850 "xmlAddNotationDecl: name == NULL\n");
1851 return(NULL);
1852 }
1853 if ((PublicID == NULL) && (SystemID == NULL)) {
1854 xmlGenericError(xmlGenericErrorContext,
1855 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001856 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001857 }
1858
1859 /*
1860 * Create the Notation table if needed.
1861 */
1862 table = (xmlNotationTablePtr) dtd->notations;
1863 if (table == NULL)
1864 dtd->notations = table = xmlCreateNotationTable();
1865 if (table == NULL) {
1866 xmlGenericError(xmlGenericErrorContext,
1867 "xmlAddNotationDecl: Table creation failed!\n");
1868 return(NULL);
1869 }
1870
1871 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1872 if (ret == NULL) {
1873 xmlGenericError(xmlGenericErrorContext,
1874 "xmlAddNotationDecl: out of memory\n");
1875 return(NULL);
1876 }
1877 memset(ret, 0, sizeof(xmlNotation));
1878
1879 /*
1880 * fill the structure.
1881 */
1882 ret->name = xmlStrdup(name);
1883 if (SystemID != NULL)
1884 ret->SystemID = xmlStrdup(SystemID);
1885 if (PublicID != NULL)
1886 ret->PublicID = xmlStrdup(PublicID);
1887
1888 /*
1889 * Validity Check:
1890 * Check the DTD for previous declarations of the ATTLIST
1891 */
1892 if (xmlHashAddEntry(table, name, ret)) {
1893 xmlGenericError(xmlGenericErrorContext,
1894 "xmlAddNotationDecl: %s already defined\n", name);
1895 xmlFreeNotation(ret);
1896 return(NULL);
1897 }
1898 return(ret);
1899}
1900
1901/**
1902 * xmlFreeNotationTable:
1903 * @table: An notation table
1904 *
1905 * Deallocate the memory used by an entities hash table.
1906 */
1907void
1908xmlFreeNotationTable(xmlNotationTablePtr table) {
1909 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1910}
1911
1912/**
1913 * xmlCopyNotation:
1914 * @nota: A notation
1915 *
1916 * Build a copy of a notation.
1917 *
1918 * Returns the new xmlNotationPtr or NULL in case of error.
1919 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001920static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001921xmlCopyNotation(xmlNotationPtr nota) {
1922 xmlNotationPtr cur;
1923
1924 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1925 if (cur == NULL) {
1926 xmlGenericError(xmlGenericErrorContext,
1927 "xmlCopyNotation: out of memory !\n");
1928 return(NULL);
1929 }
1930 if (nota->name != NULL)
1931 cur->name = xmlStrdup(nota->name);
1932 else
1933 cur->name = NULL;
1934 if (nota->PublicID != NULL)
1935 cur->PublicID = xmlStrdup(nota->PublicID);
1936 else
1937 cur->PublicID = NULL;
1938 if (nota->SystemID != NULL)
1939 cur->SystemID = xmlStrdup(nota->SystemID);
1940 else
1941 cur->SystemID = NULL;
1942 return(cur);
1943}
1944
1945/**
1946 * xmlCopyNotationTable:
1947 * @table: A notation table
1948 *
1949 * Build a copy of a notation table.
1950 *
1951 * Returns the new xmlNotationTablePtr or NULL in case of error.
1952 */
1953xmlNotationTablePtr
1954xmlCopyNotationTable(xmlNotationTablePtr table) {
1955 return((xmlNotationTablePtr) xmlHashCopy(table,
1956 (xmlHashCopier) xmlCopyNotation));
1957}
1958
1959/**
1960 * xmlDumpNotationDecl:
1961 * @buf: the XML buffer output
1962 * @nota: A notation declaration
1963 *
1964 * This will dump the content the notation declaration as an XML DTD definition
1965 */
1966void
1967xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1968 xmlBufferWriteChar(buf, "<!NOTATION ");
1969 xmlBufferWriteCHAR(buf, nota->name);
1970 if (nota->PublicID != NULL) {
1971 xmlBufferWriteChar(buf, " PUBLIC ");
1972 xmlBufferWriteQuotedString(buf, nota->PublicID);
1973 if (nota->SystemID != NULL) {
1974 xmlBufferWriteChar(buf, " ");
1975 xmlBufferWriteCHAR(buf, nota->SystemID);
1976 }
1977 } else {
1978 xmlBufferWriteChar(buf, " SYSTEM ");
1979 xmlBufferWriteCHAR(buf, nota->SystemID);
1980 }
1981 xmlBufferWriteChar(buf, " >\n");
1982}
1983
1984/**
1985 * xmlDumpNotationTable:
1986 * @buf: the XML buffer output
1987 * @table: A notation table
1988 *
1989 * This will dump the content of the notation table as an XML DTD definition
1990 */
1991void
1992xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1993 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1994}
1995
1996/************************************************************************
1997 * *
1998 * IDs *
1999 * *
2000 ************************************************************************/
2001/**
2002 * xmlCreateIDTable:
2003 *
2004 * create and initialize an empty id hash table.
2005 *
2006 * Returns the xmlIDTablePtr just created or NULL in case
2007 * of error.
2008 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002009static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002010xmlCreateIDTable(void) {
2011 return(xmlHashCreate(0));
2012}
2013
2014/**
2015 * xmlFreeID:
2016 * @not: A id
2017 *
2018 * Deallocate the memory used by an id definition
2019 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002020static void
Owen Taylor3473f882001-02-23 17:55:21 +00002021xmlFreeID(xmlIDPtr id) {
2022 if (id == NULL) return;
2023 if (id->value != NULL)
2024 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00002025 xmlFree(id);
2026}
2027
2028/**
2029 * xmlAddID:
2030 * @ctxt: the validation context
2031 * @doc: pointer to the document
2032 * @value: the value name
2033 * @attr: the attribute holding the ID
2034 *
2035 * Register a new id declaration
2036 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002037 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002038 */
2039xmlIDPtr
2040xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2041 xmlAttrPtr attr) {
2042 xmlIDPtr ret;
2043 xmlIDTablePtr table;
2044
2045 if (doc == NULL) {
2046 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002047 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002048 return(NULL);
2049 }
2050 if (value == NULL) {
2051 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002052 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002053 return(NULL);
2054 }
2055 if (attr == NULL) {
2056 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002057 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002058 return(NULL);
2059 }
2060
2061 /*
2062 * Create the ID table if needed.
2063 */
2064 table = (xmlIDTablePtr) doc->ids;
2065 if (table == NULL)
2066 doc->ids = table = xmlCreateIDTable();
2067 if (table == NULL) {
2068 xmlGenericError(xmlGenericErrorContext,
2069 "xmlAddID: Table creation failed!\n");
2070 return(NULL);
2071 }
2072
2073 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2074 if (ret == NULL) {
2075 xmlGenericError(xmlGenericErrorContext,
2076 "xmlAddID: out of memory\n");
2077 return(NULL);
2078 }
2079
2080 /*
2081 * fill the structure.
2082 */
2083 ret->value = xmlStrdup(value);
2084 ret->attr = attr;
2085
2086 if (xmlHashAddEntry(table, value, ret) < 0) {
2087 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002088 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002089 */
Daniel Veillard76575762002-09-05 14:21:15 +00002090 if (ctxt != NULL) {
2091 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002092 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002093 }
Owen Taylor3473f882001-02-23 17:55:21 +00002094 xmlFreeID(ret);
2095 return(NULL);
2096 }
2097 return(ret);
2098}
2099
2100/**
2101 * xmlFreeIDTable:
2102 * @table: An id table
2103 *
2104 * Deallocate the memory used by an ID hash table.
2105 */
2106void
2107xmlFreeIDTable(xmlIDTablePtr table) {
2108 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2109}
2110
2111/**
2112 * xmlIsID:
2113 * @doc: the document
2114 * @elem: the element carrying the attribute
2115 * @attr: the attribute
2116 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002117 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002118 * then this is simple, otherwise we use an heuristic: name ID (upper
2119 * or lowercase).
2120 *
2121 * Returns 0 or 1 depending on the lookup result
2122 */
2123int
2124xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2125 if (doc == NULL) return(0);
2126 if (attr == NULL) return(0);
2127 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2128 return(0);
2129 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2130 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2131 (xmlStrEqual(BAD_CAST "name", attr->name)))
2132 return(1);
2133 return(0);
2134 } else {
2135 xmlAttributePtr attrDecl;
2136
2137 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002138 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2139 /*
2140 * TODO: this sucks ... recomputing this every time is stupid
2141 */
2142 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2143 xmlChar *fullname;
2144
2145 fullname = xmlMalloc(len);
2146 if (fullname == NULL)
2147 return(0);
2148 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2149 (char *) elem->name);
2150 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2151 attr->name);
2152 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2153 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2154 attr->name);
2155 xmlFree(fullname);
2156 } else {
2157 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2158 attr->name);
2159 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2160 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2161 attr->name);
2162 }
Owen Taylor3473f882001-02-23 17:55:21 +00002163
2164 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2165 return(1);
2166 }
2167 return(0);
2168}
2169
2170/**
2171 * xmlRemoveID
2172 * @doc: the document
2173 * @attr: the attribute
2174 *
2175 * Remove the given attribute from the ID table maintained internally.
2176 *
2177 * Returns -1 if the lookup failed and 0 otherwise
2178 */
2179int
2180xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2181 xmlAttrPtr cur;
2182 xmlIDTablePtr table;
2183 xmlChar *ID;
2184
2185 if (doc == NULL) return(-1);
2186 if (attr == NULL) return(-1);
2187 table = (xmlIDTablePtr) doc->ids;
2188 if (table == NULL)
2189 return(-1);
2190
2191 if (attr == NULL)
2192 return(-1);
2193 ID = xmlNodeListGetString(doc, attr->children, 1);
2194 if (ID == NULL)
2195 return(-1);
2196 cur = xmlHashLookup(table, ID);
2197 if (cur != attr) {
2198 xmlFree(ID);
2199 return(-1);
2200 }
2201 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2202 xmlFree(ID);
2203 return(0);
2204}
2205
2206/**
2207 * xmlGetID:
2208 * @doc: pointer to the document
2209 * @ID: the ID value
2210 *
2211 * Search the attribute declaring the given ID
2212 *
2213 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2214 */
2215xmlAttrPtr
2216xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2217 xmlIDTablePtr table;
2218 xmlIDPtr id;
2219
2220 if (doc == NULL) {
2221 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2222 return(NULL);
2223 }
2224
2225 if (ID == NULL) {
2226 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2227 return(NULL);
2228 }
2229
2230 table = (xmlIDTablePtr) doc->ids;
2231 if (table == NULL)
2232 return(NULL);
2233
2234 id = xmlHashLookup(table, ID);
2235 if (id == NULL)
2236 return(NULL);
2237 return(id->attr);
2238}
2239
2240/************************************************************************
2241 * *
2242 * Refs *
2243 * *
2244 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002245typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002246{
2247 xmlListPtr l;
2248 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002249} xmlRemoveMemo;
2250
2251typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2252
2253typedef struct xmlValidateMemo_t
2254{
2255 xmlValidCtxtPtr ctxt;
2256 const xmlChar *name;
2257} xmlValidateMemo;
2258
2259typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002260
2261/**
2262 * xmlCreateRefTable:
2263 *
2264 * create and initialize an empty ref hash table.
2265 *
2266 * Returns the xmlRefTablePtr just created or NULL in case
2267 * of error.
2268 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002269static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002270xmlCreateRefTable(void) {
2271 return(xmlHashCreate(0));
2272}
2273
2274/**
2275 * xmlFreeRef:
2276 * @lk: A list link
2277 *
2278 * Deallocate the memory used by a ref definition
2279 */
2280static void
2281xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002282 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2283 if (ref == NULL) return;
2284 if (ref->value != NULL)
2285 xmlFree((xmlChar *)ref->value);
2286 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002287}
2288
2289/**
2290 * xmlFreeRefList:
2291 * @list_ref: A list of references.
2292 *
2293 * Deallocate the memory used by a list of references
2294 */
2295static void
2296xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002297 if (list_ref == NULL) return;
2298 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002299}
2300
2301/**
2302 * xmlWalkRemoveRef:
2303 * @data: Contents of current link
2304 * @user: Value supplied by the user
2305 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002306 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002307 */
2308static int
2309xmlWalkRemoveRef(const void *data, const void *user)
2310{
Daniel Veillard37721922001-05-04 15:21:12 +00002311 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2312 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2313 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002314
Daniel Veillard37721922001-05-04 15:21:12 +00002315 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2316 xmlListRemoveFirst(ref_list, (void *)data);
2317 return 0;
2318 }
2319 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002320}
2321
2322/**
2323 * xmlAddRef:
2324 * @ctxt: the validation context
2325 * @doc: pointer to the document
2326 * @value: the value name
2327 * @attr: the attribute holding the Ref
2328 *
2329 * Register a new ref declaration
2330 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002331 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002332 */
2333xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002334xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002335 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002336 xmlRefPtr ret;
2337 xmlRefTablePtr table;
2338 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002339
Daniel Veillard37721922001-05-04 15:21:12 +00002340 if (doc == NULL) {
2341 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002342 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002343 return(NULL);
2344 }
2345 if (value == NULL) {
2346 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002347 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002348 return(NULL);
2349 }
2350 if (attr == NULL) {
2351 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002352 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002353 return(NULL);
2354 }
Owen Taylor3473f882001-02-23 17:55:21 +00002355
Daniel Veillard37721922001-05-04 15:21:12 +00002356 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002357 * Create the Ref table if needed.
2358 */
Daniel Veillard37721922001-05-04 15:21:12 +00002359 table = (xmlRefTablePtr) doc->refs;
2360 if (table == NULL)
2361 doc->refs = table = xmlCreateRefTable();
2362 if (table == NULL) {
2363 xmlGenericError(xmlGenericErrorContext,
2364 "xmlAddRef: Table creation failed!\n");
2365 return(NULL);
2366 }
Owen Taylor3473f882001-02-23 17:55:21 +00002367
Daniel Veillard37721922001-05-04 15:21:12 +00002368 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2369 if (ret == NULL) {
2370 xmlGenericError(xmlGenericErrorContext,
2371 "xmlAddRef: out of memory\n");
2372 return(NULL);
2373 }
Owen Taylor3473f882001-02-23 17:55:21 +00002374
Daniel Veillard37721922001-05-04 15:21:12 +00002375 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002376 * fill the structure.
2377 */
Daniel Veillard37721922001-05-04 15:21:12 +00002378 ret->value = xmlStrdup(value);
2379 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002380
Daniel Veillard37721922001-05-04 15:21:12 +00002381 /* To add a reference :-
2382 * References are maintained as a list of references,
2383 * Lookup the entry, if no entry create new nodelist
2384 * Add the owning node to the NodeList
2385 * Return the ref
2386 */
Owen Taylor3473f882001-02-23 17:55:21 +00002387
Daniel Veillard37721922001-05-04 15:21:12 +00002388 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2389 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2390 xmlGenericError(xmlGenericErrorContext,
2391 "xmlAddRef: Reference list creation failed!\n");
2392 return(NULL);
2393 }
2394 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2395 xmlListDelete(ref_list);
2396 xmlGenericError(xmlGenericErrorContext,
2397 "xmlAddRef: Reference list insertion failed!\n");
2398 return(NULL);
2399 }
2400 }
2401 xmlListInsert(ref_list, ret);
2402 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002403}
2404
2405/**
2406 * xmlFreeRefTable:
2407 * @table: An ref table
2408 *
2409 * Deallocate the memory used by an Ref hash table.
2410 */
2411void
2412xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002413 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002414}
2415
2416/**
2417 * xmlIsRef:
2418 * @doc: the document
2419 * @elem: the element carrying the attribute
2420 * @attr: the attribute
2421 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002422 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002423 * then this is simple, otherwise we use an heuristic: name Ref (upper
2424 * or lowercase).
2425 *
2426 * Returns 0 or 1 depending on the lookup result
2427 */
2428int
2429xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002430 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2431 return(0);
2432 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2433 /* TODO @@@ */
2434 return(0);
2435 } else {
2436 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002437
Daniel Veillard37721922001-05-04 15:21:12 +00002438 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2439 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2440 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2441 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002442
Daniel Veillard37721922001-05-04 15:21:12 +00002443 if ((attrDecl != NULL) &&
2444 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2445 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2446 return(1);
2447 }
2448 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002449}
2450
2451/**
2452 * xmlRemoveRef
2453 * @doc: the document
2454 * @attr: the attribute
2455 *
2456 * Remove the given attribute from the Ref table maintained internally.
2457 *
2458 * Returns -1 if the lookup failed and 0 otherwise
2459 */
2460int
2461xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002462 xmlListPtr ref_list;
2463 xmlRefTablePtr table;
2464 xmlChar *ID;
2465 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002466
Daniel Veillard37721922001-05-04 15:21:12 +00002467 if (doc == NULL) return(-1);
2468 if (attr == NULL) return(-1);
2469 table = (xmlRefTablePtr) doc->refs;
2470 if (table == NULL)
2471 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002472
Daniel Veillard37721922001-05-04 15:21:12 +00002473 if (attr == NULL)
2474 return(-1);
2475 ID = xmlNodeListGetString(doc, attr->children, 1);
2476 if (ID == NULL)
2477 return(-1);
2478 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002479
Daniel Veillard37721922001-05-04 15:21:12 +00002480 if(ref_list == NULL) {
2481 xmlFree(ID);
2482 return (-1);
2483 }
2484 /* At this point, ref_list refers to a list of references which
2485 * have the same key as the supplied attr. Our list of references
2486 * is ordered by reference address and we don't have that information
2487 * here to use when removing. We'll have to walk the list and
2488 * check for a matching attribute, when we find one stop the walk
2489 * and remove the entry.
2490 * The list is ordered by reference, so that means we don't have the
2491 * key. Passing the list and the reference to the walker means we
2492 * will have enough data to be able to remove the entry.
2493 */
2494 target.l = ref_list;
2495 target.ap = attr;
2496
2497 /* Remove the supplied attr from our list */
2498 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002499
Daniel Veillard37721922001-05-04 15:21:12 +00002500 /*If the list is empty then remove the list entry in the hash */
2501 if (xmlListEmpty(ref_list))
2502 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2503 xmlFreeRefList);
2504 xmlFree(ID);
2505 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002506}
2507
2508/**
2509 * xmlGetRefs:
2510 * @doc: pointer to the document
2511 * @ID: the ID value
2512 *
2513 * Find the set of references for the supplied ID.
2514 *
2515 * Returns NULL if not found, otherwise node set for the ID.
2516 */
2517xmlListPtr
2518xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002519 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002520
Daniel Veillard37721922001-05-04 15:21:12 +00002521 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002522 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002523 return(NULL);
2524 }
Owen Taylor3473f882001-02-23 17:55:21 +00002525
Daniel Veillard37721922001-05-04 15:21:12 +00002526 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002527 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == 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 table = (xmlRefTablePtr) doc->refs;
2532 if (table == NULL)
2533 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002534
Daniel Veillard37721922001-05-04 15:21:12 +00002535 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002536}
2537
2538/************************************************************************
2539 * *
2540 * Routines for validity checking *
2541 * *
2542 ************************************************************************/
2543
2544/**
2545 * xmlGetDtdElementDesc:
2546 * @dtd: a pointer to the DtD to search
2547 * @name: the element name
2548 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002549 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002550 *
2551 * returns the xmlElementPtr if found or NULL
2552 */
2553
2554xmlElementPtr
2555xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2556 xmlElementTablePtr table;
2557 xmlElementPtr cur;
2558 xmlChar *uqname = NULL, *prefix = NULL;
2559
2560 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002561 if (dtd->elements == NULL)
2562 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002563 table = (xmlElementTablePtr) dtd->elements;
2564
2565 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002566 if (uqname != NULL)
2567 name = uqname;
2568 cur = xmlHashLookup2(table, name, prefix);
2569 if (prefix != NULL) xmlFree(prefix);
2570 if (uqname != NULL) xmlFree(uqname);
2571 return(cur);
2572}
2573/**
2574 * xmlGetDtdElementDesc2:
2575 * @dtd: a pointer to the DtD to search
2576 * @name: the element name
2577 * @create: create an empty description if not found
2578 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002579 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002580 *
2581 * returns the xmlElementPtr if found or NULL
2582 */
2583
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002584static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002585xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2586 xmlElementTablePtr table;
2587 xmlElementPtr cur;
2588 xmlChar *uqname = NULL, *prefix = NULL;
2589
2590 if (dtd == NULL) return(NULL);
2591 if (dtd->elements == NULL) {
2592 if (!create)
2593 return(NULL);
2594 /*
2595 * Create the Element table if needed.
2596 */
2597 table = (xmlElementTablePtr) dtd->elements;
2598 if (table == NULL) {
2599 table = xmlCreateElementTable();
2600 dtd->elements = (void *) table;
2601 }
2602 if (table == NULL) {
2603 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002604 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002605 return(NULL);
2606 }
2607 }
2608 table = (xmlElementTablePtr) dtd->elements;
2609
2610 uqname = xmlSplitQName2(name, &prefix);
2611 if (uqname != NULL)
2612 name = uqname;
2613 cur = xmlHashLookup2(table, name, prefix);
2614 if ((cur == NULL) && (create)) {
2615 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2616 if (cur == NULL) {
2617 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002618 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002619 return(NULL);
2620 }
2621 memset(cur, 0, sizeof(xmlElement));
2622 cur->type = XML_ELEMENT_DECL;
2623
2624 /*
2625 * fill the structure.
2626 */
2627 cur->name = xmlStrdup(name);
2628 cur->prefix = xmlStrdup(prefix);
2629 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2630
2631 xmlHashAddEntry2(table, name, prefix, cur);
2632 }
2633 if (prefix != NULL) xmlFree(prefix);
2634 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002635 return(cur);
2636}
2637
2638/**
2639 * xmlGetDtdQElementDesc:
2640 * @dtd: a pointer to the DtD to search
2641 * @name: the element name
2642 * @prefix: the element namespace prefix
2643 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002644 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002645 *
2646 * returns the xmlElementPtr if found or NULL
2647 */
2648
Daniel Veillard48da9102001-08-07 01:10:10 +00002649xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002650xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2651 const xmlChar *prefix) {
2652 xmlElementTablePtr table;
2653
2654 if (dtd == NULL) return(NULL);
2655 if (dtd->elements == NULL) return(NULL);
2656 table = (xmlElementTablePtr) dtd->elements;
2657
2658 return(xmlHashLookup2(table, name, prefix));
2659}
2660
2661/**
2662 * xmlGetDtdAttrDesc:
2663 * @dtd: a pointer to the DtD to search
2664 * @elem: the element name
2665 * @name: the attribute name
2666 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002667 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002668 * this element.
2669 *
2670 * returns the xmlAttributePtr if found or NULL
2671 */
2672
2673xmlAttributePtr
2674xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2675 xmlAttributeTablePtr table;
2676 xmlAttributePtr cur;
2677 xmlChar *uqname = NULL, *prefix = NULL;
2678
2679 if (dtd == NULL) return(NULL);
2680 if (dtd->attributes == NULL) return(NULL);
2681
2682 table = (xmlAttributeTablePtr) dtd->attributes;
2683 if (table == NULL)
2684 return(NULL);
2685
2686 uqname = xmlSplitQName2(name, &prefix);
2687
2688 if (uqname != NULL) {
2689 cur = xmlHashLookup3(table, uqname, prefix, elem);
2690 if (prefix != NULL) xmlFree(prefix);
2691 if (uqname != NULL) xmlFree(uqname);
2692 } else
2693 cur = xmlHashLookup3(table, name, NULL, elem);
2694 return(cur);
2695}
2696
2697/**
2698 * xmlGetDtdQAttrDesc:
2699 * @dtd: a pointer to the DtD to search
2700 * @elem: the element name
2701 * @name: the attribute name
2702 * @prefix: the attribute namespace prefix
2703 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002704 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002705 * this element.
2706 *
2707 * returns the xmlAttributePtr if found or NULL
2708 */
2709
Daniel Veillard48da9102001-08-07 01:10:10 +00002710xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002711xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2712 const xmlChar *prefix) {
2713 xmlAttributeTablePtr table;
2714
2715 if (dtd == NULL) return(NULL);
2716 if (dtd->attributes == NULL) return(NULL);
2717 table = (xmlAttributeTablePtr) dtd->attributes;
2718
2719 return(xmlHashLookup3(table, name, prefix, elem));
2720}
2721
2722/**
2723 * xmlGetDtdNotationDesc:
2724 * @dtd: a pointer to the DtD to search
2725 * @name: the notation name
2726 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002727 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002728 *
2729 * returns the xmlNotationPtr if found or NULL
2730 */
2731
2732xmlNotationPtr
2733xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2734 xmlNotationTablePtr table;
2735
2736 if (dtd == NULL) return(NULL);
2737 if (dtd->notations == NULL) return(NULL);
2738 table = (xmlNotationTablePtr) dtd->notations;
2739
2740 return(xmlHashLookup(table, name));
2741}
2742
2743/**
2744 * xmlValidateNotationUse:
2745 * @ctxt: the validation context
2746 * @doc: the document
2747 * @notationName: the notation name to check
2748 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002749 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002750 * - [ VC: Notation Declared ]
2751 *
2752 * returns 1 if valid or 0 otherwise
2753 */
2754
2755int
2756xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2757 const xmlChar *notationName) {
2758 xmlNotationPtr notaDecl;
2759 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2760
2761 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2762 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2763 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2764
2765 if (notaDecl == NULL) {
2766 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2767 notationName);
2768 return(0);
2769 }
2770 return(1);
2771}
2772
2773/**
2774 * xmlIsMixedElement
2775 * @doc: the document
2776 * @name: the element name
2777 *
2778 * Search in the DtDs whether an element accept Mixed content (or ANY)
2779 * basically if it is supposed to accept text childs
2780 *
2781 * returns 0 if no, 1 if yes, and -1 if no element description is available
2782 */
2783
2784int
2785xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2786 xmlElementPtr elemDecl;
2787
2788 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2789
2790 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2791 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2792 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2793 if (elemDecl == NULL) return(-1);
2794 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002795 case XML_ELEMENT_TYPE_UNDEFINED:
2796 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002797 case XML_ELEMENT_TYPE_ELEMENT:
2798 return(0);
2799 case XML_ELEMENT_TYPE_EMPTY:
2800 /*
2801 * return 1 for EMPTY since we want VC error to pop up
2802 * on <empty> </empty> for example
2803 */
2804 case XML_ELEMENT_TYPE_ANY:
2805 case XML_ELEMENT_TYPE_MIXED:
2806 return(1);
2807 }
2808 return(1);
2809}
2810
2811/**
2812 * xmlValidateNameValue:
2813 * @value: an Name value
2814 *
2815 * Validate that the given value match Name production
2816 *
2817 * returns 1 if valid or 0 otherwise
2818 */
2819
Daniel Veillard9b731d72002-04-14 12:56:08 +00002820int
Owen Taylor3473f882001-02-23 17:55:21 +00002821xmlValidateNameValue(const xmlChar *value) {
2822 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002823 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002824
2825 if (value == NULL) return(0);
2826 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002827 val = xmlStringCurrentChar(NULL, cur, &len);
2828 cur += len;
2829 if (!IS_LETTER(val) && (val != '_') &&
2830 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002831 return(0);
2832 }
2833
Daniel Veillardd8224e02002-01-13 15:43:22 +00002834 val = xmlStringCurrentChar(NULL, cur, &len);
2835 cur += len;
2836 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2837 (val == '.') || (val == '-') ||
2838 (val == '_') || (val == ':') ||
2839 (IS_COMBINING(val)) ||
2840 (IS_EXTENDER(val))) {
2841 val = xmlStringCurrentChar(NULL, cur, &len);
2842 cur += len;
2843 }
Owen Taylor3473f882001-02-23 17:55:21 +00002844
Daniel Veillardd8224e02002-01-13 15:43:22 +00002845 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002846
2847 return(1);
2848}
2849
2850/**
2851 * xmlValidateNamesValue:
2852 * @value: an Names value
2853 *
2854 * Validate that the given value match Names production
2855 *
2856 * returns 1 if valid or 0 otherwise
2857 */
2858
Daniel Veillard9b731d72002-04-14 12:56:08 +00002859int
Owen Taylor3473f882001-02-23 17:55:21 +00002860xmlValidateNamesValue(const xmlChar *value) {
2861 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002862 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002863
2864 if (value == NULL) return(0);
2865 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002866 val = xmlStringCurrentChar(NULL, cur, &len);
2867 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002868
Daniel Veillardd8224e02002-01-13 15:43:22 +00002869 if (!IS_LETTER(val) && (val != '_') &&
2870 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002871 return(0);
2872 }
2873
Daniel Veillardd8224e02002-01-13 15:43:22 +00002874 val = xmlStringCurrentChar(NULL, cur, &len);
2875 cur += len;
2876 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2877 (val == '.') || (val == '-') ||
2878 (val == '_') || (val == ':') ||
2879 (IS_COMBINING(val)) ||
2880 (IS_EXTENDER(val))) {
2881 val = xmlStringCurrentChar(NULL, cur, &len);
2882 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002883 }
2884
Daniel Veillardd8224e02002-01-13 15:43:22 +00002885 while (IS_BLANK(val)) {
2886 while (IS_BLANK(val)) {
2887 val = xmlStringCurrentChar(NULL, cur, &len);
2888 cur += len;
2889 }
2890
2891 if (!IS_LETTER(val) && (val != '_') &&
2892 (val != ':')) {
2893 return(0);
2894 }
2895 val = xmlStringCurrentChar(NULL, cur, &len);
2896 cur += len;
2897
2898 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2899 (val == '.') || (val == '-') ||
2900 (val == '_') || (val == ':') ||
2901 (IS_COMBINING(val)) ||
2902 (IS_EXTENDER(val))) {
2903 val = xmlStringCurrentChar(NULL, cur, &len);
2904 cur += len;
2905 }
2906 }
2907
2908 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002909
2910 return(1);
2911}
2912
2913/**
2914 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002915 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002916 *
2917 * Validate that the given value match Nmtoken production
2918 *
2919 * [ VC: Name Token ]
2920 *
2921 * returns 1 if valid or 0 otherwise
2922 */
2923
Daniel Veillard9b731d72002-04-14 12:56:08 +00002924int
Owen Taylor3473f882001-02-23 17:55:21 +00002925xmlValidateNmtokenValue(const xmlChar *value) {
2926 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002927 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002928
2929 if (value == NULL) return(0);
2930 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002931 val = xmlStringCurrentChar(NULL, cur, &len);
2932 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002933
Daniel Veillardd8224e02002-01-13 15:43:22 +00002934 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2935 (val != '.') && (val != '-') &&
2936 (val != '_') && (val != ':') &&
2937 (!IS_COMBINING(val)) &&
2938 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002939 return(0);
2940
Daniel Veillardd8224e02002-01-13 15:43:22 +00002941 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2942 (val == '.') || (val == '-') ||
2943 (val == '_') || (val == ':') ||
2944 (IS_COMBINING(val)) ||
2945 (IS_EXTENDER(val))) {
2946 val = xmlStringCurrentChar(NULL, cur, &len);
2947 cur += len;
2948 }
Owen Taylor3473f882001-02-23 17:55:21 +00002949
Daniel Veillardd8224e02002-01-13 15:43:22 +00002950 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002951
2952 return(1);
2953}
2954
2955/**
2956 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002957 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002958 *
2959 * Validate that the given value match Nmtokens production
2960 *
2961 * [ VC: Name Token ]
2962 *
2963 * returns 1 if valid or 0 otherwise
2964 */
2965
Daniel Veillard9b731d72002-04-14 12:56:08 +00002966int
Owen Taylor3473f882001-02-23 17:55:21 +00002967xmlValidateNmtokensValue(const xmlChar *value) {
2968 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002969 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002970
2971 if (value == NULL) return(0);
2972 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002973 val = xmlStringCurrentChar(NULL, cur, &len);
2974 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002975
Daniel Veillardd8224e02002-01-13 15:43:22 +00002976 while (IS_BLANK(val)) {
2977 val = xmlStringCurrentChar(NULL, cur, &len);
2978 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002979 }
2980
Daniel Veillardd8224e02002-01-13 15:43:22 +00002981 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2982 (val != '.') && (val != '-') &&
2983 (val != '_') && (val != ':') &&
2984 (!IS_COMBINING(val)) &&
2985 (!IS_EXTENDER(val)))
2986 return(0);
2987
2988 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2989 (val == '.') || (val == '-') ||
2990 (val == '_') || (val == ':') ||
2991 (IS_COMBINING(val)) ||
2992 (IS_EXTENDER(val))) {
2993 val = xmlStringCurrentChar(NULL, cur, &len);
2994 cur += len;
2995 }
2996
2997 while (IS_BLANK(val)) {
2998 while (IS_BLANK(val)) {
2999 val = xmlStringCurrentChar(NULL, cur, &len);
3000 cur += len;
3001 }
3002 if (val == 0) return(1);
3003
3004 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3005 (val != '.') && (val != '-') &&
3006 (val != '_') && (val != ':') &&
3007 (!IS_COMBINING(val)) &&
3008 (!IS_EXTENDER(val)))
3009 return(0);
3010
3011 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3012 (val == '.') || (val == '-') ||
3013 (val == '_') || (val == ':') ||
3014 (IS_COMBINING(val)) ||
3015 (IS_EXTENDER(val))) {
3016 val = xmlStringCurrentChar(NULL, cur, &len);
3017 cur += len;
3018 }
3019 }
3020
3021 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003022
3023 return(1);
3024}
3025
3026/**
3027 * xmlValidateNotationDecl:
3028 * @ctxt: the validation context
3029 * @doc: a document instance
3030 * @nota: a notation definition
3031 *
3032 * Try to validate a single notation definition
3033 * basically it does the following checks as described by the
3034 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003035 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003036 * But this function get called anyway ...
3037 *
3038 * returns 1 if valid or 0 otherwise
3039 */
3040
3041int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003042xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3043 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003044 int ret = 1;
3045
3046 return(ret);
3047}
3048
3049/**
3050 * xmlValidateAttributeValue:
3051 * @type: an attribute type
3052 * @value: an attribute value
3053 *
3054 * Validate that the given attribute value match the proper production
3055 *
3056 * [ VC: ID ]
3057 * Values of type ID must match the Name production....
3058 *
3059 * [ VC: IDREF ]
3060 * Values of type IDREF must match the Name production, and values
3061 * of type IDREFS must match Names ...
3062 *
3063 * [ VC: Entity Name ]
3064 * Values of type ENTITY must match the Name production, values
3065 * of type ENTITIES must match Names ...
3066 *
3067 * [ VC: Name Token ]
3068 * Values of type NMTOKEN must match the Nmtoken production; values
3069 * of type NMTOKENS must match Nmtokens.
3070 *
3071 * returns 1 if valid or 0 otherwise
3072 */
3073
3074int
3075xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3076 switch (type) {
3077 case XML_ATTRIBUTE_ENTITIES:
3078 case XML_ATTRIBUTE_IDREFS:
3079 return(xmlValidateNamesValue(value));
3080 case XML_ATTRIBUTE_ENTITY:
3081 case XML_ATTRIBUTE_IDREF:
3082 case XML_ATTRIBUTE_ID:
3083 case XML_ATTRIBUTE_NOTATION:
3084 return(xmlValidateNameValue(value));
3085 case XML_ATTRIBUTE_NMTOKENS:
3086 case XML_ATTRIBUTE_ENUMERATION:
3087 return(xmlValidateNmtokensValue(value));
3088 case XML_ATTRIBUTE_NMTOKEN:
3089 return(xmlValidateNmtokenValue(value));
3090 case XML_ATTRIBUTE_CDATA:
3091 break;
3092 }
3093 return(1);
3094}
3095
3096/**
3097 * xmlValidateAttributeValue2:
3098 * @ctxt: the validation context
3099 * @doc: the document
3100 * @name: the attribute name (used for error reporting only)
3101 * @type: the attribute type
3102 * @value: the attribute value
3103 *
3104 * Validate that the given attribute value match a given type.
3105 * This typically cannot be done before having finished parsing
3106 * the subsets.
3107 *
3108 * [ VC: IDREF ]
3109 * Values of type IDREF must match one of the declared IDs
3110 * Values of type IDREFS must match a sequence of the declared IDs
3111 * each Name must match the value of an ID attribute on some element
3112 * in the XML document; i.e. IDREF values must match the value of
3113 * some ID attribute
3114 *
3115 * [ VC: Entity Name ]
3116 * Values of type ENTITY must match one declared entity
3117 * Values of type ENTITIES must match a sequence of declared entities
3118 *
3119 * [ VC: Notation Attributes ]
3120 * all notation names in the declaration must be declared.
3121 *
3122 * returns 1 if valid or 0 otherwise
3123 */
3124
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003125static int
Owen Taylor3473f882001-02-23 17:55:21 +00003126xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3127 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3128 int ret = 1;
3129 switch (type) {
3130 case XML_ATTRIBUTE_IDREFS:
3131 case XML_ATTRIBUTE_IDREF:
3132 case XML_ATTRIBUTE_ID:
3133 case XML_ATTRIBUTE_NMTOKENS:
3134 case XML_ATTRIBUTE_ENUMERATION:
3135 case XML_ATTRIBUTE_NMTOKEN:
3136 case XML_ATTRIBUTE_CDATA:
3137 break;
3138 case XML_ATTRIBUTE_ENTITY: {
3139 xmlEntityPtr ent;
3140
3141 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003142 if ((ent == NULL) && (doc->standalone == 1)) {
3143 doc->standalone = 0;
3144 ent = xmlGetDocEntity(doc, value);
3145 if (ent != NULL) {
3146 VERROR(ctxt->userData,
3147"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3148 name, value);
3149 /* WAIT to get answer from the Core WG on this
3150 ret = 0;
3151 */
3152 }
3153 }
Owen Taylor3473f882001-02-23 17:55:21 +00003154 if (ent == NULL) {
3155 VERROR(ctxt->userData,
3156 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3157 name, value);
3158 ret = 0;
3159 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3160 VERROR(ctxt->userData,
3161 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3162 name, value);
3163 ret = 0;
3164 }
3165 break;
3166 }
3167 case XML_ATTRIBUTE_ENTITIES: {
3168 xmlChar *dup, *nam = NULL, *cur, save;
3169 xmlEntityPtr ent;
3170
3171 dup = xmlStrdup(value);
3172 if (dup == NULL)
3173 return(0);
3174 cur = dup;
3175 while (*cur != 0) {
3176 nam = cur;
3177 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3178 save = *cur;
3179 *cur = 0;
3180 ent = xmlGetDocEntity(doc, nam);
3181 if (ent == NULL) {
3182 VERROR(ctxt->userData,
3183 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3184 name, nam);
3185 ret = 0;
3186 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3187 VERROR(ctxt->userData,
3188 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3189 name, nam);
3190 ret = 0;
3191 }
3192 if (save == 0)
3193 break;
3194 *cur = save;
3195 while (IS_BLANK(*cur)) cur++;
3196 }
3197 xmlFree(dup);
3198 break;
3199 }
3200 case XML_ATTRIBUTE_NOTATION: {
3201 xmlNotationPtr nota;
3202
3203 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3204 if ((nota == NULL) && (doc->extSubset != NULL))
3205 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3206
3207 if (nota == NULL) {
3208 VERROR(ctxt->userData,
3209 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3210 name, value);
3211 ret = 0;
3212 }
3213 break;
3214 }
3215 }
3216 return(ret);
3217}
3218
3219/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003220 * xmlValidCtxtNormalizeAttributeValue:
3221 * @ctxt: the validation context
3222 * @doc: the document
3223 * @elem: the parent
3224 * @name: the attribute name
3225 * @value: the attribute value
3226 * @ctxt: the validation context or NULL
3227 *
3228 * Does the validation related extra step of the normalization of attribute
3229 * values:
3230 *
3231 * If the declared value is not CDATA, then the XML processor must further
3232 * process the normalized attribute value by discarding any leading and
3233 * trailing space (#x20) characters, and by replacing sequences of space
3234 * (#x20) characters by single space (#x20) character.
3235 *
3236 * Also check VC: Standalone Document Declaration in P32, and update
3237 * ctxt->valid accordingly
3238 *
3239 * returns a new normalized string if normalization is needed, NULL otherwise
3240 * the caller must free the returned value.
3241 */
3242
3243xmlChar *
3244xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3245 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3246 xmlChar *ret, *dst;
3247 const xmlChar *src;
3248 xmlAttributePtr attrDecl = NULL;
3249 int extsubset = 0;
3250
3251 if (doc == NULL) return(NULL);
3252 if (elem == NULL) return(NULL);
3253 if (name == NULL) return(NULL);
3254 if (value == NULL) return(NULL);
3255
3256 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3257 xmlChar qname[500];
3258 snprintf((char *) qname, sizeof(qname), "%s:%s",
3259 elem->ns->prefix, elem->name);
3260 qname[sizeof(qname) - 1] = 0;
3261 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3262 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3263 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3264 if (attrDecl != NULL)
3265 extsubset = 1;
3266 }
3267 }
3268 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3269 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3270 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3271 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3272 if (attrDecl != NULL)
3273 extsubset = 1;
3274 }
3275
3276 if (attrDecl == NULL)
3277 return(NULL);
3278 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3279 return(NULL);
3280
3281 ret = xmlStrdup(value);
3282 if (ret == NULL)
3283 return(NULL);
3284 src = value;
3285 dst = ret;
3286 while (*src == 0x20) src++;
3287 while (*src != 0) {
3288 if (*src == 0x20) {
3289 while (*src == 0x20) src++;
3290 if (*src != 0)
3291 *dst++ = 0x20;
3292 } else {
3293 *dst++ = *src++;
3294 }
3295 }
3296 *dst = 0;
3297 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3298 VERROR(ctxt->userData,
3299"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3300 name, elem->name);
3301 ctxt->valid = 0;
3302 }
3303 return(ret);
3304}
3305
3306/**
Owen Taylor3473f882001-02-23 17:55:21 +00003307 * xmlValidNormalizeAttributeValue:
3308 * @doc: the document
3309 * @elem: the parent
3310 * @name: the attribute name
3311 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003312 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003313 *
3314 * Does the validation related extra step of the normalization of attribute
3315 * values:
3316 *
3317 * If the declared value is not CDATA, then the XML processor must further
3318 * process the normalized attribute value by discarding any leading and
3319 * trailing space (#x20) characters, and by replacing sequences of space
3320 * (#x20) characters by single space (#x20) character.
3321 *
3322 * returns a new normalized string if normalization is needed, NULL otherwise
3323 * the caller must free the returned value.
3324 */
3325
3326xmlChar *
3327xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3328 const xmlChar *name, const xmlChar *value) {
3329 xmlChar *ret, *dst;
3330 const xmlChar *src;
3331 xmlAttributePtr attrDecl = NULL;
3332
3333 if (doc == NULL) return(NULL);
3334 if (elem == NULL) return(NULL);
3335 if (name == NULL) return(NULL);
3336 if (value == NULL) return(NULL);
3337
3338 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3339 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003340 snprintf((char *) qname, sizeof(qname), "%s:%s",
3341 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003342 qname[sizeof(qname) - 1] = 0;
3343 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3344 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3345 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3346 }
3347 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3348 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3349 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3350
3351 if (attrDecl == NULL)
3352 return(NULL);
3353 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3354 return(NULL);
3355
3356 ret = xmlStrdup(value);
3357 if (ret == NULL)
3358 return(NULL);
3359 src = value;
3360 dst = ret;
3361 while (*src == 0x20) src++;
3362 while (*src != 0) {
3363 if (*src == 0x20) {
3364 while (*src == 0x20) src++;
3365 if (*src != 0)
3366 *dst++ = 0x20;
3367 } else {
3368 *dst++ = *src++;
3369 }
3370 }
3371 *dst = 0;
3372 return(ret);
3373}
3374
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003375static void
Owen Taylor3473f882001-02-23 17:55:21 +00003376xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003377 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003378 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3379}
3380
3381/**
3382 * xmlValidateAttributeDecl:
3383 * @ctxt: the validation context
3384 * @doc: a document instance
3385 * @attr: an attribute definition
3386 *
3387 * Try to validate a single attribute definition
3388 * basically it does the following checks as described by the
3389 * XML-1.0 recommendation:
3390 * - [ VC: Attribute Default Legal ]
3391 * - [ VC: Enumeration ]
3392 * - [ VC: ID Attribute Default ]
3393 *
3394 * The ID/IDREF uniqueness and matching are done separately
3395 *
3396 * returns 1 if valid or 0 otherwise
3397 */
3398
3399int
3400xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3401 xmlAttributePtr attr) {
3402 int ret = 1;
3403 int val;
3404 CHECK_DTD;
3405 if(attr == NULL) return(1);
3406
3407 /* Attribute Default Legal */
3408 /* Enumeration */
3409 if (attr->defaultValue != NULL) {
3410 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3411 if (val == 0) {
3412 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003413 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003414 attr->name, attr->elem);
3415 }
3416 ret &= val;
3417 }
3418
3419 /* ID Attribute Default */
3420 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3421 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3422 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3423 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003424 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003425 attr->name, attr->elem);
3426 ret = 0;
3427 }
3428
3429 /* One ID per Element Type */
3430 if (attr->atype == XML_ATTRIBUTE_ID) {
3431 int nbId;
3432
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003433 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003434 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3435 attr->elem);
3436 if (elem != NULL) {
3437 nbId = xmlScanIDAttributeDecl(NULL, elem);
3438 } else {
3439 xmlAttributeTablePtr table;
3440
3441 /*
3442 * The attribute may be declared in the internal subset and the
3443 * element in the external subset.
3444 */
3445 nbId = 0;
3446 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3447 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3448 xmlValidateAttributeIdCallback, &nbId);
3449 }
3450 if (nbId > 1) {
3451 VERROR(ctxt->userData,
3452 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3453 attr->elem, nbId, attr->name);
3454 } else if (doc->extSubset != NULL) {
3455 int extId = 0;
3456 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3457 if (elem != NULL) {
3458 extId = xmlScanIDAttributeDecl(NULL, elem);
3459 }
3460 if (extId > 1) {
3461 VERROR(ctxt->userData,
3462 "Element %s has %d ID attribute defined in the external subset : %s\n",
3463 attr->elem, extId, attr->name);
3464 } else if (extId + nbId > 1) {
3465 VERROR(ctxt->userData,
3466"Element %s has ID attributes defined in the internal and external subset : %s\n",
3467 attr->elem, attr->name);
3468 }
3469 }
3470 }
3471
3472 /* Validity Constraint: Enumeration */
3473 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3474 xmlEnumerationPtr tree = attr->tree;
3475 while (tree != NULL) {
3476 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3477 tree = tree->next;
3478 }
3479 if (tree == NULL) {
3480 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003481"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003482 attr->defaultValue, attr->name, attr->elem);
3483 ret = 0;
3484 }
3485 }
3486
3487 return(ret);
3488}
3489
3490/**
3491 * xmlValidateElementDecl:
3492 * @ctxt: the validation context
3493 * @doc: a document instance
3494 * @elem: an element definition
3495 *
3496 * Try to validate a single element definition
3497 * basically it does the following checks as described by the
3498 * XML-1.0 recommendation:
3499 * - [ VC: One ID per Element Type ]
3500 * - [ VC: No Duplicate Types ]
3501 * - [ VC: Unique Element Type Declaration ]
3502 *
3503 * returns 1 if valid or 0 otherwise
3504 */
3505
3506int
3507xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3508 xmlElementPtr elem) {
3509 int ret = 1;
3510 xmlElementPtr tst;
3511
3512 CHECK_DTD;
3513
3514 if (elem == NULL) return(1);
3515
3516 /* No Duplicate Types */
3517 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3518 xmlElementContentPtr cur, next;
3519 const xmlChar *name;
3520
3521 cur = elem->content;
3522 while (cur != NULL) {
3523 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3524 if (cur->c1 == NULL) break;
3525 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3526 name = cur->c1->name;
3527 next = cur->c2;
3528 while (next != NULL) {
3529 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3530 if (xmlStrEqual(next->name, name)) {
3531 VERROR(ctxt->userData,
3532 "Definition of %s has duplicate references of %s\n",
3533 elem->name, name);
3534 ret = 0;
3535 }
3536 break;
3537 }
3538 if (next->c1 == NULL) break;
3539 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3540 if (xmlStrEqual(next->c1->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 next = next->c2;
3547 }
3548 }
3549 cur = cur->c2;
3550 }
3551 }
3552
3553 /* VC: Unique Element Type Declaration */
3554 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003555 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003556 ((tst->prefix == elem->prefix) ||
3557 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003558 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003559 VERROR(ctxt->userData, "Redefinition of element %s\n",
3560 elem->name);
3561 ret = 0;
3562 }
3563 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003564 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003565 ((tst->prefix == elem->prefix) ||
3566 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003567 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003568 VERROR(ctxt->userData, "Redefinition of element %s\n",
3569 elem->name);
3570 ret = 0;
3571 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003572 /* One ID per Element Type
3573 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003574 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3575 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003576 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003577 return(ret);
3578}
3579
3580/**
3581 * xmlValidateOneAttribute:
3582 * @ctxt: the validation context
3583 * @doc: a document instance
3584 * @elem: an element instance
3585 * @attr: an attribute instance
3586 * @value: the attribute value (without entities processing)
3587 *
3588 * Try to validate a single attribute for an element
3589 * basically it does the following checks as described by the
3590 * XML-1.0 recommendation:
3591 * - [ VC: Attribute Value Type ]
3592 * - [ VC: Fixed Attribute Default ]
3593 * - [ VC: Entity Name ]
3594 * - [ VC: Name Token ]
3595 * - [ VC: ID ]
3596 * - [ VC: IDREF ]
3597 * - [ VC: Entity Name ]
3598 * - [ VC: Notation Attributes ]
3599 *
3600 * The ID/IDREF uniqueness and matching are done separately
3601 *
3602 * returns 1 if valid or 0 otherwise
3603 */
3604
3605int
3606xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3607 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3608 /* xmlElementPtr elemDecl; */
3609 xmlAttributePtr attrDecl = NULL;
3610 int val;
3611 int ret = 1;
3612
3613 CHECK_DTD;
3614 if ((elem == NULL) || (elem->name == NULL)) return(0);
3615 if ((attr == NULL) || (attr->name == NULL)) return(0);
3616
3617 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3618 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003619 snprintf((char *) qname, sizeof(qname), "%s:%s",
3620 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003621 qname[sizeof(qname) - 1] = 0;
3622 if (attr->ns != NULL) {
3623 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3624 attr->name, attr->ns->prefix);
3625 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3626 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3627 attr->name, attr->ns->prefix);
3628 } else {
3629 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3630 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3631 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3632 qname, attr->name);
3633 }
3634 }
3635 if (attrDecl == NULL) {
3636 if (attr->ns != NULL) {
3637 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3638 attr->name, attr->ns->prefix);
3639 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3640 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3641 attr->name, attr->ns->prefix);
3642 } else {
3643 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3644 elem->name, attr->name);
3645 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3646 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3647 elem->name, attr->name);
3648 }
3649 }
3650
3651
3652 /* Validity Constraint: Attribute Value Type */
3653 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003654 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003655 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003656 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003657 attr->name, elem->name);
3658 return(0);
3659 }
3660 attr->atype = attrDecl->atype;
3661
3662 val = xmlValidateAttributeValue(attrDecl->atype, value);
3663 if (val == 0) {
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 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003667 attr->name, elem->name);
3668 ret = 0;
3669 }
3670
3671 /* Validity constraint: Fixed Attribute Default */
3672 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3673 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
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 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003677 attr->name, elem->name, attrDecl->defaultValue);
3678 ret = 0;
3679 }
3680 }
3681
3682 /* Validity Constraint: ID uniqueness */
3683 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3684 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3685 ret = 0;
3686 }
3687
3688 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3689 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3690 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3691 ret = 0;
3692 }
3693
3694 /* Validity Constraint: Notation Attributes */
3695 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3696 xmlEnumerationPtr tree = attrDecl->tree;
3697 xmlNotationPtr nota;
3698
3699 /* First check that the given NOTATION was declared */
3700 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3701 if (nota == NULL)
3702 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3703
3704 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003705 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003706 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003707 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003708 value, attr->name, elem->name);
3709 ret = 0;
3710 }
3711
3712 /* Second, verify that it's among the list */
3713 while (tree != NULL) {
3714 if (xmlStrEqual(tree->name, value)) break;
3715 tree = tree->next;
3716 }
3717 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003718 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003719 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003720"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003721 value, attr->name, elem->name);
3722 ret = 0;
3723 }
3724 }
3725
3726 /* Validity Constraint: Enumeration */
3727 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3728 xmlEnumerationPtr tree = attrDecl->tree;
3729 while (tree != NULL) {
3730 if (xmlStrEqual(tree->name, value)) break;
3731 tree = tree->next;
3732 }
3733 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003734 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003735 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003736 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003737 value, attr->name, elem->name);
3738 ret = 0;
3739 }
3740 }
3741
3742 /* Fixed Attribute Default */
3743 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3744 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003745 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003746 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003747 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003748 attr->name, elem->name, attrDecl->defaultValue);
3749 ret = 0;
3750 }
3751
3752 /* Extra check for the attribute value */
3753 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3754 attrDecl->atype, value);
3755
3756 return(ret);
3757}
3758
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003759/**
3760 * xmlValidateSkipIgnorable:
3761 * @ctxt: the validation context
3762 * @child: the child list
3763 *
3764 * Skip ignorable elements w.r.t. the validation process
3765 *
3766 * returns the first element to consider for validation of the content model
3767 */
3768
3769static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003770xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003771 while (child != NULL) {
3772 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003773 /* These things are ignored (skipped) during validation. */
3774 case XML_PI_NODE:
3775 case XML_COMMENT_NODE:
3776 case XML_XINCLUDE_START:
3777 case XML_XINCLUDE_END:
3778 child = child->next;
3779 break;
3780 case XML_TEXT_NODE:
3781 if (xmlIsBlankNode(child))
3782 child = child->next;
3783 else
3784 return(child);
3785 break;
3786 /* keep current node */
3787 default:
3788 return(child);
3789 }
3790 }
3791 return(child);
3792}
3793
3794/**
3795 * xmlValidateElementType:
3796 * @ctxt: the validation context
3797 *
3798 * Try to validate the content model of an element internal function
3799 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003800 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3801 * reference is found and -3 if the validation succeeded but
3802 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003803 */
3804
3805static int
3806xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003807 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003808 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003809
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003810 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003811 if ((NODE == NULL) && (CONT == NULL))
3812 return(1);
3813 if ((NODE == NULL) &&
3814 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3815 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3816 return(1);
3817 }
3818 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003819 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003820 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003821
3822 /*
3823 * We arrive here when more states need to be examined
3824 */
3825cont:
3826
3827 /*
3828 * We just recovered from a rollback generated by a possible
3829 * epsilon transition, go directly to the analysis phase
3830 */
3831 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003832 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003833 DEBUG_VALID_STATE(NODE, CONT)
3834 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003835 goto analyze;
3836 }
3837
3838 DEBUG_VALID_STATE(NODE, CONT)
3839 /*
3840 * we may have to save a backup state here. This is the equivalent
3841 * of handling epsilon transition in NFAs.
3842 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003843 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003844 ((CONT->parent == NULL) ||
3845 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003846 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003847 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003848 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003849 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003850 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3851 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003852 }
3853
3854
3855 /*
3856 * Check first if the content matches
3857 */
3858 switch (CONT->type) {
3859 case XML_ELEMENT_CONTENT_PCDATA:
3860 if (NODE == NULL) {
3861 DEBUG_VALID_MSG("pcdata failed no node");
3862 ret = 0;
3863 break;
3864 }
3865 if (NODE->type == XML_TEXT_NODE) {
3866 DEBUG_VALID_MSG("pcdata found, skip to next");
3867 /*
3868 * go to next element in the content model
3869 * skipping ignorable elems
3870 */
3871 do {
3872 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003873 NODE = xmlValidateSkipIgnorable(NODE);
3874 if ((NODE != NULL) &&
3875 (NODE->type == XML_ENTITY_REF_NODE))
3876 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003877 } while ((NODE != NULL) &&
3878 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003879 (NODE->type != XML_TEXT_NODE) &&
3880 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003881 ret = 1;
3882 break;
3883 } else {
3884 DEBUG_VALID_MSG("pcdata failed");
3885 ret = 0;
3886 break;
3887 }
3888 break;
3889 case XML_ELEMENT_CONTENT_ELEMENT:
3890 if (NODE == NULL) {
3891 DEBUG_VALID_MSG("element failed no node");
3892 ret = 0;
3893 break;
3894 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003895 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3896 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003897 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003898 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3899 ret = (CONT->prefix == NULL);
3900 } else if (CONT->prefix == NULL) {
3901 ret = 0;
3902 } else {
3903 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3904 }
3905 }
3906 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003907 DEBUG_VALID_MSG("element found, skip to next");
3908 /*
3909 * go to next element in the content model
3910 * skipping ignorable elems
3911 */
3912 do {
3913 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003914 NODE = xmlValidateSkipIgnorable(NODE);
3915 if ((NODE != NULL) &&
3916 (NODE->type == XML_ENTITY_REF_NODE))
3917 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003918 } while ((NODE != NULL) &&
3919 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003920 (NODE->type != XML_TEXT_NODE) &&
3921 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003922 } else {
3923 DEBUG_VALID_MSG("element failed");
3924 ret = 0;
3925 break;
3926 }
3927 break;
3928 case XML_ELEMENT_CONTENT_OR:
3929 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003930 * Small optimization.
3931 */
3932 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3933 if ((NODE == NULL) ||
3934 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3935 DEPTH++;
3936 CONT = CONT->c2;
3937 goto cont;
3938 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003939 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3940 ret = (CONT->c1->prefix == NULL);
3941 } else if (CONT->c1->prefix == NULL) {
3942 ret = 0;
3943 } else {
3944 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3945 }
3946 if (ret == 0) {
3947 DEPTH++;
3948 CONT = CONT->c2;
3949 goto cont;
3950 }
Daniel Veillard85349052001-04-20 13:48:21 +00003951 }
3952
3953 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003954 * save the second branch 'or' branch
3955 */
3956 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003957 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3958 OCCURS, ROLLBACK_OR) < 0)
3959 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003960 DEPTH++;
3961 CONT = CONT->c1;
3962 goto cont;
3963 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003964 /*
3965 * Small optimization.
3966 */
3967 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3968 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3969 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3970 if ((NODE == NULL) ||
3971 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3972 DEPTH++;
3973 CONT = CONT->c2;
3974 goto cont;
3975 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003976 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3977 ret = (CONT->c1->prefix == NULL);
3978 } else if (CONT->c1->prefix == NULL) {
3979 ret = 0;
3980 } else {
3981 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3982 }
3983 if (ret == 0) {
3984 DEPTH++;
3985 CONT = CONT->c2;
3986 goto cont;
3987 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003988 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003989 DEPTH++;
3990 CONT = CONT->c1;
3991 goto cont;
3992 }
3993
3994 /*
3995 * At this point handle going up in the tree
3996 */
3997 if (ret == -1) {
3998 DEBUG_VALID_MSG("error found returning");
3999 return(ret);
4000 }
4001analyze:
4002 while (CONT != NULL) {
4003 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004004 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004005 * this level.
4006 */
4007 if (ret == 0) {
4008 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004009 xmlNodePtr cur;
4010
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004011 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004012 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004013 DEBUG_VALID_MSG("Once branch failed, rollback");
4014 if (vstateVPop(ctxt) < 0 ) {
4015 DEBUG_VALID_MSG("exhaustion, failed");
4016 return(0);
4017 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004018 if (cur != ctxt->vstate->node)
4019 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004020 goto cont;
4021 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004022 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004023 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004024 DEBUG_VALID_MSG("Plus branch failed, rollback");
4025 if (vstateVPop(ctxt) < 0 ) {
4026 DEBUG_VALID_MSG("exhaustion, failed");
4027 return(0);
4028 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004029 if (cur != ctxt->vstate->node)
4030 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004031 goto cont;
4032 }
4033 DEBUG_VALID_MSG("Plus branch found");
4034 ret = 1;
4035 break;
4036 case XML_ELEMENT_CONTENT_MULT:
4037#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004038 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004039 DEBUG_VALID_MSG("Mult branch failed");
4040 } else {
4041 DEBUG_VALID_MSG("Mult branch found");
4042 }
4043#endif
4044 ret = 1;
4045 break;
4046 case XML_ELEMENT_CONTENT_OPT:
4047 DEBUG_VALID_MSG("Option branch failed");
4048 ret = 1;
4049 break;
4050 }
4051 } else {
4052 switch (CONT->ocur) {
4053 case XML_ELEMENT_CONTENT_OPT:
4054 DEBUG_VALID_MSG("Option branch succeeded");
4055 ret = 1;
4056 break;
4057 case XML_ELEMENT_CONTENT_ONCE:
4058 DEBUG_VALID_MSG("Once branch succeeded");
4059 ret = 1;
4060 break;
4061 case XML_ELEMENT_CONTENT_PLUS:
4062 if (STATE == ROLLBACK_PARENT) {
4063 DEBUG_VALID_MSG("Plus branch rollback");
4064 ret = 1;
4065 break;
4066 }
4067 if (NODE == NULL) {
4068 DEBUG_VALID_MSG("Plus branch exhausted");
4069 ret = 1;
4070 break;
4071 }
4072 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004073 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004074 goto cont;
4075 case XML_ELEMENT_CONTENT_MULT:
4076 if (STATE == ROLLBACK_PARENT) {
4077 DEBUG_VALID_MSG("Mult branch rollback");
4078 ret = 1;
4079 break;
4080 }
4081 if (NODE == NULL) {
4082 DEBUG_VALID_MSG("Mult branch exhausted");
4083 ret = 1;
4084 break;
4085 }
4086 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004087 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004088 goto cont;
4089 }
4090 }
4091 STATE = 0;
4092
4093 /*
4094 * Then act accordingly at the parent level
4095 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004096 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004097 if (CONT->parent == NULL)
4098 break;
4099
4100 switch (CONT->parent->type) {
4101 case XML_ELEMENT_CONTENT_PCDATA:
4102 DEBUG_VALID_MSG("Error: parent pcdata");
4103 return(-1);
4104 case XML_ELEMENT_CONTENT_ELEMENT:
4105 DEBUG_VALID_MSG("Error: parent element");
4106 return(-1);
4107 case XML_ELEMENT_CONTENT_OR:
4108 if (ret == 1) {
4109 DEBUG_VALID_MSG("Or succeeded");
4110 CONT = CONT->parent;
4111 DEPTH--;
4112 } else {
4113 DEBUG_VALID_MSG("Or failed");
4114 CONT = CONT->parent;
4115 DEPTH--;
4116 }
4117 break;
4118 case XML_ELEMENT_CONTENT_SEQ:
4119 if (ret == 0) {
4120 DEBUG_VALID_MSG("Sequence failed");
4121 CONT = CONT->parent;
4122 DEPTH--;
4123 } else if (CONT == CONT->parent->c1) {
4124 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4125 CONT = CONT->parent->c2;
4126 goto cont;
4127 } else {
4128 DEBUG_VALID_MSG("Sequence succeeded");
4129 CONT = CONT->parent;
4130 DEPTH--;
4131 }
4132 }
4133 }
4134 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004135 xmlNodePtr cur;
4136
4137 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004138 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4139 if (vstateVPop(ctxt) < 0 ) {
4140 DEBUG_VALID_MSG("exhaustion, failed");
4141 return(0);
4142 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004143 if (cur != ctxt->vstate->node)
4144 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004145 goto cont;
4146 }
4147 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004148 xmlNodePtr cur;
4149
4150 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004151 DEBUG_VALID_MSG("Failure, rollback");
4152 if (vstateVPop(ctxt) < 0 ) {
4153 DEBUG_VALID_MSG("exhaustion, failed");
4154 return(0);
4155 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004156 if (cur != ctxt->vstate->node)
4157 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004158 goto cont;
4159 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004160 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004161}
4162
4163/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004164 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004165 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004166 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004167 * @content: An element
4168 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4169 *
4170 * This will dump the list of elements to the buffer
4171 * Intended just for the debug routine
4172 */
4173static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004174xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004175 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004176 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004177
4178 if (node == NULL) return;
4179 if (glob) strcat(buf, "(");
4180 cur = node;
4181 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004182 len = strlen(buf);
4183 if (size - len < 50) {
4184 if ((size - len > 4) && (buf[len - 1] != '.'))
4185 strcat(buf, " ...");
4186 return;
4187 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004188 switch (cur->type) {
4189 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004190 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004191 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004192 if ((size - len > 4) && (buf[len - 1] != '.'))
4193 strcat(buf, " ...");
4194 return;
4195 }
4196 strcat(buf, (char *) cur->ns->prefix);
4197 strcat(buf, ":");
4198 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004199 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004200 if ((size - len > 4) && (buf[len - 1] != '.'))
4201 strcat(buf, " ...");
4202 return;
4203 }
4204 strcat(buf, (char *) cur->name);
4205 if (cur->next != NULL)
4206 strcat(buf, " ");
4207 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004208 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004209 if (xmlIsBlankNode(cur))
4210 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004211 case XML_CDATA_SECTION_NODE:
4212 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004213 strcat(buf, "CDATA");
4214 if (cur->next != NULL)
4215 strcat(buf, " ");
4216 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004217 case XML_ATTRIBUTE_NODE:
4218 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004219#ifdef LIBXML_DOCB_ENABLED
4220 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004221#endif
4222 case XML_HTML_DOCUMENT_NODE:
4223 case XML_DOCUMENT_TYPE_NODE:
4224 case XML_DOCUMENT_FRAG_NODE:
4225 case XML_NOTATION_NODE:
4226 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004227 strcat(buf, "???");
4228 if (cur->next != NULL)
4229 strcat(buf, " ");
4230 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004231 case XML_ENTITY_NODE:
4232 case XML_PI_NODE:
4233 case XML_DTD_NODE:
4234 case XML_COMMENT_NODE:
4235 case XML_ELEMENT_DECL:
4236 case XML_ATTRIBUTE_DECL:
4237 case XML_ENTITY_DECL:
4238 case XML_XINCLUDE_START:
4239 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004240 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004241 }
4242 cur = cur->next;
4243 }
4244 if (glob) strcat(buf, ")");
4245}
4246
4247/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004248 * xmlValidateElementContent:
4249 * @ctxt: the validation context
4250 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004251 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004252 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004253 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004254 *
4255 * Try to validate the content model of an element
4256 *
4257 * returns 1 if valid or 0 if not and -1 in case of error
4258 */
4259
4260static int
4261xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004262 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004263 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004264 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004265 xmlElementContentPtr cont;
4266 const xmlChar *name;
4267
4268 if (elemDecl == NULL)
4269 return(-1);
4270 cont = elemDecl->content;
4271 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004272
4273 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004274 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004275 */
4276 ctxt->vstateMax = 8;
4277 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4278 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4279 if (ctxt->vstateTab == NULL) {
4280 xmlGenericError(xmlGenericErrorContext,
4281 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004282 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004283 }
4284 /*
4285 * The first entry in the stack is reserved to the current state
4286 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004287 ctxt->nodeMax = 0;
4288 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004289 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004290 ctxt->vstate = &ctxt->vstateTab[0];
4291 ctxt->vstateNr = 1;
4292 CONT = cont;
4293 NODE = child;
4294 DEPTH = 0;
4295 OCCURS = 0;
4296 STATE = 0;
4297 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004298 if ((ret == -3) && (warn)) {
4299 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004300 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004301 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004302 /*
4303 * An entities reference appeared at this level.
4304 * Buid a minimal representation of this node content
4305 * sufficient to run the validation process on it
4306 */
4307 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004308 cur = child;
4309 while (cur != NULL) {
4310 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004311 case XML_ENTITY_REF_NODE:
4312 /*
4313 * Push the current node to be able to roll back
4314 * and process within the entity
4315 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004316 if ((cur->children != NULL) &&
4317 (cur->children->children != NULL)) {
4318 nodeVPush(ctxt, cur);
4319 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004320 continue;
4321 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004322 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004323 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004324 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004325 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004326 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004327 case XML_CDATA_SECTION_NODE:
4328 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004329 case XML_ELEMENT_NODE:
4330 /*
4331 * Allocate a new node and minimally fills in
4332 * what's required
4333 */
4334 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4335 if (tmp == NULL) {
4336 xmlGenericError(xmlGenericErrorContext,
4337 "xmlValidateElementContent : malloc failed\n");
4338 xmlFreeNodeList(repl);
4339 ret = -1;
4340 goto done;
4341 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004342 tmp->type = cur->type;
4343 tmp->name = cur->name;
4344 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004345 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004346 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004347 if (repl == NULL)
4348 repl = last = tmp;
4349 else {
4350 last->next = tmp;
4351 last = tmp;
4352 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004353 if (cur->type == XML_CDATA_SECTION_NODE) {
4354 /*
4355 * E59 spaces in CDATA does not match the
4356 * nonterminal S
4357 */
4358 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4359 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004360 break;
4361 default:
4362 break;
4363 }
4364 /*
4365 * Switch to next element
4366 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004367 cur = cur->next;
4368 while (cur == NULL) {
4369 cur = nodeVPop(ctxt);
4370 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004371 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004372 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004373 }
4374 }
4375
4376 /*
4377 * Relaunch the validation
4378 */
4379 ctxt->vstate = &ctxt->vstateTab[0];
4380 ctxt->vstateNr = 1;
4381 CONT = cont;
4382 NODE = repl;
4383 DEPTH = 0;
4384 OCCURS = 0;
4385 STATE = 0;
4386 ret = xmlValidateElementType(ctxt);
4387 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004388 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004389 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4390 char expr[5000];
4391 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004392
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004393 expr[0] = 0;
4394 xmlSnprintfElementContent(expr, 5000, cont, 1);
4395 list[0] = 0;
4396 if (repl != NULL)
4397 xmlSnprintfElements(list, 5000, repl, 1);
4398 else
4399 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004400
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004401 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004402 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004403 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004404 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004405 name, expr, list);
4406 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004407 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004408 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004409 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004410 expr, list);
4411 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004412 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004413 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004414 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004415 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004416 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004417 name);
4418 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004419 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004420 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004421 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004422 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004423 }
4424 ret = 0;
4425 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004426 if (ret == -3)
4427 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004428
4429
4430done:
4431 /*
4432 * Deallocate the copy if done, and free up the validation stack
4433 */
4434 while (repl != NULL) {
4435 tmp = repl->next;
4436 xmlFree(repl);
4437 repl = tmp;
4438 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004439 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004440 if (ctxt->vstateTab != NULL) {
4441 xmlFree(ctxt->vstateTab);
4442 ctxt->vstateTab = NULL;
4443 }
4444 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004445 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004446 if (ctxt->nodeTab != NULL) {
4447 xmlFree(ctxt->nodeTab);
4448 ctxt->nodeTab = NULL;
4449 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004450 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004451
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004452}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004453
Owen Taylor3473f882001-02-23 17:55:21 +00004454/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004455 * xmlValidateCdataElement:
4456 * @ctxt: the validation context
4457 * @doc: a document instance
4458 * @elem: an element instance
4459 *
4460 * Check that an element follows #CDATA
4461 *
4462 * returns 1 if valid or 0 otherwise
4463 */
4464static int
4465xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4466 xmlNodePtr elem) {
4467 int ret = 1;
4468 xmlNodePtr cur, child;
4469
4470 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4471 return(0);
4472
4473 child = elem->children;
4474
4475 cur = child;
4476 while (cur != NULL) {
4477 switch (cur->type) {
4478 case XML_ENTITY_REF_NODE:
4479 /*
4480 * Push the current node to be able to roll back
4481 * and process within the entity
4482 */
4483 if ((cur->children != NULL) &&
4484 (cur->children->children != NULL)) {
4485 nodeVPush(ctxt, cur);
4486 cur = cur->children->children;
4487 continue;
4488 }
4489 break;
4490 case XML_COMMENT_NODE:
4491 case XML_PI_NODE:
4492 case XML_TEXT_NODE:
4493 case XML_CDATA_SECTION_NODE:
4494 break;
4495 default:
4496 ret = 0;
4497 goto done;
4498 }
4499 /*
4500 * Switch to next element
4501 */
4502 cur = cur->next;
4503 while (cur == NULL) {
4504 cur = nodeVPop(ctxt);
4505 if (cur == NULL)
4506 break;
4507 cur = cur->next;
4508 }
4509 }
4510done:
4511 ctxt->nodeMax = 0;
4512 ctxt->nodeNr = 0;
4513 if (ctxt->nodeTab != NULL) {
4514 xmlFree(ctxt->nodeTab);
4515 ctxt->nodeTab = NULL;
4516 }
4517 return(ret);
4518}
4519
4520/**
Owen Taylor3473f882001-02-23 17:55:21 +00004521 * xmlValidateOneElement:
4522 * @ctxt: the validation context
4523 * @doc: a document instance
4524 * @elem: an element instance
4525 *
4526 * Try to validate a single element and it's attributes,
4527 * basically it does the following checks as described by the
4528 * XML-1.0 recommendation:
4529 * - [ VC: Element Valid ]
4530 * - [ VC: Required Attribute ]
4531 * Then call xmlValidateOneAttribute() for each attribute present.
4532 *
4533 * The ID/IDREF checkings are done separately
4534 *
4535 * returns 1 if valid or 0 otherwise
4536 */
4537
4538int
4539xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4540 xmlNodePtr elem) {
4541 xmlElementPtr elemDecl = NULL;
4542 xmlElementContentPtr cont;
4543 xmlAttributePtr attr;
4544 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004545 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004546 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004547 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004548 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004549
4550 CHECK_DTD;
4551
4552 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004553 switch (elem->type) {
4554 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004555 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004556 VERROR(ctxt->userData,
4557 "Attribute element not expected here\n");
4558 return(0);
4559 case XML_TEXT_NODE:
4560 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004561 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004562 VERROR(ctxt->userData, "Text element has childs !\n");
4563 return(0);
4564 }
4565 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004566 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004567 VERROR(ctxt->userData, "Text element has attributes !\n");
4568 return(0);
4569 }
4570 if (elem->ns != 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 namespace !\n");
4573 return(0);
4574 }
4575 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004576 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004577 VERROR(ctxt->userData,
4578 "Text element carries namespace definitions !\n");
4579 return(0);
4580 }
4581 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004582 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004583 VERROR(ctxt->userData,
4584 "Text element has no content !\n");
4585 return(0);
4586 }
4587 return(1);
4588 case XML_XINCLUDE_START:
4589 case XML_XINCLUDE_END:
4590 return(1);
4591 case XML_CDATA_SECTION_NODE:
4592 case XML_ENTITY_REF_NODE:
4593 case XML_PI_NODE:
4594 case XML_COMMENT_NODE:
4595 return(1);
4596 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004597 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004598 VERROR(ctxt->userData,
4599 "Entity element not expected here\n");
4600 return(0);
4601 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004602 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004603 VERROR(ctxt->userData,
4604 "Notation element not expected here\n");
4605 return(0);
4606 case XML_DOCUMENT_NODE:
4607 case XML_DOCUMENT_TYPE_NODE:
4608 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004609 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004610 VERROR(ctxt->userData,
4611 "Document element not expected here\n");
4612 return(0);
4613 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004614 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004615 VERROR(ctxt->userData,
4616 "\n");
4617 return(0);
4618 case XML_ELEMENT_NODE:
4619 break;
4620 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004621 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004622 VERROR(ctxt->userData,
4623 "unknown element type %d\n", elem->type);
4624 return(0);
4625 }
4626 if (elem->name == NULL) return(0);
4627
4628 /*
4629 * Fetch the declaration for the qualified name
4630 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004631 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4632 prefix = elem->ns->prefix;
4633
4634 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004635 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004636 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004637 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004638 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004639 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004640 if (elemDecl != NULL)
4641 extsubset = 1;
4642 }
Owen Taylor3473f882001-02-23 17:55:21 +00004643 }
4644
4645 /*
4646 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004647 * This is "non-strict" validation should be done on the
4648 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004649 */
4650 if (elemDecl == NULL) {
4651 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004652 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004653 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004654 if (elemDecl != NULL)
4655 extsubset = 1;
4656 }
Owen Taylor3473f882001-02-23 17:55:21 +00004657 }
4658 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004659 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004660 VERROR(ctxt->userData, "No declaration for element %s\n",
4661 elem->name);
4662 return(0);
4663 }
4664
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004665 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004666 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004667 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004668 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004669 VERROR(ctxt->userData, "No declaration for element %s\n",
4670 elem->name);
4671 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004672 case XML_ELEMENT_TYPE_EMPTY:
4673 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004674 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004675 VERROR(ctxt->userData,
4676 "Element %s was declared EMPTY this one has content\n",
4677 elem->name);
4678 ret = 0;
4679 }
4680 break;
4681 case XML_ELEMENT_TYPE_ANY:
4682 /* I don't think anything is required then */
4683 break;
4684 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004685
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004686 /* simple case of declared as #PCDATA */
4687 if ((elemDecl->content != NULL) &&
4688 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4689 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4690 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004691 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004692 VERROR(ctxt->userData,
4693 "Element %s was declared #PCDATA but contains non text nodes\n",
4694 elem->name);
4695 }
4696 break;
4697 }
Owen Taylor3473f882001-02-23 17:55:21 +00004698 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004699 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004700 while (child != NULL) {
4701 if (child->type == XML_ELEMENT_NODE) {
4702 name = child->name;
4703 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4704 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004705 snprintf((char *) qname, sizeof(qname), "%s:%s",
4706 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004707 qname[sizeof(qname) - 1] = 0;
4708 cont = elemDecl->content;
4709 while (cont != NULL) {
4710 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4711 if (xmlStrEqual(cont->name, qname)) break;
4712 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4713 (cont->c1 != NULL) &&
4714 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4715 if (xmlStrEqual(cont->c1->name, qname)) break;
4716 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4717 (cont->c1 == NULL) ||
4718 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4719 /* Internal error !!! */
4720 xmlGenericError(xmlGenericErrorContext,
4721 "Internal: MIXED struct bad\n");
4722 break;
4723 }
4724 cont = cont->c2;
4725 }
4726 if (cont != NULL)
4727 goto child_ok;
4728 }
4729 cont = elemDecl->content;
4730 while (cont != NULL) {
4731 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4732 if (xmlStrEqual(cont->name, name)) break;
4733 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4734 (cont->c1 != NULL) &&
4735 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4736 if (xmlStrEqual(cont->c1->name, name)) break;
4737 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4738 (cont->c1 == NULL) ||
4739 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4740 /* Internal error !!! */
4741 xmlGenericError(xmlGenericErrorContext,
4742 "Internal: MIXED struct bad\n");
4743 break;
4744 }
4745 cont = cont->c2;
4746 }
4747 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004748 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004749 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004750 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004751 name, elem->name);
4752 ret = 0;
4753 }
4754 }
4755child_ok:
4756 child = child->next;
4757 }
4758 break;
4759 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004760 if ((doc->standalone == 1) && (extsubset == 1)) {
4761 /*
4762 * VC: Standalone Document Declaration
4763 * - element types with element content, if white space
4764 * occurs directly within any instance of those types.
4765 */
4766 child = elem->children;
4767 while (child != NULL) {
4768 if (child->type == XML_TEXT_NODE) {
4769 const xmlChar *content = child->content;
4770
4771 while (IS_BLANK(*content))
4772 content++;
4773 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004774 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004775 VERROR(ctxt->userData,
4776"standalone: %s declared in the external subset contains white spaces nodes\n",
4777 elem->name);
4778 ret = 0;
4779 break;
4780 }
4781 }
4782 child =child->next;
4783 }
4784 }
Owen Taylor3473f882001-02-23 17:55:21 +00004785 child = elem->children;
4786 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004787 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004788 if (tmp <= 0)
4789 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004790 break;
4791 }
4792
4793 /* [ VC: Required Attribute ] */
4794 attr = elemDecl->attributes;
4795 while (attr != NULL) {
4796 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004797 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004798
Daniel Veillarde4301c82002-02-13 13:32:35 +00004799 if ((attr->prefix == NULL) &&
4800 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4801 xmlNsPtr ns;
4802
4803 ns = elem->nsDef;
4804 while (ns != NULL) {
4805 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004806 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004807 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004808 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004809 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4810 xmlNsPtr ns;
4811
4812 ns = elem->nsDef;
4813 while (ns != NULL) {
4814 if (xmlStrEqual(attr->name, ns->prefix))
4815 goto found;
4816 ns = ns->next;
4817 }
4818 } else {
4819 xmlAttrPtr attrib;
4820
4821 attrib = elem->properties;
4822 while (attrib != NULL) {
4823 if (xmlStrEqual(attrib->name, attr->name)) {
4824 if (attr->prefix != NULL) {
4825 xmlNsPtr nameSpace = attrib->ns;
4826
4827 if (nameSpace == NULL)
4828 nameSpace = elem->ns;
4829 /*
4830 * qualified names handling is problematic, having a
4831 * different prefix should be possible but DTDs don't
4832 * allow to define the URI instead of the prefix :-(
4833 */
4834 if (nameSpace == NULL) {
4835 if (qualified < 0)
4836 qualified = 0;
4837 } else if (!xmlStrEqual(nameSpace->prefix,
4838 attr->prefix)) {
4839 if (qualified < 1)
4840 qualified = 1;
4841 } else
4842 goto found;
4843 } else {
4844 /*
4845 * We should allow applications to define namespaces
4846 * for their application even if the DTD doesn't
4847 * carry one, otherwise, basically we would always
4848 * break.
4849 */
4850 goto found;
4851 }
4852 }
4853 attrib = attrib->next;
4854 }
Owen Taylor3473f882001-02-23 17:55:21 +00004855 }
4856 if (qualified == -1) {
4857 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004858 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004859 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004860 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004861 elem->name, attr->name);
4862 ret = 0;
4863 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004864 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004865 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004866 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004867 elem->name, attr->prefix,attr->name);
4868 ret = 0;
4869 }
4870 } else if (qualified == 0) {
4871 VWARNING(ctxt->userData,
4872 "Element %s required attribute %s:%s has no prefix\n",
4873 elem->name, attr->prefix,attr->name);
4874 } else if (qualified == 1) {
4875 VWARNING(ctxt->userData,
4876 "Element %s required attribute %s:%s has different prefix\n",
4877 elem->name, attr->prefix,attr->name);
4878 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004879 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4880 /*
4881 * Special tests checking #FIXED namespace declarations
4882 * have the right value since this is not done as an
4883 * attribute checking
4884 */
4885 if ((attr->prefix == NULL) &&
4886 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4887 xmlNsPtr ns;
4888
4889 ns = elem->nsDef;
4890 while (ns != NULL) {
4891 if (ns->prefix == NULL) {
4892 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004893 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00004894 VERROR(ctxt->userData,
4895 "Element %s namespace name for default namespace does not match the DTD\n",
4896 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004897 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004898 }
4899 goto found;
4900 }
4901 ns = ns->next;
4902 }
4903 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4904 xmlNsPtr ns;
4905
4906 ns = elem->nsDef;
4907 while (ns != NULL) {
4908 if (xmlStrEqual(attr->name, ns->prefix)) {
4909 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004910 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00004911 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004912 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00004913 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004914 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004915 }
4916 goto found;
4917 }
4918 ns = ns->next;
4919 }
4920 }
Owen Taylor3473f882001-02-23 17:55:21 +00004921 }
4922found:
4923 attr = attr->nexth;
4924 }
4925 return(ret);
4926}
4927
4928/**
4929 * xmlValidateRoot:
4930 * @ctxt: the validation context
4931 * @doc: a document instance
4932 *
4933 * Try to validate a the root element
4934 * basically it does the following check as described by the
4935 * XML-1.0 recommendation:
4936 * - [ VC: Root Element Type ]
4937 * it doesn't try to recurse or apply other check to the element
4938 *
4939 * returns 1 if valid or 0 otherwise
4940 */
4941
4942int
4943xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4944 xmlNodePtr root;
4945 if (doc == NULL) return(0);
4946
4947 root = xmlDocGetRootElement(doc);
4948 if ((root == NULL) || (root->name == NULL)) {
4949 VERROR(ctxt->userData, "Not valid: no root element\n");
4950 return(0);
4951 }
4952
4953 /*
4954 * When doing post validation against a separate DTD, those may
4955 * no internal subset has been generated
4956 */
4957 if ((doc->intSubset != NULL) &&
4958 (doc->intSubset->name != NULL)) {
4959 /*
4960 * Check first the document root against the NQName
4961 */
4962 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4963 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4964 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004965 snprintf((char *) qname, sizeof(qname), "%s:%s",
4966 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004967 qname[sizeof(qname) - 1] = 0;
4968 if (xmlStrEqual(doc->intSubset->name, qname))
4969 goto name_ok;
4970 }
4971 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4972 (xmlStrEqual(root->name, BAD_CAST "html")))
4973 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00004974 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00004975 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004976 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004977 root->name, doc->intSubset->name);
4978 return(0);
4979
4980 }
4981 }
4982name_ok:
4983 return(1);
4984}
4985
4986
4987/**
4988 * xmlValidateElement:
4989 * @ctxt: the validation context
4990 * @doc: a document instance
4991 * @elem: an element instance
4992 *
4993 * Try to validate the subtree under an element
4994 *
4995 * returns 1 if valid or 0 otherwise
4996 */
4997
4998int
4999xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5000 xmlNodePtr child;
5001 xmlAttrPtr attr;
5002 xmlChar *value;
5003 int ret = 1;
5004
5005 if (elem == NULL) return(0);
5006
5007 /*
5008 * XInclude elements were added after parsing in the infoset,
5009 * they don't really mean anything validation wise.
5010 */
5011 if ((elem->type == XML_XINCLUDE_START) ||
5012 (elem->type == XML_XINCLUDE_END))
5013 return(1);
5014
5015 CHECK_DTD;
5016
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005017 /*
5018 * Entities references have to be handled separately
5019 */
5020 if (elem->type == XML_ENTITY_REF_NODE) {
5021 return(1);
5022 }
5023
Owen Taylor3473f882001-02-23 17:55:21 +00005024 ret &= xmlValidateOneElement(ctxt, doc, elem);
5025 attr = elem->properties;
5026 while(attr != NULL) {
5027 value = xmlNodeListGetString(doc, attr->children, 0);
5028 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5029 if (value != NULL)
5030 xmlFree(value);
5031 attr= attr->next;
5032 }
5033 child = elem->children;
5034 while (child != NULL) {
5035 ret &= xmlValidateElement(ctxt, doc, child);
5036 child = child->next;
5037 }
5038
5039 return(ret);
5040}
5041
Daniel Veillard8730c562001-02-26 10:49:57 +00005042/**
5043 * xmlValidateRef:
5044 * @ref: A reference to be validated
5045 * @ctxt: Validation context
5046 * @name: Name of ID we are searching for
5047 *
5048 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005049static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005050xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005051 const xmlChar *name) {
5052 xmlAttrPtr id;
5053 xmlAttrPtr attr;
5054
5055 if (ref == NULL)
5056 return;
5057 attr = ref->attr;
5058 if (attr == NULL)
5059 return;
5060 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5061 id = xmlGetID(ctxt->doc, name);
5062 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005063 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005064 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005065 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005066 attr->name, name);
5067 ctxt->valid = 0;
5068 }
5069 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5070 xmlChar *dup, *str = NULL, *cur, save;
5071
5072 dup = xmlStrdup(name);
5073 if (dup == NULL) {
5074 ctxt->valid = 0;
5075 return;
5076 }
5077 cur = dup;
5078 while (*cur != 0) {
5079 str = cur;
5080 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5081 save = *cur;
5082 *cur = 0;
5083 id = xmlGetID(ctxt->doc, str);
5084 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005085 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005086 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005087 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005088 attr->name, str);
5089 ctxt->valid = 0;
5090 }
5091 if (save == 0)
5092 break;
5093 *cur = save;
5094 while (IS_BLANK(*cur)) cur++;
5095 }
5096 xmlFree(dup);
5097 }
5098}
5099
5100/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005101 * xmlWalkValidateList:
5102 * @data: Contents of current link
5103 * @user: Value supplied by the user
5104 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005105 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005106 */
5107static int
5108xmlWalkValidateList(const void *data, const void *user)
5109{
5110 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5111 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5112 return 1;
5113}
5114
5115/**
5116 * xmlValidateCheckRefCallback:
5117 * @ref_list: List of references
5118 * @ctxt: Validation context
5119 * @name: Name of ID we are searching for
5120 *
5121 */
5122static void
5123xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5124 const xmlChar *name) {
5125 xmlValidateMemo memo;
5126
5127 if (ref_list == NULL)
5128 return;
5129 memo.ctxt = ctxt;
5130 memo.name = name;
5131
5132 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5133
5134}
5135
5136/**
Owen Taylor3473f882001-02-23 17:55:21 +00005137 * xmlValidateDocumentFinal:
5138 * @ctxt: the validation context
5139 * @doc: a document instance
5140 *
5141 * Does the final step for the document validation once all the
5142 * incremental validation steps have been completed
5143 *
5144 * basically it does the following checks described by the XML Rec
5145 *
5146 *
5147 * returns 1 if valid or 0 otherwise
5148 */
5149
5150int
5151xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5152 xmlRefTablePtr table;
5153
5154 if (doc == NULL) {
5155 xmlGenericError(xmlGenericErrorContext,
5156 "xmlValidateDocumentFinal: doc == NULL\n");
5157 return(0);
5158 }
5159
5160 /*
5161 * Check all the NOTATION/NOTATIONS attributes
5162 */
5163 /*
5164 * Check all the ENTITY/ENTITIES attributes definition for validity
5165 */
5166 /*
5167 * Check all the IDREF/IDREFS attributes definition for validity
5168 */
5169 table = (xmlRefTablePtr) doc->refs;
5170 ctxt->doc = doc;
5171 ctxt->valid = 1;
5172 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5173 return(ctxt->valid);
5174}
5175
5176/**
5177 * xmlValidateDtd:
5178 * @ctxt: the validation context
5179 * @doc: a document instance
5180 * @dtd: a dtd instance
5181 *
5182 * Try to validate the document against the dtd instance
5183 *
5184 * basically it does check all the definitions in the DtD.
5185 *
5186 * returns 1 if valid or 0 otherwise
5187 */
5188
5189int
5190xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5191 int ret;
5192 xmlDtdPtr oldExt;
5193 xmlNodePtr root;
5194
5195 if (dtd == NULL) return(0);
5196 if (doc == NULL) return(0);
5197 oldExt = doc->extSubset;
5198 doc->extSubset = dtd;
5199 ret = xmlValidateRoot(ctxt, doc);
5200 if (ret == 0) {
5201 doc->extSubset = oldExt;
5202 return(ret);
5203 }
5204 if (doc->ids != NULL) {
5205 xmlFreeIDTable(doc->ids);
5206 doc->ids = NULL;
5207 }
5208 if (doc->refs != NULL) {
5209 xmlFreeRefTable(doc->refs);
5210 doc->refs = NULL;
5211 }
5212 root = xmlDocGetRootElement(doc);
5213 ret = xmlValidateElement(ctxt, doc, root);
5214 ret &= xmlValidateDocumentFinal(ctxt, doc);
5215 doc->extSubset = oldExt;
5216 return(ret);
5217}
5218
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005219static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005220xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5221 const xmlChar *name ATTRIBUTE_UNUSED) {
5222 if (cur == NULL)
5223 return;
5224 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5225 xmlChar *notation = cur->content;
5226
Daniel Veillard878eab02002-02-19 13:46:09 +00005227 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005228 int ret;
5229
5230 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5231 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005232 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005233 }
5234 }
5235 }
5236}
5237
5238static void
Owen Taylor3473f882001-02-23 17:55:21 +00005239xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005240 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005241 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005242 xmlDocPtr doc;
5243 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005244
Owen Taylor3473f882001-02-23 17:55:21 +00005245 if (cur == NULL)
5246 return;
5247 switch (cur->atype) {
5248 case XML_ATTRIBUTE_CDATA:
5249 case XML_ATTRIBUTE_ID:
5250 case XML_ATTRIBUTE_IDREF :
5251 case XML_ATTRIBUTE_IDREFS:
5252 case XML_ATTRIBUTE_NMTOKEN:
5253 case XML_ATTRIBUTE_NMTOKENS:
5254 case XML_ATTRIBUTE_ENUMERATION:
5255 break;
5256 case XML_ATTRIBUTE_ENTITY:
5257 case XML_ATTRIBUTE_ENTITIES:
5258 case XML_ATTRIBUTE_NOTATION:
5259 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005260
5261 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5262 cur->atype, cur->defaultValue);
5263 if ((ret == 0) && (ctxt->valid == 1))
5264 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005265 }
5266 if (cur->tree != NULL) {
5267 xmlEnumerationPtr tree = cur->tree;
5268 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005269 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005270 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005271 if ((ret == 0) && (ctxt->valid == 1))
5272 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005273 tree = tree->next;
5274 }
5275 }
5276 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005277 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5278 doc = cur->doc;
5279 if ((doc == NULL) || (cur->elem == NULL)) {
5280 VERROR(ctxt->userData,
5281 "xmlValidateAttributeCallback(%s): internal error\n",
5282 cur->name);
5283 return;
5284 }
5285 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5286 if (elem == NULL)
5287 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5288 if (elem == NULL) {
5289 VERROR(ctxt->userData,
5290 "attribute %s: could not find decl for element %s\n",
5291 cur->name, cur->elem);
5292 return;
5293 }
5294 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5295 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005296 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005297 cur->name, cur->elem);
5298 ctxt->valid = 0;
5299 }
5300 }
Owen Taylor3473f882001-02-23 17:55:21 +00005301}
5302
5303/**
5304 * xmlValidateDtdFinal:
5305 * @ctxt: the validation context
5306 * @doc: a document instance
5307 *
5308 * Does the final step for the dtds validation once all the
5309 * subsets have been parsed
5310 *
5311 * basically it does the following checks described by the XML Rec
5312 * - check that ENTITY and ENTITIES type attributes default or
5313 * possible values matches one of the defined entities.
5314 * - check that NOTATION type attributes default or
5315 * possible values matches one of the defined notations.
5316 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005317 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005318 */
5319
5320int
5321xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005322 xmlDtdPtr dtd;
5323 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005324 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005325
5326 if (doc == NULL) return(0);
5327 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5328 return(0);
5329 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005330 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005331 dtd = doc->intSubset;
5332 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5333 table = (xmlAttributeTablePtr) dtd->attributes;
5334 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005335 }
5336 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005337 entities = (xmlEntitiesTablePtr) dtd->entities;
5338 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5339 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005340 }
5341 dtd = doc->extSubset;
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 return(ctxt->valid);
5352}
5353
5354/**
5355 * xmlValidateDocument:
5356 * @ctxt: the validation context
5357 * @doc: a document instance
5358 *
5359 * Try to validate the document instance
5360 *
5361 * basically it does the all the checks described by the XML Rec
5362 * i.e. validates the internal and external subset (if present)
5363 * and validate the document tree.
5364 *
5365 * returns 1 if valid or 0 otherwise
5366 */
5367
5368int
5369xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5370 int ret;
5371 xmlNodePtr root;
5372
5373 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5374 return(0);
5375 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5376 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5377 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5378 doc->intSubset->SystemID);
5379 if (doc->extSubset == NULL) {
5380 if (doc->intSubset->SystemID != NULL) {
5381 VERROR(ctxt->userData,
5382 "Could not load the external subset \"%s\"\n",
5383 doc->intSubset->SystemID);
5384 } else {
5385 VERROR(ctxt->userData,
5386 "Could not load the external subset \"%s\"\n",
5387 doc->intSubset->ExternalID);
5388 }
5389 return(0);
5390 }
5391 }
5392
5393 if (doc->ids != NULL) {
5394 xmlFreeIDTable(doc->ids);
5395 doc->ids = NULL;
5396 }
5397 if (doc->refs != NULL) {
5398 xmlFreeRefTable(doc->refs);
5399 doc->refs = NULL;
5400 }
5401 ret = xmlValidateDtdFinal(ctxt, doc);
5402 if (!xmlValidateRoot(ctxt, doc)) return(0);
5403
5404 root = xmlDocGetRootElement(doc);
5405 ret &= xmlValidateElement(ctxt, doc, root);
5406 ret &= xmlValidateDocumentFinal(ctxt, doc);
5407 return(ret);
5408}
5409
5410
5411/************************************************************************
5412 * *
5413 * Routines for dynamic validation editing *
5414 * *
5415 ************************************************************************/
5416
5417/**
5418 * xmlValidGetPotentialChildren:
5419 * @ctree: an element content tree
5420 * @list: an array to store the list of child names
5421 * @len: a pointer to the number of element in the list
5422 * @max: the size of the array
5423 *
5424 * Build/extend a list of potential children allowed by the content tree
5425 *
5426 * returns the number of element in the list, or -1 in case of error.
5427 */
5428
5429int
5430xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5431 int *len, int max) {
5432 int i;
5433
5434 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5435 return(-1);
5436 if (*len >= max) return(*len);
5437
5438 switch (ctree->type) {
5439 case XML_ELEMENT_CONTENT_PCDATA:
5440 for (i = 0; i < *len;i++)
5441 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5442 list[(*len)++] = BAD_CAST "#PCDATA";
5443 break;
5444 case XML_ELEMENT_CONTENT_ELEMENT:
5445 for (i = 0; i < *len;i++)
5446 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5447 list[(*len)++] = ctree->name;
5448 break;
5449 case XML_ELEMENT_CONTENT_SEQ:
5450 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5451 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5452 break;
5453 case XML_ELEMENT_CONTENT_OR:
5454 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5455 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5456 break;
5457 }
5458
5459 return(*len);
5460}
5461
5462/**
5463 * xmlValidGetValidElements:
5464 * @prev: an element to insert after
5465 * @next: an element to insert next
5466 * @list: an array to store the list of child names
5467 * @max: the size of the array
5468 *
5469 * This function returns the list of authorized children to insert
5470 * within an existing tree while respecting the validity constraints
5471 * forced by the Dtd. The insertion point is defined using @prev and
5472 * @next in the following ways:
5473 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5474 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5475 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5476 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5477 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5478 *
5479 * pointers to the element names are inserted at the beginning of the array
5480 * and do not need to be freed.
5481 *
5482 * returns the number of element in the list, or -1 in case of error. If
5483 * the function returns the value @max the caller is invited to grow the
5484 * receiving array and retry.
5485 */
5486
5487int
5488xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5489 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005490 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005491 int nb_valid_elements = 0;
5492 const xmlChar *elements[256];
5493 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005494 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005495
5496 xmlNode *ref_node;
5497 xmlNode *parent;
5498 xmlNode *test_node;
5499
5500 xmlNode *prev_next;
5501 xmlNode *next_prev;
5502 xmlNode *parent_childs;
5503 xmlNode *parent_last;
5504
5505 xmlElement *element_desc;
5506
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005507 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005508
Owen Taylor3473f882001-02-23 17:55:21 +00005509 if (prev == NULL && next == NULL)
5510 return(-1);
5511
5512 if (list == NULL) return(-1);
5513 if (max <= 0) return(-1);
5514
5515 nb_valid_elements = 0;
5516 ref_node = prev ? prev : next;
5517 parent = ref_node->parent;
5518
5519 /*
5520 * Retrieves the parent element declaration
5521 */
5522 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5523 parent->name);
5524 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5525 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5526 parent->name);
5527 if (element_desc == NULL) return(-1);
5528
5529 /*
5530 * Do a backup of the current tree structure
5531 */
5532 prev_next = prev ? prev->next : NULL;
5533 next_prev = next ? next->prev : NULL;
5534 parent_childs = parent->children;
5535 parent_last = parent->last;
5536
5537 /*
5538 * Creates a dummy node and insert it into the tree
5539 */
5540 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5541 test_node->doc = ref_node->doc;
5542 test_node->parent = parent;
5543 test_node->prev = prev;
5544 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005545 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005546
5547 if (prev) prev->next = test_node;
5548 else parent->children = test_node;
5549
5550 if (next) next->prev = test_node;
5551 else parent->last = test_node;
5552
5553 /*
5554 * Insert each potential child node and check if the parent is
5555 * still valid
5556 */
5557 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5558 elements, &nb_elements, 256);
5559
5560 for (i = 0;i < nb_elements;i++) {
5561 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005562 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005563 int j;
5564
5565 for (j = 0; j < nb_valid_elements;j++)
5566 if (xmlStrEqual(elements[i], list[j])) break;
5567 list[nb_valid_elements++] = elements[i];
5568 if (nb_valid_elements >= max) break;
5569 }
5570 }
5571
5572 /*
5573 * Restore the tree structure
5574 */
5575 if (prev) prev->next = prev_next;
5576 if (next) next->prev = next_prev;
5577 parent->children = parent_childs;
5578 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005579
5580 /*
5581 * Free up the dummy node
5582 */
5583 test_node->name = name;
5584 xmlFreeNode(test_node);
5585
Owen Taylor3473f882001-02-23 17:55:21 +00005586 return(nb_valid_elements);
5587}