blob: 7fa634c7338b96def2afe5aa79075a89ef7ccc88 [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 */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000029/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000030
Daniel Veillarda646cfd2002-09-17 21:50:03 +000031#define TODO \
32 xmlGenericError(xmlGenericErrorContext, \
33 "Unimplemented block at %s:%d\n", \
34 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000035
Daniel Veillardea7751d2002-12-20 00:16:24 +000036#define VERROR \
37 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
Owen Taylor3473f882001-02-23 17:55:21 +000038
Daniel Veillardea7751d2002-12-20 00:16:24 +000039#define VWARNING \
40 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
41
42
43#ifdef LIBXML_REGEXP_ENABLED
44/*
45 * If regexp are enabled we can do continuous validation without the
46 * need of a tree to validate the content model. this is done in each
47 * callbacks.
48 * Each xmlValidState represent the validation state associated to the
49 * set of nodes currently open from the document root to the current element.
50 */
51
52
53typedef struct _xmlValidState {
54 xmlElementPtr elemDecl; /* pointer to the content model */
55 xmlNodePtr node; /* pointer to the current node */
56 xmlRegExecCtxtPtr exec; /* regexp runtime */
57} _xmlValidState;
58
59
60static int
61vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
62 if (ctxt->vstateMax == 0) {
63 ctxt->vstateMax = 10;
64 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
65 sizeof(ctxt->vstateTab[0]));
66 if (ctxt->vstateTab == NULL) {
67 VERROR(ctxt->userData, "realloc failed !n");
68 return(-1);
69 }
70 }
71
72 if (ctxt->vstateNr >= ctxt->vstateMax) {
73 ctxt->vstateMax *= 2;
74 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
75 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
76 if (ctxt->vstateTab == NULL) {
77 VERROR(ctxt->userData, "realloc failed !n");
78 return(-1);
79 }
80 }
81 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
82 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
83 ctxt->vstateTab[ctxt->vstateNr].node = node;
84 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
85 if (elemDecl->contModel == NULL)
86 xmlValidBuildContentModel(ctxt, elemDecl);
87 if (elemDecl->contModel != NULL) {
88 ctxt->vstateTab[ctxt->vstateNr].exec =
89 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
90 } else {
91 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
92 VERROR(ctxt->userData,
93 "Failed to build content model regexp for %s", node->name);
94 }
95 }
96 return(ctxt->vstateNr++);
97}
98
99static int
100vstateVPop(xmlValidCtxtPtr ctxt) {
101 xmlElementPtr elemDecl;
102
103 if (ctxt->vstateNr <= 1) return(-1);
104 ctxt->vstateNr--;
105 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
106 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
107 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
108 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
109 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
110 }
111 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
112 if (ctxt->vstateNr >= 1)
113 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
114 else
115 ctxt->vstate = NULL;
116 return(ctxt->vstateNr);
117}
118
119#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000120/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000121 * If regexp are not enabled, it uses a home made algorithm less
122 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000123 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000124 * only restriction is on the deepness of the tree limited by the
125 * size of the occurs bitfield
126 *
127 * this is the content of a saved state for rollbacks
128 */
129
130#define ROLLBACK_OR 0
131#define ROLLBACK_PARENT 1
132
Daniel Veillardb44025c2001-10-11 22:55:55 +0000133typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000134 xmlElementContentPtr cont; /* pointer to the content model subtree */
135 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000136 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000137 unsigned char depth; /* current depth in the overall tree */
138 unsigned char state; /* ROLLBACK_XXX */
139} _xmlValidState;
140
Daniel Veillardfc57b412002-04-29 15:50:14 +0000141#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000142#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
143#define CONT ctxt->vstate->cont
144#define NODE ctxt->vstate->node
145#define DEPTH ctxt->vstate->depth
146#define OCCURS ctxt->vstate->occurs
147#define STATE ctxt->vstate->state
148
Daniel Veillard5344c602001-12-31 16:37:34 +0000149#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
150#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000151
Daniel Veillard5344c602001-12-31 16:37:34 +0000152#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
153#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000154
155static int
156vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
157 xmlNodePtr node, unsigned char depth, long occurs,
158 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000159 int i = ctxt->vstateNr - 1;
160
Daniel Veillard940492d2002-04-15 10:15:25 +0000161 if (ctxt->vstateNr > MAX_RECURSE) {
162 return(-1);
163 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000164 if (ctxt->vstateNr >= ctxt->vstateMax) {
165 ctxt->vstateMax *= 2;
166 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
167 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
168 if (ctxt->vstateTab == NULL) {
169 xmlGenericError(xmlGenericErrorContext,
170 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000171 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000172 }
Daniel Veillard06803992001-04-22 10:35:56 +0000173 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000174 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000175 /*
176 * Don't push on the stack a state already here
177 */
178 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
179 (ctxt->vstateTab[i].node == node) &&
180 (ctxt->vstateTab[i].depth == depth) &&
181 (ctxt->vstateTab[i].occurs == occurs) &&
182 (ctxt->vstateTab[i].state == state))
183 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000184 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
185 ctxt->vstateTab[ctxt->vstateNr].node = node;
186 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
187 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
188 ctxt->vstateTab[ctxt->vstateNr].state = state;
189 return(ctxt->vstateNr++);
190}
191
192static int
193vstateVPop(xmlValidCtxtPtr ctxt) {
194 if (ctxt->vstateNr <= 1) return(-1);
195 ctxt->vstateNr--;
196 ctxt->vstate = &ctxt->vstateTab[0];
197 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
198 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
199 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
200 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
201 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
202 return(ctxt->vstateNr);
203}
204
Daniel Veillard118aed72002-09-24 14:13:13 +0000205#endif /* LIBXML_REGEXP_ENABLED */
206
Daniel Veillard1c732d22002-11-30 11:22:59 +0000207static int
208nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
209{
210 if (ctxt->nodeMax <= 0) {
211 ctxt->nodeMax = 4;
212 ctxt->nodeTab =
213 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
214 sizeof(ctxt->nodeTab[0]));
215 if (ctxt->nodeTab == NULL) {
216 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
217 ctxt->nodeMax = 0;
218 return (0);
219 }
220 }
221 if (ctxt->nodeNr >= ctxt->nodeMax) {
222 ctxt->nodeMax *= 2;
223 ctxt->nodeTab =
224 (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
225 ctxt->nodeMax *
226 sizeof(ctxt->nodeTab[0]));
227 if (ctxt->nodeTab == NULL) {
228 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
229 return (0);
230 }
231 }
232 ctxt->nodeTab[ctxt->nodeNr] = value;
233 ctxt->node = value;
234 return (ctxt->nodeNr++);
235}
236static xmlNodePtr
237nodeVPop(xmlValidCtxtPtr ctxt)
238{
239 xmlNodePtr ret;
240
241 if (ctxt->nodeNr <= 0)
242 return (0);
243 ctxt->nodeNr--;
244 if (ctxt->nodeNr > 0)
245 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
246 else
247 ctxt->node = NULL;
248 ret = ctxt->nodeTab[ctxt->nodeNr];
249 ctxt->nodeTab[ctxt->nodeNr] = 0;
250 return (ret);
251}
Owen Taylor3473f882001-02-23 17:55:21 +0000252
Owen Taylor3473f882001-02-23 17:55:21 +0000253#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000254static void
255xmlValidPrintNode(xmlNodePtr cur) {
256 if (cur == NULL) {
257 xmlGenericError(xmlGenericErrorContext, "null");
258 return;
259 }
260 switch (cur->type) {
261 case XML_ELEMENT_NODE:
262 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
263 break;
264 case XML_TEXT_NODE:
265 xmlGenericError(xmlGenericErrorContext, "text ");
266 break;
267 case XML_CDATA_SECTION_NODE:
268 xmlGenericError(xmlGenericErrorContext, "cdata ");
269 break;
270 case XML_ENTITY_REF_NODE:
271 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
272 break;
273 case XML_PI_NODE:
274 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
275 break;
276 case XML_COMMENT_NODE:
277 xmlGenericError(xmlGenericErrorContext, "comment ");
278 break;
279 case XML_ATTRIBUTE_NODE:
280 xmlGenericError(xmlGenericErrorContext, "?attr? ");
281 break;
282 case XML_ENTITY_NODE:
283 xmlGenericError(xmlGenericErrorContext, "?ent? ");
284 break;
285 case XML_DOCUMENT_NODE:
286 xmlGenericError(xmlGenericErrorContext, "?doc? ");
287 break;
288 case XML_DOCUMENT_TYPE_NODE:
289 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
290 break;
291 case XML_DOCUMENT_FRAG_NODE:
292 xmlGenericError(xmlGenericErrorContext, "?frag? ");
293 break;
294 case XML_NOTATION_NODE:
295 xmlGenericError(xmlGenericErrorContext, "?nota? ");
296 break;
297 case XML_HTML_DOCUMENT_NODE:
298 xmlGenericError(xmlGenericErrorContext, "?html? ");
299 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000300#ifdef LIBXML_DOCB_ENABLED
301 case XML_DOCB_DOCUMENT_NODE:
302 xmlGenericError(xmlGenericErrorContext, "?docb? ");
303 break;
304#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000305 case XML_DTD_NODE:
306 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
307 break;
308 case XML_ELEMENT_DECL:
309 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
310 break;
311 case XML_ATTRIBUTE_DECL:
312 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
313 break;
314 case XML_ENTITY_DECL:
315 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
316 break;
317 case XML_NAMESPACE_DECL:
318 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
319 break;
320 case XML_XINCLUDE_START:
321 xmlGenericError(xmlGenericErrorContext, "incstart ");
322 break;
323 case XML_XINCLUDE_END:
324 xmlGenericError(xmlGenericErrorContext, "incend ");
325 break;
326 }
327}
328
329static void
330xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000331 if (cur == NULL)
332 xmlGenericError(xmlGenericErrorContext, "null ");
333 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000334 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000335 cur = cur->next;
336 }
337}
338
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000339static void
340xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000341 char expr[1000];
342
343 expr[0] = 0;
344 xmlGenericError(xmlGenericErrorContext, "valid: ");
345 xmlValidPrintNodeList(cur);
346 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000347 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000348 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
349}
350
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000351static void
352xmlValidDebugState(xmlValidStatePtr state) {
353 xmlGenericError(xmlGenericErrorContext, "(");
354 if (state->cont == NULL)
355 xmlGenericError(xmlGenericErrorContext, "null,");
356 else
357 switch (state->cont->type) {
358 case XML_ELEMENT_CONTENT_PCDATA:
359 xmlGenericError(xmlGenericErrorContext, "pcdata,");
360 break;
361 case XML_ELEMENT_CONTENT_ELEMENT:
362 xmlGenericError(xmlGenericErrorContext, "%s,",
363 state->cont->name);
364 break;
365 case XML_ELEMENT_CONTENT_SEQ:
366 xmlGenericError(xmlGenericErrorContext, "seq,");
367 break;
368 case XML_ELEMENT_CONTENT_OR:
369 xmlGenericError(xmlGenericErrorContext, "or,");
370 break;
371 }
372 xmlValidPrintNode(state->node);
373 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
374 state->depth, state->occurs, state->state);
375}
376
377static void
378xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
379 int i, j;
380
381 xmlGenericError(xmlGenericErrorContext, "state: ");
382 xmlValidDebugState(ctxt->vstate);
383 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
384 ctxt->vstateNr - 1);
385 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
386 xmlValidDebugState(&ctxt->vstateTab[j]);
387 xmlGenericError(xmlGenericErrorContext, "\n");
388}
389
390/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000391#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000392 *****/
393
394#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000395#define DEBUG_VALID_MSG(m) \
396 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
397
Owen Taylor3473f882001-02-23 17:55:21 +0000398#else
399#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000400#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000401#endif
402
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000403/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000404
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000405#define VECTXT(ctxt, node) \
406 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000407 (node != NULL)) { \
408 xmlChar *base = xmlNodeGetBase(NULL,node); \
409 if (base != NULL) { \
410 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000411 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000412 xmlFree(base); \
413 } else \
414 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000415 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000416 }
417
418#define VWCTXT(ctxt, node) \
419 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000420 (node != NULL)) { \
421 xmlChar *base = xmlNodeGetBase(NULL,node); \
422 if (base != NULL) { \
423 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000424 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000425 xmlFree(base); \
426 } else \
427 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000428 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000429 }
430
Owen Taylor3473f882001-02-23 17:55:21 +0000431#define CHECK_DTD \
432 if (doc == NULL) return(0); \
433 else if ((doc->intSubset == NULL) && \
434 (doc->extSubset == NULL)) return(0)
435
Daniel Veillarda10efa82001-04-18 13:09:01 +0000436static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
437 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000438xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
439
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000440#ifdef LIBXML_REGEXP_ENABLED
441
442/************************************************************************
443 * *
444 * Content model validation based on the regexps *
445 * *
446 ************************************************************************/
447
448/**
449 * xmlValidBuildAContentModel:
450 * @content: the content model
451 * @ctxt: the schema parser context
452 * @name: the element name whose content is being built
453 *
454 * Generate the automata sequence needed for that type
455 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000456 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000457 */
458static int
459xmlValidBuildAContentModel(xmlElementContentPtr content,
460 xmlValidCtxtPtr ctxt,
461 const xmlChar *name) {
462 if (content == NULL) {
463 VERROR(ctxt->userData,
464 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000465 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000466 }
467 switch (content->type) {
468 case XML_ELEMENT_CONTENT_PCDATA:
469 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
470 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000471 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000472 break;
473 case XML_ELEMENT_CONTENT_ELEMENT: {
474 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000475 xmlChar *QName = NULL;
476 const xmlChar *fname = content->name;
477
478 if (content->prefix != NULL) {
479 int len;
480
481 len = xmlStrlen(content->name) +
482 xmlStrlen(content->prefix) + 2;
483 QName = xmlMalloc(len);
484 if (QName == NULL) {
485 VERROR(ctxt->userData,
486 "ContentModel %s : alloc failed\n", name);
487 return(0);
488 }
489 snprintf((char *) QName, len, "%s:%s",
490 (char *)content->prefix,
491 (char *)content->name);
492 fname = QName;
493 }
494
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000495 switch (content->ocur) {
496 case XML_ELEMENT_CONTENT_ONCE:
497 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000498 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000499 break;
500 case XML_ELEMENT_CONTENT_OPT:
501 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000502 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000503 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
504 break;
505 case XML_ELEMENT_CONTENT_PLUS:
506 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000507 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000508 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000509 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000510 break;
511 case XML_ELEMENT_CONTENT_MULT:
512 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000513 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000514 break;
515 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000516 if (QName != NULL)
517 xmlFree(QName);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000518 break;
519 }
520 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000521 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000522 xmlElementContentOccur ocur;
523
524 /*
525 * Simply iterate over the content
526 */
527 oldstate = ctxt->state;
528 ocur = content->ocur;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000529 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000530 xmlValidBuildAContentModel(content->c1, ctxt, name);
531 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000532 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
533 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
534 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000535 oldend = ctxt->state;
536 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000537 switch (ocur) {
538 case XML_ELEMENT_CONTENT_ONCE:
539 break;
540 case XML_ELEMENT_CONTENT_OPT:
541 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
542 break;
543 case XML_ELEMENT_CONTENT_MULT:
544 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000545 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000546 break;
547 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000548 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000549 break;
550 }
551 break;
552 }
553 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000554 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000555 xmlElementContentOccur ocur;
556
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000557 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000558 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
559 (ocur == XML_ELEMENT_CONTENT_MULT)) {
560 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
561 ctxt->state, NULL);
562 }
563 oldstate = ctxt->state;
564 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000565
566 /*
567 * iterate over the subtypes and remerge the end with an
568 * epsilon transition
569 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000570 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000571 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000572 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000573 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000574 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000575 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
576 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000577 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000578 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000579 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
580 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000581 switch (ocur) {
582 case XML_ELEMENT_CONTENT_ONCE:
583 break;
584 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000585 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000586 break;
587 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000588 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
589 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000590 break;
591 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000592 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000593 break;
594 }
595 break;
596 }
597 default:
598 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
599 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000600 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000601 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000602 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000603}
604/**
605 * xmlValidBuildContentModel:
606 * @ctxt: a validation context
607 * @elem: an element declaration node
608 *
609 * (Re)Build the automata associated to the content model of this
610 * element
611 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000612 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000613 */
614int
615xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
616 xmlAutomataStatePtr start;
617
618 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000619 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000620 if (elem->type != XML_ELEMENT_DECL)
621 return(0);
622 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
623 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000624 /* TODO: should we rebuild in this case ? */
625 if (elem->contModel != NULL)
Daniel Veillard84d70a42002-09-16 10:51:38 +0000626 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000627
628 ctxt->am = xmlNewAutomata();
629 if (ctxt->am == NULL) {
630 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
631 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000632 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000633 }
634 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
635 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
636 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000637 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillard23e73572002-09-19 19:56:43 +0000638 if (!xmlRegexpIsDeterminist(elem->contModel)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000639 char expr[5000];
640 expr[0] = 0;
641 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
642 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
643 elem->name, expr);
644#ifdef DEBUG_REGEXP_ALGO
645 xmlRegexpPrint(stderr, elem->contModel);
646#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000647 ctxt->valid = 0;
648 }
649 ctxt->state = NULL;
650 xmlFreeAutomata(ctxt->am);
651 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000652 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000653}
654
655#endif /* LIBXML_REGEXP_ENABLED */
656
Owen Taylor3473f882001-02-23 17:55:21 +0000657/************************************************************************
658 * *
659 * QName handling helper *
660 * *
661 ************************************************************************/
662
663/**
664 * xmlSplitQName2:
665 * @name: an XML parser context
666 * @prefix: a xmlChar **
667 *
668 * parse an XML qualified name string
669 *
670 * [NS 5] QName ::= (Prefix ':')? LocalPart
671 *
672 * [NS 6] Prefix ::= NCName
673 *
674 * [NS 7] LocalPart ::= NCName
675 *
676 * Returns NULL if not a QName, otherwise the local part, and prefix
677 * is updated to get the Prefix if any.
678 */
679
680xmlChar *
681xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
682 int len = 0;
683 xmlChar *ret = NULL;
684
685 *prefix = NULL;
686
Daniel Veillardf4309d72001-10-02 09:28:58 +0000687#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000688 /* xml: prefix is not really a namespace */
689 if ((name[0] == 'x') && (name[1] == 'm') &&
690 (name[2] == 'l') && (name[3] == ':'))
691 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000692#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000693
694 /* nasty but valid */
695 if (name[0] == ':')
696 return(NULL);
697
698 /*
699 * we are not trying to validate but just to cut, and yes it will
700 * work even if this is as set of UTF-8 encoded chars
701 */
702 while ((name[len] != 0) && (name[len] != ':'))
703 len++;
704
705 if (name[len] == 0)
706 return(NULL);
707
708 *prefix = xmlStrndup(name, len);
709 ret = xmlStrdup(&name[len + 1]);
710
711 return(ret);
712}
713
714/****************************************************************
715 * *
716 * Util functions for data allocation/deallocation *
717 * *
718 ****************************************************************/
719
720/**
721 * xmlNewElementContent:
722 * @name: the subelement name or NULL
723 * @type: the type of element content decl
724 *
725 * Allocate an element content structure.
726 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000727 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000728 */
729xmlElementContentPtr
730xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
731 xmlElementContentPtr ret;
732
733 switch(type) {
734 case XML_ELEMENT_CONTENT_ELEMENT:
735 if (name == NULL) {
736 xmlGenericError(xmlGenericErrorContext,
737 "xmlNewElementContent : name == NULL !\n");
738 }
739 break;
740 case XML_ELEMENT_CONTENT_PCDATA:
741 case XML_ELEMENT_CONTENT_SEQ:
742 case XML_ELEMENT_CONTENT_OR:
743 if (name != NULL) {
744 xmlGenericError(xmlGenericErrorContext,
745 "xmlNewElementContent : name != NULL !\n");
746 }
747 break;
748 default:
749 xmlGenericError(xmlGenericErrorContext,
750 "xmlNewElementContent: unknown type %d\n", type);
751 return(NULL);
752 }
753 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
754 if (ret == NULL) {
755 xmlGenericError(xmlGenericErrorContext,
756 "xmlNewElementContent : out of memory!\n");
757 return(NULL);
758 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000759 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000760 ret->type = type;
761 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000762 if (name != NULL) {
763 xmlChar *prefix = NULL;
764 ret->name = xmlSplitQName2(name, &prefix);
765 if (ret->name == NULL)
766 ret->name = xmlStrdup(name);
767 ret->prefix = prefix;
768 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000769 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000770 ret->prefix = NULL;
771 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000772 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000773 return(ret);
774}
775
776/**
777 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000778 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000779 *
780 * Build a copy of an element content description.
781 *
782 * Returns the new xmlElementContentPtr or NULL in case of error.
783 */
784xmlElementContentPtr
785xmlCopyElementContent(xmlElementContentPtr cur) {
786 xmlElementContentPtr ret;
787
788 if (cur == NULL) return(NULL);
789 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
790 if (ret == NULL) {
791 xmlGenericError(xmlGenericErrorContext,
792 "xmlCopyElementContent : out of memory\n");
793 return(NULL);
794 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000795 if (cur->prefix != NULL)
796 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000797 ret->ocur = cur->ocur;
798 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000799 if (ret->c1 != NULL)
800 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000801 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000802 if (ret->c2 != NULL)
803 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000804 return(ret);
805}
806
807/**
808 * xmlFreeElementContent:
809 * @cur: the element content tree to free
810 *
811 * Free an element content structure. This is a recursive call !
812 */
813void
814xmlFreeElementContent(xmlElementContentPtr cur) {
815 if (cur == NULL) return;
816 switch (cur->type) {
817 case XML_ELEMENT_CONTENT_PCDATA:
818 case XML_ELEMENT_CONTENT_ELEMENT:
819 case XML_ELEMENT_CONTENT_SEQ:
820 case XML_ELEMENT_CONTENT_OR:
821 break;
822 default:
823 xmlGenericError(xmlGenericErrorContext,
824 "xmlFreeElementContent : type %d\n", cur->type);
825 return;
826 }
827 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
828 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
829 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000830 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000831 xmlFree(cur);
832}
833
834/**
835 * xmlDumpElementContent:
836 * @buf: An XML buffer
837 * @content: An element table
838 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
839 *
840 * This will dump the content of the element table as an XML DTD definition
841 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000842static void
Owen Taylor3473f882001-02-23 17:55:21 +0000843xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
844 if (content == NULL) return;
845
846 if (glob) xmlBufferWriteChar(buf, "(");
847 switch (content->type) {
848 case XML_ELEMENT_CONTENT_PCDATA:
849 xmlBufferWriteChar(buf, "#PCDATA");
850 break;
851 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000852 if (content->prefix != NULL) {
853 xmlBufferWriteCHAR(buf, content->prefix);
854 xmlBufferWriteChar(buf, ":");
855 }
Owen Taylor3473f882001-02-23 17:55:21 +0000856 xmlBufferWriteCHAR(buf, content->name);
857 break;
858 case XML_ELEMENT_CONTENT_SEQ:
859 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
860 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
861 xmlDumpElementContent(buf, content->c1, 1);
862 else
863 xmlDumpElementContent(buf, content->c1, 0);
864 xmlBufferWriteChar(buf, " , ");
865 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
866 xmlDumpElementContent(buf, content->c2, 1);
867 else
868 xmlDumpElementContent(buf, content->c2, 0);
869 break;
870 case XML_ELEMENT_CONTENT_OR:
871 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
872 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
873 xmlDumpElementContent(buf, content->c1, 1);
874 else
875 xmlDumpElementContent(buf, content->c1, 0);
876 xmlBufferWriteChar(buf, " | ");
877 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
878 xmlDumpElementContent(buf, content->c2, 1);
879 else
880 xmlDumpElementContent(buf, content->c2, 0);
881 break;
882 default:
883 xmlGenericError(xmlGenericErrorContext,
884 "xmlDumpElementContent: unknown type %d\n",
885 content->type);
886 }
887 if (glob)
888 xmlBufferWriteChar(buf, ")");
889 switch (content->ocur) {
890 case XML_ELEMENT_CONTENT_ONCE:
891 break;
892 case XML_ELEMENT_CONTENT_OPT:
893 xmlBufferWriteChar(buf, "?");
894 break;
895 case XML_ELEMENT_CONTENT_MULT:
896 xmlBufferWriteChar(buf, "*");
897 break;
898 case XML_ELEMENT_CONTENT_PLUS:
899 xmlBufferWriteChar(buf, "+");
900 break;
901 }
902}
903
904/**
905 * xmlSprintfElementContent:
906 * @buf: an output buffer
907 * @content: An element table
908 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
909 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000910 * Deprecated, unsafe, use xmlSnprintfElementContent
911 */
912void
913xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
914 xmlElementContentPtr content ATTRIBUTE_UNUSED,
915 int glob ATTRIBUTE_UNUSED) {
916}
917
918/**
919 * xmlSnprintfElementContent:
920 * @buf: an output buffer
921 * @size: the buffer size
922 * @content: An element table
923 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
924 *
Owen Taylor3473f882001-02-23 17:55:21 +0000925 * This will dump the content of the element content definition
926 * Intended just for the debug routine
927 */
928void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000929xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
930 int len;
931
Owen Taylor3473f882001-02-23 17:55:21 +0000932 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000933 len = strlen(buf);
934 if (size - len < 50) {
935 if ((size - len > 4) && (buf[len - 1] != '.'))
936 strcat(buf, " ...");
937 return;
938 }
Owen Taylor3473f882001-02-23 17:55:21 +0000939 if (glob) strcat(buf, "(");
940 switch (content->type) {
941 case XML_ELEMENT_CONTENT_PCDATA:
942 strcat(buf, "#PCDATA");
943 break;
944 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000945 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000946 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000947 strcat(buf, " ...");
948 return;
949 }
950 strcat(buf, (char *) content->prefix);
951 strcat(buf, ":");
952 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000953 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000954 strcat(buf, " ...");
955 return;
956 }
Owen Taylor3473f882001-02-23 17:55:21 +0000957 strcat(buf, (char *) content->name);
958 break;
959 case XML_ELEMENT_CONTENT_SEQ:
960 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
961 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000962 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000963 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000964 xmlSnprintfElementContent(buf, size, content->c1, 0);
965 len = strlen(buf);
966 if (size - len < 50) {
967 if ((size - len > 4) && (buf[len - 1] != '.'))
968 strcat(buf, " ...");
969 return;
970 }
Owen Taylor3473f882001-02-23 17:55:21 +0000971 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000972 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
973 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
974 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000975 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000976 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000977 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000978 break;
979 case XML_ELEMENT_CONTENT_OR:
980 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
981 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000982 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000983 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000984 xmlSnprintfElementContent(buf, size, content->c1, 0);
985 len = strlen(buf);
986 if (size - len < 50) {
987 if ((size - len > 4) && (buf[len - 1] != '.'))
988 strcat(buf, " ...");
989 return;
990 }
Owen Taylor3473f882001-02-23 17:55:21 +0000991 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000992 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
993 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
994 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000995 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000996 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000997 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000998 break;
999 }
1000 if (glob)
1001 strcat(buf, ")");
1002 switch (content->ocur) {
1003 case XML_ELEMENT_CONTENT_ONCE:
1004 break;
1005 case XML_ELEMENT_CONTENT_OPT:
1006 strcat(buf, "?");
1007 break;
1008 case XML_ELEMENT_CONTENT_MULT:
1009 strcat(buf, "*");
1010 break;
1011 case XML_ELEMENT_CONTENT_PLUS:
1012 strcat(buf, "+");
1013 break;
1014 }
1015}
1016
1017/****************************************************************
1018 * *
1019 * Registration of DTD declarations *
1020 * *
1021 ****************************************************************/
1022
1023/**
1024 * xmlCreateElementTable:
1025 *
1026 * create and initialize an empty element hash table.
1027 *
1028 * Returns the xmlElementTablePtr just created or NULL in case of error.
1029 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001030static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001031xmlCreateElementTable(void) {
1032 return(xmlHashCreate(0));
1033}
1034
1035/**
1036 * xmlFreeElement:
1037 * @elem: An element
1038 *
1039 * Deallocate the memory used by an element definition
1040 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001041static void
Owen Taylor3473f882001-02-23 17:55:21 +00001042xmlFreeElement(xmlElementPtr elem) {
1043 if (elem == NULL) return;
1044 xmlUnlinkNode((xmlNodePtr) elem);
1045 xmlFreeElementContent(elem->content);
1046 if (elem->name != NULL)
1047 xmlFree((xmlChar *) elem->name);
1048 if (elem->prefix != NULL)
1049 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001050#ifdef LIBXML_REGEXP_ENABLED
1051 if (elem->contModel != NULL)
1052 xmlRegFreeRegexp(elem->contModel);
1053#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001054 xmlFree(elem);
1055}
1056
1057
1058/**
1059 * xmlAddElementDecl:
1060 * @ctxt: the validation context
1061 * @dtd: pointer to the DTD
1062 * @name: the entity name
1063 * @type: the element type
1064 * @content: the element content tree or NULL
1065 *
1066 * Register a new element declaration
1067 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001068 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001069 */
1070xmlElementPtr
1071xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1072 xmlElementTypeVal type,
1073 xmlElementContentPtr content) {
1074 xmlElementPtr ret;
1075 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001076 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001077 xmlChar *ns, *uqname;
1078
1079 if (dtd == NULL) {
1080 xmlGenericError(xmlGenericErrorContext,
1081 "xmlAddElementDecl: dtd == NULL\n");
1082 return(NULL);
1083 }
1084 if (name == NULL) {
1085 xmlGenericError(xmlGenericErrorContext,
1086 "xmlAddElementDecl: name == NULL\n");
1087 return(NULL);
1088 }
1089 switch (type) {
1090 case XML_ELEMENT_TYPE_EMPTY:
1091 if (content != NULL) {
1092 xmlGenericError(xmlGenericErrorContext,
1093 "xmlAddElementDecl: content != NULL for EMPTY\n");
1094 return(NULL);
1095 }
1096 break;
1097 case XML_ELEMENT_TYPE_ANY:
1098 if (content != NULL) {
1099 xmlGenericError(xmlGenericErrorContext,
1100 "xmlAddElementDecl: content != NULL for ANY\n");
1101 return(NULL);
1102 }
1103 break;
1104 case XML_ELEMENT_TYPE_MIXED:
1105 if (content == NULL) {
1106 xmlGenericError(xmlGenericErrorContext,
1107 "xmlAddElementDecl: content == NULL for MIXED\n");
1108 return(NULL);
1109 }
1110 break;
1111 case XML_ELEMENT_TYPE_ELEMENT:
1112 if (content == NULL) {
1113 xmlGenericError(xmlGenericErrorContext,
1114 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1115 return(NULL);
1116 }
1117 break;
1118 default:
1119 xmlGenericError(xmlGenericErrorContext,
1120 "xmlAddElementDecl: unknown type %d\n", type);
1121 return(NULL);
1122 }
1123
1124 /*
1125 * check if name is a QName
1126 */
1127 uqname = xmlSplitQName2(name, &ns);
1128 if (uqname != NULL)
1129 name = uqname;
1130
1131 /*
1132 * Create the Element table if needed.
1133 */
1134 table = (xmlElementTablePtr) dtd->elements;
1135 if (table == NULL) {
1136 table = xmlCreateElementTable();
1137 dtd->elements = (void *) table;
1138 }
1139 if (table == NULL) {
1140 xmlGenericError(xmlGenericErrorContext,
1141 "xmlAddElementDecl: Table creation failed!\n");
1142 return(NULL);
1143 }
1144
Daniel Veillarda10efa82001-04-18 13:09:01 +00001145 /*
1146 * lookup old attributes inserted on an undefined element in the
1147 * internal subset.
1148 */
1149 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1150 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1151 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1152 oldAttributes = ret->attributes;
1153 ret->attributes = NULL;
1154 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1155 xmlFreeElement(ret);
1156 }
Owen Taylor3473f882001-02-23 17:55:21 +00001157 }
Owen Taylor3473f882001-02-23 17:55:21 +00001158
1159 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001160 * The element may already be present if one of its attribute
1161 * was registered first
1162 */
1163 ret = xmlHashLookup2(table, name, ns);
1164 if (ret != NULL) {
1165 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1166 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001167 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001168 */
1169 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1170 if (uqname != NULL)
1171 xmlFree(uqname);
1172 return(NULL);
1173 }
1174 } else {
1175 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1176 if (ret == NULL) {
1177 xmlGenericError(xmlGenericErrorContext,
1178 "xmlAddElementDecl: out of memory\n");
1179 return(NULL);
1180 }
1181 memset(ret, 0, sizeof(xmlElement));
1182 ret->type = XML_ELEMENT_DECL;
1183
1184 /*
1185 * fill the structure.
1186 */
1187 ret->name = xmlStrdup(name);
1188 ret->prefix = ns;
1189
1190 /*
1191 * Validity Check:
1192 * Insertion must not fail
1193 */
1194 if (xmlHashAddEntry2(table, name, ns, ret)) {
1195 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001196 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001197 */
1198 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1199 xmlFreeElement(ret);
1200 if (uqname != NULL)
1201 xmlFree(uqname);
1202 return(NULL);
1203 }
1204 }
1205
1206 /*
1207 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001208 */
1209 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001210 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001211 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001212
1213 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001214 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001215 */
1216 ret->parent = dtd;
1217 ret->doc = dtd->doc;
1218 if (dtd->last == NULL) {
1219 dtd->children = dtd->last = (xmlNodePtr) ret;
1220 } else {
1221 dtd->last->next = (xmlNodePtr) ret;
1222 ret->prev = dtd->last;
1223 dtd->last = (xmlNodePtr) ret;
1224 }
1225 if (uqname != NULL)
1226 xmlFree(uqname);
1227 return(ret);
1228}
1229
1230/**
1231 * xmlFreeElementTable:
1232 * @table: An element table
1233 *
1234 * Deallocate the memory used by an element hash table.
1235 */
1236void
1237xmlFreeElementTable(xmlElementTablePtr table) {
1238 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1239}
1240
1241/**
1242 * xmlCopyElement:
1243 * @elem: An element
1244 *
1245 * Build a copy of an element.
1246 *
1247 * Returns the new xmlElementPtr or NULL in case of error.
1248 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001249static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001250xmlCopyElement(xmlElementPtr elem) {
1251 xmlElementPtr cur;
1252
1253 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1254 if (cur == NULL) {
1255 xmlGenericError(xmlGenericErrorContext,
1256 "xmlCopyElement: out of memory !\n");
1257 return(NULL);
1258 }
1259 memset(cur, 0, sizeof(xmlElement));
1260 cur->type = XML_ELEMENT_DECL;
1261 cur->etype = elem->etype;
1262 if (elem->name != NULL)
1263 cur->name = xmlStrdup(elem->name);
1264 else
1265 cur->name = NULL;
1266 if (elem->prefix != NULL)
1267 cur->prefix = xmlStrdup(elem->prefix);
1268 else
1269 cur->prefix = NULL;
1270 cur->content = xmlCopyElementContent(elem->content);
1271 /* TODO : rebuild the attribute list on the copy */
1272 cur->attributes = NULL;
1273 return(cur);
1274}
1275
1276/**
1277 * xmlCopyElementTable:
1278 * @table: An element table
1279 *
1280 * Build a copy of an element table.
1281 *
1282 * Returns the new xmlElementTablePtr or NULL in case of error.
1283 */
1284xmlElementTablePtr
1285xmlCopyElementTable(xmlElementTablePtr table) {
1286 return((xmlElementTablePtr) xmlHashCopy(table,
1287 (xmlHashCopier) xmlCopyElement));
1288}
1289
1290/**
1291 * xmlDumpElementDecl:
1292 * @buf: the XML buffer output
1293 * @elem: An element table
1294 *
1295 * This will dump the content of the element declaration as an XML
1296 * DTD definition
1297 */
1298void
1299xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1300 switch (elem->etype) {
1301 case XML_ELEMENT_TYPE_EMPTY:
1302 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001303 if (elem->prefix != NULL) {
1304 xmlBufferWriteCHAR(buf, elem->prefix);
1305 xmlBufferWriteChar(buf, ":");
1306 }
Owen Taylor3473f882001-02-23 17:55:21 +00001307 xmlBufferWriteCHAR(buf, elem->name);
1308 xmlBufferWriteChar(buf, " EMPTY>\n");
1309 break;
1310 case XML_ELEMENT_TYPE_ANY:
1311 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001312 if (elem->prefix != NULL) {
1313 xmlBufferWriteCHAR(buf, elem->prefix);
1314 xmlBufferWriteChar(buf, ":");
1315 }
Owen Taylor3473f882001-02-23 17:55:21 +00001316 xmlBufferWriteCHAR(buf, elem->name);
1317 xmlBufferWriteChar(buf, " ANY>\n");
1318 break;
1319 case XML_ELEMENT_TYPE_MIXED:
1320 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001321 if (elem->prefix != NULL) {
1322 xmlBufferWriteCHAR(buf, elem->prefix);
1323 xmlBufferWriteChar(buf, ":");
1324 }
Owen Taylor3473f882001-02-23 17:55:21 +00001325 xmlBufferWriteCHAR(buf, elem->name);
1326 xmlBufferWriteChar(buf, " ");
1327 xmlDumpElementContent(buf, elem->content, 1);
1328 xmlBufferWriteChar(buf, ">\n");
1329 break;
1330 case XML_ELEMENT_TYPE_ELEMENT:
1331 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001332 if (elem->prefix != NULL) {
1333 xmlBufferWriteCHAR(buf, elem->prefix);
1334 xmlBufferWriteChar(buf, ":");
1335 }
Owen Taylor3473f882001-02-23 17:55:21 +00001336 xmlBufferWriteCHAR(buf, elem->name);
1337 xmlBufferWriteChar(buf, " ");
1338 xmlDumpElementContent(buf, elem->content, 1);
1339 xmlBufferWriteChar(buf, ">\n");
1340 break;
1341 default:
1342 xmlGenericError(xmlGenericErrorContext,
1343 "xmlDumpElementDecl: internal: unknown type %d\n",
1344 elem->etype);
1345 }
1346}
1347
1348/**
1349 * xmlDumpElementTable:
1350 * @buf: the XML buffer output
1351 * @table: An element table
1352 *
1353 * This will dump the content of the element table as an XML DTD definition
1354 */
1355void
1356xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1357 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1358}
1359
1360/**
1361 * xmlCreateEnumeration:
1362 * @name: the enumeration name or NULL
1363 *
1364 * create and initialize an enumeration attribute node.
1365 *
1366 * Returns the xmlEnumerationPtr just created or NULL in case
1367 * of error.
1368 */
1369xmlEnumerationPtr
1370xmlCreateEnumeration(xmlChar *name) {
1371 xmlEnumerationPtr ret;
1372
1373 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1374 if (ret == NULL) {
1375 xmlGenericError(xmlGenericErrorContext,
1376 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1377 (long)sizeof(xmlEnumeration));
1378 return(NULL);
1379 }
1380 memset(ret, 0, sizeof(xmlEnumeration));
1381
1382 if (name != NULL)
1383 ret->name = xmlStrdup(name);
1384 return(ret);
1385}
1386
1387/**
1388 * xmlFreeEnumeration:
1389 * @cur: the tree to free.
1390 *
1391 * free an enumeration attribute node (recursive).
1392 */
1393void
1394xmlFreeEnumeration(xmlEnumerationPtr cur) {
1395 if (cur == NULL) return;
1396
1397 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1398
1399 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001400 xmlFree(cur);
1401}
1402
1403/**
1404 * xmlCopyEnumeration:
1405 * @cur: the tree to copy.
1406 *
1407 * Copy an enumeration attribute node (recursive).
1408 *
1409 * Returns the xmlEnumerationPtr just created or NULL in case
1410 * of error.
1411 */
1412xmlEnumerationPtr
1413xmlCopyEnumeration(xmlEnumerationPtr cur) {
1414 xmlEnumerationPtr ret;
1415
1416 if (cur == NULL) return(NULL);
1417 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1418
1419 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1420 else ret->next = NULL;
1421
1422 return(ret);
1423}
1424
1425/**
1426 * xmlDumpEnumeration:
1427 * @buf: the XML buffer output
1428 * @enum: An enumeration
1429 *
1430 * This will dump the content of the enumeration
1431 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001432static void
Owen Taylor3473f882001-02-23 17:55:21 +00001433xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1434 if (cur == NULL) return;
1435
1436 xmlBufferWriteCHAR(buf, cur->name);
1437 if (cur->next == NULL)
1438 xmlBufferWriteChar(buf, ")");
1439 else {
1440 xmlBufferWriteChar(buf, " | ");
1441 xmlDumpEnumeration(buf, cur->next);
1442 }
1443}
1444
1445/**
1446 * xmlCreateAttributeTable:
1447 *
1448 * create and initialize an empty attribute hash table.
1449 *
1450 * Returns the xmlAttributeTablePtr just created or NULL in case
1451 * of error.
1452 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001453static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001454xmlCreateAttributeTable(void) {
1455 return(xmlHashCreate(0));
1456}
1457
1458/**
1459 * xmlScanAttributeDeclCallback:
1460 * @attr: the attribute decl
1461 * @list: the list to update
1462 *
1463 * Callback called by xmlScanAttributeDecl when a new attribute
1464 * has to be entered in the list.
1465 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001466static void
Owen Taylor3473f882001-02-23 17:55:21 +00001467xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001468 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001469 attr->nexth = *list;
1470 *list = attr;
1471}
1472
1473/**
1474 * xmlScanAttributeDecl:
1475 * @dtd: pointer to the DTD
1476 * @elem: the element name
1477 *
1478 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001479 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001480 *
1481 * Returns the pointer to the first attribute decl in the chain,
1482 * possibly NULL.
1483 */
1484xmlAttributePtr
1485xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1486 xmlAttributePtr ret = NULL;
1487 xmlAttributeTablePtr table;
1488
1489 if (dtd == NULL) {
1490 xmlGenericError(xmlGenericErrorContext,
1491 "xmlScanAttributeDecl: dtd == NULL\n");
1492 return(NULL);
1493 }
1494 if (elem == NULL) {
1495 xmlGenericError(xmlGenericErrorContext,
1496 "xmlScanAttributeDecl: elem == NULL\n");
1497 return(NULL);
1498 }
1499 table = (xmlAttributeTablePtr) dtd->attributes;
1500 if (table == NULL)
1501 return(NULL);
1502
1503 /* WRONG !!! */
1504 xmlHashScan3(table, NULL, NULL, elem,
1505 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1506 return(ret);
1507}
1508
1509/**
1510 * xmlScanIDAttributeDecl:
1511 * @ctxt: the validation context
1512 * @elem: the element name
1513 *
1514 * Verify that the element don't have too many ID attributes
1515 * declared.
1516 *
1517 * Returns the number of ID attributes found.
1518 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001519static int
Owen Taylor3473f882001-02-23 17:55:21 +00001520xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1521 xmlAttributePtr cur;
1522 int ret = 0;
1523
1524 if (elem == NULL) return(0);
1525 cur = elem->attributes;
1526 while (cur != NULL) {
1527 if (cur->atype == XML_ATTRIBUTE_ID) {
1528 ret ++;
1529 if (ret > 1)
1530 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001531 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001532 elem->name, cur->name);
1533 }
1534 cur = cur->nexth;
1535 }
1536 return(ret);
1537}
1538
1539/**
1540 * xmlFreeAttribute:
1541 * @elem: An attribute
1542 *
1543 * Deallocate the memory used by an attribute definition
1544 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001545static void
Owen Taylor3473f882001-02-23 17:55:21 +00001546xmlFreeAttribute(xmlAttributePtr attr) {
1547 if (attr == NULL) return;
1548 xmlUnlinkNode((xmlNodePtr) attr);
1549 if (attr->tree != NULL)
1550 xmlFreeEnumeration(attr->tree);
1551 if (attr->elem != NULL)
1552 xmlFree((xmlChar *) attr->elem);
1553 if (attr->name != NULL)
1554 xmlFree((xmlChar *) attr->name);
1555 if (attr->defaultValue != NULL)
1556 xmlFree((xmlChar *) attr->defaultValue);
1557 if (attr->prefix != NULL)
1558 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001559 xmlFree(attr);
1560}
1561
1562
1563/**
1564 * xmlAddAttributeDecl:
1565 * @ctxt: the validation context
1566 * @dtd: pointer to the DTD
1567 * @elem: the element name
1568 * @name: the attribute name
1569 * @ns: the attribute namespace prefix
1570 * @type: the attribute type
1571 * @def: the attribute default type
1572 * @defaultValue: the attribute default value
1573 * @tree: if it's an enumeration, the associated list
1574 *
1575 * Register a new attribute declaration
1576 * Note that @tree becomes the ownership of the DTD
1577 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001578 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001579 */
1580xmlAttributePtr
1581xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1582 const xmlChar *name, const xmlChar *ns,
1583 xmlAttributeType type, xmlAttributeDefault def,
1584 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1585 xmlAttributePtr ret;
1586 xmlAttributeTablePtr table;
1587 xmlElementPtr elemDef;
1588
1589 if (dtd == NULL) {
1590 xmlGenericError(xmlGenericErrorContext,
1591 "xmlAddAttributeDecl: dtd == NULL\n");
1592 xmlFreeEnumeration(tree);
1593 return(NULL);
1594 }
1595 if (name == NULL) {
1596 xmlGenericError(xmlGenericErrorContext,
1597 "xmlAddAttributeDecl: name == NULL\n");
1598 xmlFreeEnumeration(tree);
1599 return(NULL);
1600 }
1601 if (elem == NULL) {
1602 xmlGenericError(xmlGenericErrorContext,
1603 "xmlAddAttributeDecl: elem == NULL\n");
1604 xmlFreeEnumeration(tree);
1605 return(NULL);
1606 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001607
Owen Taylor3473f882001-02-23 17:55:21 +00001608 /*
1609 * Check the type and possibly the default value.
1610 */
1611 switch (type) {
1612 case XML_ATTRIBUTE_CDATA:
1613 break;
1614 case XML_ATTRIBUTE_ID:
1615 break;
1616 case XML_ATTRIBUTE_IDREF:
1617 break;
1618 case XML_ATTRIBUTE_IDREFS:
1619 break;
1620 case XML_ATTRIBUTE_ENTITY:
1621 break;
1622 case XML_ATTRIBUTE_ENTITIES:
1623 break;
1624 case XML_ATTRIBUTE_NMTOKEN:
1625 break;
1626 case XML_ATTRIBUTE_NMTOKENS:
1627 break;
1628 case XML_ATTRIBUTE_ENUMERATION:
1629 break;
1630 case XML_ATTRIBUTE_NOTATION:
1631 break;
1632 default:
1633 xmlGenericError(xmlGenericErrorContext,
1634 "xmlAddAttributeDecl: unknown type %d\n", type);
1635 xmlFreeEnumeration(tree);
1636 return(NULL);
1637 }
1638 if ((defaultValue != NULL) &&
1639 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001640 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001641 elem, name, defaultValue);
1642 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001643 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001644 }
1645
1646 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001647 * Check first that an attribute defined in the external subset wasn't
1648 * already defined in the internal subset
1649 */
1650 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1651 (dtd->doc->intSubset != NULL) &&
1652 (dtd->doc->intSubset->attributes != NULL)) {
1653 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1654 if (ret != NULL)
1655 return(NULL);
1656 }
1657
1658 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001659 * Create the Attribute table if needed.
1660 */
1661 table = (xmlAttributeTablePtr) dtd->attributes;
1662 if (table == NULL) {
1663 table = xmlCreateAttributeTable();
1664 dtd->attributes = (void *) table;
1665 }
1666 if (table == NULL) {
1667 xmlGenericError(xmlGenericErrorContext,
1668 "xmlAddAttributeDecl: Table creation failed!\n");
1669 return(NULL);
1670 }
1671
1672
1673 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1674 if (ret == NULL) {
1675 xmlGenericError(xmlGenericErrorContext,
1676 "xmlAddAttributeDecl: out of memory\n");
1677 return(NULL);
1678 }
1679 memset(ret, 0, sizeof(xmlAttribute));
1680 ret->type = XML_ATTRIBUTE_DECL;
1681
1682 /*
1683 * fill the structure.
1684 */
1685 ret->atype = type;
1686 ret->name = xmlStrdup(name);
1687 ret->prefix = xmlStrdup(ns);
1688 ret->elem = xmlStrdup(elem);
1689 ret->def = def;
1690 ret->tree = tree;
1691 if (defaultValue != NULL)
1692 ret->defaultValue = xmlStrdup(defaultValue);
1693
1694 /*
1695 * Validity Check:
1696 * Search the DTD for previous declarations of the ATTLIST
1697 */
1698 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1699 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001700 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001701 */
1702 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001703 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001704 name, elem);
1705 xmlFreeAttribute(ret);
1706 return(NULL);
1707 }
1708
1709 /*
1710 * Validity Check:
1711 * Multiple ID per element
1712 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001713 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001714 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001715
Owen Taylor3473f882001-02-23 17:55:21 +00001716 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001717 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001718 VERROR(ctxt->userData,
1719 "Element %s has too may ID attributes defined : %s\n",
1720 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001721 ctxt->valid = 0;
1722 }
1723
Daniel Veillard48da9102001-08-07 01:10:10 +00001724 /*
1725 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001726 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001727 */
1728 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1729 ((ret->prefix != NULL &&
1730 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1731 ret->nexth = elemDef->attributes;
1732 elemDef->attributes = ret;
1733 } else {
1734 xmlAttributePtr tmp = elemDef->attributes;
1735
1736 while ((tmp != NULL) &&
1737 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1738 ((ret->prefix != NULL &&
1739 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1740 if (tmp->nexth == NULL)
1741 break;
1742 tmp = tmp->nexth;
1743 }
1744 if (tmp != NULL) {
1745 ret->nexth = tmp->nexth;
1746 tmp->nexth = ret;
1747 } else {
1748 ret->nexth = elemDef->attributes;
1749 elemDef->attributes = ret;
1750 }
1751 }
Owen Taylor3473f882001-02-23 17:55:21 +00001752 }
1753
1754 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001755 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001756 */
1757 ret->parent = dtd;
1758 ret->doc = dtd->doc;
1759 if (dtd->last == NULL) {
1760 dtd->children = dtd->last = (xmlNodePtr) ret;
1761 } else {
1762 dtd->last->next = (xmlNodePtr) ret;
1763 ret->prev = dtd->last;
1764 dtd->last = (xmlNodePtr) ret;
1765 }
1766 return(ret);
1767}
1768
1769/**
1770 * xmlFreeAttributeTable:
1771 * @table: An attribute table
1772 *
1773 * Deallocate the memory used by an entities hash table.
1774 */
1775void
1776xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1777 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1778}
1779
1780/**
1781 * xmlCopyAttribute:
1782 * @attr: An attribute
1783 *
1784 * Build a copy of an attribute.
1785 *
1786 * Returns the new xmlAttributePtr or NULL in case of error.
1787 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001788static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001789xmlCopyAttribute(xmlAttributePtr attr) {
1790 xmlAttributePtr cur;
1791
1792 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1793 if (cur == NULL) {
1794 xmlGenericError(xmlGenericErrorContext,
1795 "xmlCopyAttribute: out of memory !\n");
1796 return(NULL);
1797 }
1798 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001799 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001800 cur->atype = attr->atype;
1801 cur->def = attr->def;
1802 cur->tree = xmlCopyEnumeration(attr->tree);
1803 if (attr->elem != NULL)
1804 cur->elem = xmlStrdup(attr->elem);
1805 if (attr->name != NULL)
1806 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001807 if (attr->prefix != NULL)
1808 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001809 if (attr->defaultValue != NULL)
1810 cur->defaultValue = xmlStrdup(attr->defaultValue);
1811 return(cur);
1812}
1813
1814/**
1815 * xmlCopyAttributeTable:
1816 * @table: An attribute table
1817 *
1818 * Build a copy of an attribute table.
1819 *
1820 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1821 */
1822xmlAttributeTablePtr
1823xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1824 return((xmlAttributeTablePtr) xmlHashCopy(table,
1825 (xmlHashCopier) xmlCopyAttribute));
1826}
1827
1828/**
1829 * xmlDumpAttributeDecl:
1830 * @buf: the XML buffer output
1831 * @attr: An attribute declaration
1832 *
1833 * This will dump the content of the attribute declaration as an XML
1834 * DTD definition
1835 */
1836void
1837xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1838 xmlBufferWriteChar(buf, "<!ATTLIST ");
1839 xmlBufferWriteCHAR(buf, attr->elem);
1840 xmlBufferWriteChar(buf, " ");
1841 if (attr->prefix != NULL) {
1842 xmlBufferWriteCHAR(buf, attr->prefix);
1843 xmlBufferWriteChar(buf, ":");
1844 }
1845 xmlBufferWriteCHAR(buf, attr->name);
1846 switch (attr->atype) {
1847 case XML_ATTRIBUTE_CDATA:
1848 xmlBufferWriteChar(buf, " CDATA");
1849 break;
1850 case XML_ATTRIBUTE_ID:
1851 xmlBufferWriteChar(buf, " ID");
1852 break;
1853 case XML_ATTRIBUTE_IDREF:
1854 xmlBufferWriteChar(buf, " IDREF");
1855 break;
1856 case XML_ATTRIBUTE_IDREFS:
1857 xmlBufferWriteChar(buf, " IDREFS");
1858 break;
1859 case XML_ATTRIBUTE_ENTITY:
1860 xmlBufferWriteChar(buf, " ENTITY");
1861 break;
1862 case XML_ATTRIBUTE_ENTITIES:
1863 xmlBufferWriteChar(buf, " ENTITIES");
1864 break;
1865 case XML_ATTRIBUTE_NMTOKEN:
1866 xmlBufferWriteChar(buf, " NMTOKEN");
1867 break;
1868 case XML_ATTRIBUTE_NMTOKENS:
1869 xmlBufferWriteChar(buf, " NMTOKENS");
1870 break;
1871 case XML_ATTRIBUTE_ENUMERATION:
1872 xmlBufferWriteChar(buf, " (");
1873 xmlDumpEnumeration(buf, attr->tree);
1874 break;
1875 case XML_ATTRIBUTE_NOTATION:
1876 xmlBufferWriteChar(buf, " NOTATION (");
1877 xmlDumpEnumeration(buf, attr->tree);
1878 break;
1879 default:
1880 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001881 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001882 attr->atype);
1883 }
1884 switch (attr->def) {
1885 case XML_ATTRIBUTE_NONE:
1886 break;
1887 case XML_ATTRIBUTE_REQUIRED:
1888 xmlBufferWriteChar(buf, " #REQUIRED");
1889 break;
1890 case XML_ATTRIBUTE_IMPLIED:
1891 xmlBufferWriteChar(buf, " #IMPLIED");
1892 break;
1893 case XML_ATTRIBUTE_FIXED:
1894 xmlBufferWriteChar(buf, " #FIXED");
1895 break;
1896 default:
1897 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001898 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001899 attr->def);
1900 }
1901 if (attr->defaultValue != NULL) {
1902 xmlBufferWriteChar(buf, " ");
1903 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1904 }
1905 xmlBufferWriteChar(buf, ">\n");
1906}
1907
1908/**
1909 * xmlDumpAttributeTable:
1910 * @buf: the XML buffer output
1911 * @table: An attribute table
1912 *
1913 * This will dump the content of the attribute table as an XML DTD definition
1914 */
1915void
1916xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1917 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1918}
1919
1920/************************************************************************
1921 * *
1922 * NOTATIONs *
1923 * *
1924 ************************************************************************/
1925/**
1926 * xmlCreateNotationTable:
1927 *
1928 * create and initialize an empty notation hash table.
1929 *
1930 * Returns the xmlNotationTablePtr just created or NULL in case
1931 * of error.
1932 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001933static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001934xmlCreateNotationTable(void) {
1935 return(xmlHashCreate(0));
1936}
1937
1938/**
1939 * xmlFreeNotation:
1940 * @not: A notation
1941 *
1942 * Deallocate the memory used by an notation definition
1943 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001944static void
Owen Taylor3473f882001-02-23 17:55:21 +00001945xmlFreeNotation(xmlNotationPtr nota) {
1946 if (nota == NULL) return;
1947 if (nota->name != NULL)
1948 xmlFree((xmlChar *) nota->name);
1949 if (nota->PublicID != NULL)
1950 xmlFree((xmlChar *) nota->PublicID);
1951 if (nota->SystemID != NULL)
1952 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001953 xmlFree(nota);
1954}
1955
1956
1957/**
1958 * xmlAddNotationDecl:
1959 * @dtd: pointer to the DTD
1960 * @ctxt: the validation context
1961 * @name: the entity name
1962 * @PublicID: the public identifier or NULL
1963 * @SystemID: the system identifier or NULL
1964 *
1965 * Register a new notation declaration
1966 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001967 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001968 */
1969xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001970xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001971 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001972 const xmlChar *PublicID, const xmlChar *SystemID) {
1973 xmlNotationPtr ret;
1974 xmlNotationTablePtr table;
1975
1976 if (dtd == NULL) {
1977 xmlGenericError(xmlGenericErrorContext,
1978 "xmlAddNotationDecl: dtd == NULL\n");
1979 return(NULL);
1980 }
1981 if (name == NULL) {
1982 xmlGenericError(xmlGenericErrorContext,
1983 "xmlAddNotationDecl: name == NULL\n");
1984 return(NULL);
1985 }
1986 if ((PublicID == NULL) && (SystemID == NULL)) {
1987 xmlGenericError(xmlGenericErrorContext,
1988 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001989 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001990 }
1991
1992 /*
1993 * Create the Notation table if needed.
1994 */
1995 table = (xmlNotationTablePtr) dtd->notations;
1996 if (table == NULL)
1997 dtd->notations = table = xmlCreateNotationTable();
1998 if (table == NULL) {
1999 xmlGenericError(xmlGenericErrorContext,
2000 "xmlAddNotationDecl: Table creation failed!\n");
2001 return(NULL);
2002 }
2003
2004 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2005 if (ret == NULL) {
2006 xmlGenericError(xmlGenericErrorContext,
2007 "xmlAddNotationDecl: out of memory\n");
2008 return(NULL);
2009 }
2010 memset(ret, 0, sizeof(xmlNotation));
2011
2012 /*
2013 * fill the structure.
2014 */
2015 ret->name = xmlStrdup(name);
2016 if (SystemID != NULL)
2017 ret->SystemID = xmlStrdup(SystemID);
2018 if (PublicID != NULL)
2019 ret->PublicID = xmlStrdup(PublicID);
2020
2021 /*
2022 * Validity Check:
2023 * Check the DTD for previous declarations of the ATTLIST
2024 */
2025 if (xmlHashAddEntry(table, name, ret)) {
2026 xmlGenericError(xmlGenericErrorContext,
2027 "xmlAddNotationDecl: %s already defined\n", name);
2028 xmlFreeNotation(ret);
2029 return(NULL);
2030 }
2031 return(ret);
2032}
2033
2034/**
2035 * xmlFreeNotationTable:
2036 * @table: An notation table
2037 *
2038 * Deallocate the memory used by an entities hash table.
2039 */
2040void
2041xmlFreeNotationTable(xmlNotationTablePtr table) {
2042 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2043}
2044
2045/**
2046 * xmlCopyNotation:
2047 * @nota: A notation
2048 *
2049 * Build a copy of a notation.
2050 *
2051 * Returns the new xmlNotationPtr or NULL in case of error.
2052 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002053static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002054xmlCopyNotation(xmlNotationPtr nota) {
2055 xmlNotationPtr cur;
2056
2057 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2058 if (cur == NULL) {
2059 xmlGenericError(xmlGenericErrorContext,
2060 "xmlCopyNotation: out of memory !\n");
2061 return(NULL);
2062 }
2063 if (nota->name != NULL)
2064 cur->name = xmlStrdup(nota->name);
2065 else
2066 cur->name = NULL;
2067 if (nota->PublicID != NULL)
2068 cur->PublicID = xmlStrdup(nota->PublicID);
2069 else
2070 cur->PublicID = NULL;
2071 if (nota->SystemID != NULL)
2072 cur->SystemID = xmlStrdup(nota->SystemID);
2073 else
2074 cur->SystemID = NULL;
2075 return(cur);
2076}
2077
2078/**
2079 * xmlCopyNotationTable:
2080 * @table: A notation table
2081 *
2082 * Build a copy of a notation table.
2083 *
2084 * Returns the new xmlNotationTablePtr or NULL in case of error.
2085 */
2086xmlNotationTablePtr
2087xmlCopyNotationTable(xmlNotationTablePtr table) {
2088 return((xmlNotationTablePtr) xmlHashCopy(table,
2089 (xmlHashCopier) xmlCopyNotation));
2090}
2091
2092/**
2093 * xmlDumpNotationDecl:
2094 * @buf: the XML buffer output
2095 * @nota: A notation declaration
2096 *
2097 * This will dump the content the notation declaration as an XML DTD definition
2098 */
2099void
2100xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2101 xmlBufferWriteChar(buf, "<!NOTATION ");
2102 xmlBufferWriteCHAR(buf, nota->name);
2103 if (nota->PublicID != NULL) {
2104 xmlBufferWriteChar(buf, " PUBLIC ");
2105 xmlBufferWriteQuotedString(buf, nota->PublicID);
2106 if (nota->SystemID != NULL) {
2107 xmlBufferWriteChar(buf, " ");
2108 xmlBufferWriteCHAR(buf, nota->SystemID);
2109 }
2110 } else {
2111 xmlBufferWriteChar(buf, " SYSTEM ");
2112 xmlBufferWriteCHAR(buf, nota->SystemID);
2113 }
2114 xmlBufferWriteChar(buf, " >\n");
2115}
2116
2117/**
2118 * xmlDumpNotationTable:
2119 * @buf: the XML buffer output
2120 * @table: A notation table
2121 *
2122 * This will dump the content of the notation table as an XML DTD definition
2123 */
2124void
2125xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2126 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2127}
2128
2129/************************************************************************
2130 * *
2131 * IDs *
2132 * *
2133 ************************************************************************/
2134/**
2135 * xmlCreateIDTable:
2136 *
2137 * create and initialize an empty id hash table.
2138 *
2139 * Returns the xmlIDTablePtr just created or NULL in case
2140 * of error.
2141 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002142static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002143xmlCreateIDTable(void) {
2144 return(xmlHashCreate(0));
2145}
2146
2147/**
2148 * xmlFreeID:
2149 * @not: A id
2150 *
2151 * Deallocate the memory used by an id definition
2152 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002153static void
Owen Taylor3473f882001-02-23 17:55:21 +00002154xmlFreeID(xmlIDPtr id) {
2155 if (id == NULL) return;
2156 if (id->value != NULL)
2157 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002158 if (id->name != NULL)
2159 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002160 xmlFree(id);
2161}
2162
2163/**
2164 * xmlAddID:
2165 * @ctxt: the validation context
2166 * @doc: pointer to the document
2167 * @value: the value name
2168 * @attr: the attribute holding the ID
2169 *
2170 * Register a new id declaration
2171 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002172 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002173 */
2174xmlIDPtr
2175xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2176 xmlAttrPtr attr) {
2177 xmlIDPtr ret;
2178 xmlIDTablePtr table;
2179
2180 if (doc == NULL) {
2181 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002182 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002183 return(NULL);
2184 }
2185 if (value == NULL) {
2186 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002187 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002188 return(NULL);
2189 }
2190 if (attr == NULL) {
2191 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002192 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002193 return(NULL);
2194 }
2195
2196 /*
2197 * Create the ID table if needed.
2198 */
2199 table = (xmlIDTablePtr) doc->ids;
2200 if (table == NULL)
2201 doc->ids = table = xmlCreateIDTable();
2202 if (table == NULL) {
2203 xmlGenericError(xmlGenericErrorContext,
2204 "xmlAddID: Table creation failed!\n");
2205 return(NULL);
2206 }
2207
2208 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2209 if (ret == NULL) {
2210 xmlGenericError(xmlGenericErrorContext,
2211 "xmlAddID: out of memory\n");
2212 return(NULL);
2213 }
2214
2215 /*
2216 * fill the structure.
2217 */
2218 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002219 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2220 /*
2221 * Operating in streaming mode, attr is gonna disapear
2222 */
2223 ret->name = xmlStrdup(attr->name);
2224 ret->attr = NULL;
2225 } else {
2226 ret->attr = attr;
2227 ret->name = NULL;
2228 }
2229 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002230
2231 if (xmlHashAddEntry(table, value, ret) < 0) {
2232 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002233 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002234 */
Daniel Veillard76575762002-09-05 14:21:15 +00002235 if (ctxt != NULL) {
2236 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002237 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002238 }
Owen Taylor3473f882001-02-23 17:55:21 +00002239 xmlFreeID(ret);
2240 return(NULL);
2241 }
2242 return(ret);
2243}
2244
2245/**
2246 * xmlFreeIDTable:
2247 * @table: An id table
2248 *
2249 * Deallocate the memory used by an ID hash table.
2250 */
2251void
2252xmlFreeIDTable(xmlIDTablePtr table) {
2253 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2254}
2255
2256/**
2257 * xmlIsID:
2258 * @doc: the document
2259 * @elem: the element carrying the attribute
2260 * @attr: the attribute
2261 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002262 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002263 * then this is done if DTD loading has been requested. In the case
2264 * of HTML documents parsed with the HTML parser, then ID detection is
2265 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002266 *
2267 * Returns 0 or 1 depending on the lookup result
2268 */
2269int
2270xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2271 if (doc == NULL) return(0);
2272 if (attr == NULL) return(0);
2273 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2274 return(0);
2275 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2276 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2277 (xmlStrEqual(BAD_CAST "name", attr->name)))
2278 return(1);
2279 return(0);
2280 } else {
2281 xmlAttributePtr attrDecl;
2282
2283 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002284 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2285 /*
2286 * TODO: this sucks ... recomputing this every time is stupid
2287 */
2288 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2289 xmlChar *fullname;
2290
2291 fullname = xmlMalloc(len);
2292 if (fullname == NULL)
2293 return(0);
2294 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2295 (char *) elem->name);
2296 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2297 attr->name);
2298 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2299 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2300 attr->name);
2301 xmlFree(fullname);
2302 } else {
2303 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2304 attr->name);
2305 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2306 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2307 attr->name);
2308 }
Owen Taylor3473f882001-02-23 17:55:21 +00002309
2310 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2311 return(1);
2312 }
2313 return(0);
2314}
2315
2316/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002317 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002318 * @doc: the document
2319 * @attr: the attribute
2320 *
2321 * Remove the given attribute from the ID table maintained internally.
2322 *
2323 * Returns -1 if the lookup failed and 0 otherwise
2324 */
2325int
2326xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2327 xmlAttrPtr cur;
2328 xmlIDTablePtr table;
2329 xmlChar *ID;
2330
2331 if (doc == NULL) return(-1);
2332 if (attr == NULL) return(-1);
2333 table = (xmlIDTablePtr) doc->ids;
2334 if (table == NULL)
2335 return(-1);
2336
2337 if (attr == NULL)
2338 return(-1);
2339 ID = xmlNodeListGetString(doc, attr->children, 1);
2340 if (ID == NULL)
2341 return(-1);
2342 cur = xmlHashLookup(table, ID);
2343 if (cur != attr) {
2344 xmlFree(ID);
2345 return(-1);
2346 }
2347 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2348 xmlFree(ID);
2349 return(0);
2350}
2351
2352/**
2353 * xmlGetID:
2354 * @doc: pointer to the document
2355 * @ID: the ID value
2356 *
2357 * Search the attribute declaring the given ID
2358 *
2359 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2360 */
2361xmlAttrPtr
2362xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2363 xmlIDTablePtr table;
2364 xmlIDPtr id;
2365
2366 if (doc == NULL) {
2367 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2368 return(NULL);
2369 }
2370
2371 if (ID == NULL) {
2372 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2373 return(NULL);
2374 }
2375
2376 table = (xmlIDTablePtr) doc->ids;
2377 if (table == NULL)
2378 return(NULL);
2379
2380 id = xmlHashLookup(table, ID);
2381 if (id == NULL)
2382 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002383 if (id->attr == NULL) {
2384 /*
2385 * We are operating on a stream, return a well known reference
2386 * since the attribute node doesn't exist anymore
2387 */
2388 return((xmlAttrPtr) doc);
2389 }
Owen Taylor3473f882001-02-23 17:55:21 +00002390 return(id->attr);
2391}
2392
2393/************************************************************************
2394 * *
2395 * Refs *
2396 * *
2397 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002398typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002399{
2400 xmlListPtr l;
2401 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002402} xmlRemoveMemo;
2403
2404typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2405
2406typedef struct xmlValidateMemo_t
2407{
2408 xmlValidCtxtPtr ctxt;
2409 const xmlChar *name;
2410} xmlValidateMemo;
2411
2412typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002413
2414/**
2415 * xmlCreateRefTable:
2416 *
2417 * create and initialize an empty ref hash table.
2418 *
2419 * Returns the xmlRefTablePtr just created or NULL in case
2420 * of error.
2421 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002422static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002423xmlCreateRefTable(void) {
2424 return(xmlHashCreate(0));
2425}
2426
2427/**
2428 * xmlFreeRef:
2429 * @lk: A list link
2430 *
2431 * Deallocate the memory used by a ref definition
2432 */
2433static void
2434xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002435 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2436 if (ref == NULL) return;
2437 if (ref->value != NULL)
2438 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002439 if (ref->name != NULL)
2440 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002441 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002442}
2443
2444/**
2445 * xmlFreeRefList:
2446 * @list_ref: A list of references.
2447 *
2448 * Deallocate the memory used by a list of references
2449 */
2450static void
2451xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002452 if (list_ref == NULL) return;
2453 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002454}
2455
2456/**
2457 * xmlWalkRemoveRef:
2458 * @data: Contents of current link
2459 * @user: Value supplied by the user
2460 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002461 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002462 */
2463static int
2464xmlWalkRemoveRef(const void *data, const void *user)
2465{
Daniel Veillard37721922001-05-04 15:21:12 +00002466 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2467 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2468 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002469
Daniel Veillard37721922001-05-04 15:21:12 +00002470 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2471 xmlListRemoveFirst(ref_list, (void *)data);
2472 return 0;
2473 }
2474 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002475}
2476
2477/**
2478 * xmlAddRef:
2479 * @ctxt: the validation context
2480 * @doc: pointer to the document
2481 * @value: the value name
2482 * @attr: the attribute holding the Ref
2483 *
2484 * Register a new ref declaration
2485 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002486 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002487 */
2488xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002489xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002490 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002491 xmlRefPtr ret;
2492 xmlRefTablePtr table;
2493 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002494
Daniel Veillard37721922001-05-04 15:21:12 +00002495 if (doc == NULL) {
2496 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002497 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002498 return(NULL);
2499 }
2500 if (value == NULL) {
2501 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002502 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002503 return(NULL);
2504 }
2505 if (attr == NULL) {
2506 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002507 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002508 return(NULL);
2509 }
Owen Taylor3473f882001-02-23 17:55:21 +00002510
Daniel Veillard37721922001-05-04 15:21:12 +00002511 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002512 * Create the Ref table if needed.
2513 */
Daniel Veillard37721922001-05-04 15:21:12 +00002514 table = (xmlRefTablePtr) doc->refs;
2515 if (table == NULL)
2516 doc->refs = table = xmlCreateRefTable();
2517 if (table == NULL) {
2518 xmlGenericError(xmlGenericErrorContext,
2519 "xmlAddRef: Table creation failed!\n");
2520 return(NULL);
2521 }
Owen Taylor3473f882001-02-23 17:55:21 +00002522
Daniel Veillard37721922001-05-04 15:21:12 +00002523 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2524 if (ret == NULL) {
2525 xmlGenericError(xmlGenericErrorContext,
2526 "xmlAddRef: out of memory\n");
2527 return(NULL);
2528 }
Owen Taylor3473f882001-02-23 17:55:21 +00002529
Daniel Veillard37721922001-05-04 15:21:12 +00002530 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002531 * fill the structure.
2532 */
Daniel Veillard37721922001-05-04 15:21:12 +00002533 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002534 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2535 /*
2536 * Operating in streaming mode, attr is gonna disapear
2537 */
2538 ret->name = xmlStrdup(attr->name);
2539 ret->attr = NULL;
2540 } else {
2541 ret->name = NULL;
2542 ret->attr = attr;
2543 }
2544 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002545
Daniel Veillard37721922001-05-04 15:21:12 +00002546 /* To add a reference :-
2547 * References are maintained as a list of references,
2548 * Lookup the entry, if no entry create new nodelist
2549 * Add the owning node to the NodeList
2550 * Return the ref
2551 */
Owen Taylor3473f882001-02-23 17:55:21 +00002552
Daniel Veillard37721922001-05-04 15:21:12 +00002553 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2554 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2555 xmlGenericError(xmlGenericErrorContext,
2556 "xmlAddRef: Reference list creation failed!\n");
2557 return(NULL);
2558 }
2559 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2560 xmlListDelete(ref_list);
2561 xmlGenericError(xmlGenericErrorContext,
2562 "xmlAddRef: Reference list insertion failed!\n");
2563 return(NULL);
2564 }
2565 }
2566 xmlListInsert(ref_list, ret);
2567 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002568}
2569
2570/**
2571 * xmlFreeRefTable:
2572 * @table: An ref table
2573 *
2574 * Deallocate the memory used by an Ref hash table.
2575 */
2576void
2577xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002578 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002579}
2580
2581/**
2582 * xmlIsRef:
2583 * @doc: the document
2584 * @elem: the element carrying the attribute
2585 * @attr: the attribute
2586 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002587 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002588 * then this is simple, otherwise we use an heuristic: name Ref (upper
2589 * or lowercase).
2590 *
2591 * Returns 0 or 1 depending on the lookup result
2592 */
2593int
2594xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002595 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2596 return(0);
2597 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2598 /* TODO @@@ */
2599 return(0);
2600 } else {
2601 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002602
Daniel Veillard37721922001-05-04 15:21:12 +00002603 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2604 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2605 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2606 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002607
Daniel Veillard37721922001-05-04 15:21:12 +00002608 if ((attrDecl != NULL) &&
2609 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2610 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2611 return(1);
2612 }
2613 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002614}
2615
2616/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002617 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002618 * @doc: the document
2619 * @attr: the attribute
2620 *
2621 * Remove the given attribute from the Ref table maintained internally.
2622 *
2623 * Returns -1 if the lookup failed and 0 otherwise
2624 */
2625int
2626xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002627 xmlListPtr ref_list;
2628 xmlRefTablePtr table;
2629 xmlChar *ID;
2630 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002631
Daniel Veillard37721922001-05-04 15:21:12 +00002632 if (doc == NULL) return(-1);
2633 if (attr == NULL) return(-1);
2634 table = (xmlRefTablePtr) doc->refs;
2635 if (table == NULL)
2636 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002637
Daniel Veillard37721922001-05-04 15:21:12 +00002638 if (attr == NULL)
2639 return(-1);
2640 ID = xmlNodeListGetString(doc, attr->children, 1);
2641 if (ID == NULL)
2642 return(-1);
2643 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002644
Daniel Veillard37721922001-05-04 15:21:12 +00002645 if(ref_list == NULL) {
2646 xmlFree(ID);
2647 return (-1);
2648 }
2649 /* At this point, ref_list refers to a list of references which
2650 * have the same key as the supplied attr. Our list of references
2651 * is ordered by reference address and we don't have that information
2652 * here to use when removing. We'll have to walk the list and
2653 * check for a matching attribute, when we find one stop the walk
2654 * and remove the entry.
2655 * The list is ordered by reference, so that means we don't have the
2656 * key. Passing the list and the reference to the walker means we
2657 * will have enough data to be able to remove the entry.
2658 */
2659 target.l = ref_list;
2660 target.ap = attr;
2661
2662 /* Remove the supplied attr from our list */
2663 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002664
Daniel Veillard37721922001-05-04 15:21:12 +00002665 /*If the list is empty then remove the list entry in the hash */
2666 if (xmlListEmpty(ref_list))
2667 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2668 xmlFreeRefList);
2669 xmlFree(ID);
2670 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002671}
2672
2673/**
2674 * xmlGetRefs:
2675 * @doc: pointer to the document
2676 * @ID: the ID value
2677 *
2678 * Find the set of references for the supplied ID.
2679 *
2680 * Returns NULL if not found, otherwise node set for the ID.
2681 */
2682xmlListPtr
2683xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002684 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002685
Daniel Veillard37721922001-05-04 15:21:12 +00002686 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002687 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002688 return(NULL);
2689 }
Owen Taylor3473f882001-02-23 17:55:21 +00002690
Daniel Veillard37721922001-05-04 15:21:12 +00002691 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002692 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002693 return(NULL);
2694 }
Owen Taylor3473f882001-02-23 17:55:21 +00002695
Daniel Veillard37721922001-05-04 15:21:12 +00002696 table = (xmlRefTablePtr) doc->refs;
2697 if (table == NULL)
2698 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002699
Daniel Veillard37721922001-05-04 15:21:12 +00002700 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002701}
2702
2703/************************************************************************
2704 * *
2705 * Routines for validity checking *
2706 * *
2707 ************************************************************************/
2708
2709/**
2710 * xmlGetDtdElementDesc:
2711 * @dtd: a pointer to the DtD to search
2712 * @name: the element name
2713 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002714 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002715 *
2716 * returns the xmlElementPtr if found or NULL
2717 */
2718
2719xmlElementPtr
2720xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2721 xmlElementTablePtr table;
2722 xmlElementPtr cur;
2723 xmlChar *uqname = NULL, *prefix = NULL;
2724
2725 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002726 if (dtd->elements == NULL)
2727 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002728 table = (xmlElementTablePtr) dtd->elements;
2729
2730 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002731 if (uqname != NULL)
2732 name = uqname;
2733 cur = xmlHashLookup2(table, name, prefix);
2734 if (prefix != NULL) xmlFree(prefix);
2735 if (uqname != NULL) xmlFree(uqname);
2736 return(cur);
2737}
2738/**
2739 * xmlGetDtdElementDesc2:
2740 * @dtd: a pointer to the DtD to search
2741 * @name: the element name
2742 * @create: create an empty description if not found
2743 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002744 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002745 *
2746 * returns the xmlElementPtr if found or NULL
2747 */
2748
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002749static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002750xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2751 xmlElementTablePtr table;
2752 xmlElementPtr cur;
2753 xmlChar *uqname = NULL, *prefix = NULL;
2754
2755 if (dtd == NULL) return(NULL);
2756 if (dtd->elements == NULL) {
2757 if (!create)
2758 return(NULL);
2759 /*
2760 * Create the Element table if needed.
2761 */
2762 table = (xmlElementTablePtr) dtd->elements;
2763 if (table == NULL) {
2764 table = xmlCreateElementTable();
2765 dtd->elements = (void *) table;
2766 }
2767 if (table == NULL) {
2768 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002769 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002770 return(NULL);
2771 }
2772 }
2773 table = (xmlElementTablePtr) dtd->elements;
2774
2775 uqname = xmlSplitQName2(name, &prefix);
2776 if (uqname != NULL)
2777 name = uqname;
2778 cur = xmlHashLookup2(table, name, prefix);
2779 if ((cur == NULL) && (create)) {
2780 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2781 if (cur == NULL) {
2782 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002783 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002784 return(NULL);
2785 }
2786 memset(cur, 0, sizeof(xmlElement));
2787 cur->type = XML_ELEMENT_DECL;
2788
2789 /*
2790 * fill the structure.
2791 */
2792 cur->name = xmlStrdup(name);
2793 cur->prefix = xmlStrdup(prefix);
2794 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2795
2796 xmlHashAddEntry2(table, name, prefix, cur);
2797 }
2798 if (prefix != NULL) xmlFree(prefix);
2799 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002800 return(cur);
2801}
2802
2803/**
2804 * xmlGetDtdQElementDesc:
2805 * @dtd: a pointer to the DtD to search
2806 * @name: the element name
2807 * @prefix: the element namespace prefix
2808 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002809 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002810 *
2811 * returns the xmlElementPtr if found or NULL
2812 */
2813
Daniel Veillard48da9102001-08-07 01:10:10 +00002814xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002815xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2816 const xmlChar *prefix) {
2817 xmlElementTablePtr table;
2818
2819 if (dtd == NULL) return(NULL);
2820 if (dtd->elements == NULL) return(NULL);
2821 table = (xmlElementTablePtr) dtd->elements;
2822
2823 return(xmlHashLookup2(table, name, prefix));
2824}
2825
2826/**
2827 * xmlGetDtdAttrDesc:
2828 * @dtd: a pointer to the DtD to search
2829 * @elem: the element name
2830 * @name: the attribute name
2831 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002832 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002833 * this element.
2834 *
2835 * returns the xmlAttributePtr if found or NULL
2836 */
2837
2838xmlAttributePtr
2839xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2840 xmlAttributeTablePtr table;
2841 xmlAttributePtr cur;
2842 xmlChar *uqname = NULL, *prefix = NULL;
2843
2844 if (dtd == NULL) return(NULL);
2845 if (dtd->attributes == NULL) return(NULL);
2846
2847 table = (xmlAttributeTablePtr) dtd->attributes;
2848 if (table == NULL)
2849 return(NULL);
2850
2851 uqname = xmlSplitQName2(name, &prefix);
2852
2853 if (uqname != NULL) {
2854 cur = xmlHashLookup3(table, uqname, prefix, elem);
2855 if (prefix != NULL) xmlFree(prefix);
2856 if (uqname != NULL) xmlFree(uqname);
2857 } else
2858 cur = xmlHashLookup3(table, name, NULL, elem);
2859 return(cur);
2860}
2861
2862/**
2863 * xmlGetDtdQAttrDesc:
2864 * @dtd: a pointer to the DtD to search
2865 * @elem: the element name
2866 * @name: the attribute name
2867 * @prefix: the attribute namespace prefix
2868 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002869 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002870 * this element.
2871 *
2872 * returns the xmlAttributePtr if found or NULL
2873 */
2874
Daniel Veillard48da9102001-08-07 01:10:10 +00002875xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002876xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2877 const xmlChar *prefix) {
2878 xmlAttributeTablePtr table;
2879
2880 if (dtd == NULL) return(NULL);
2881 if (dtd->attributes == NULL) return(NULL);
2882 table = (xmlAttributeTablePtr) dtd->attributes;
2883
2884 return(xmlHashLookup3(table, name, prefix, elem));
2885}
2886
2887/**
2888 * xmlGetDtdNotationDesc:
2889 * @dtd: a pointer to the DtD to search
2890 * @name: the notation name
2891 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002892 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002893 *
2894 * returns the xmlNotationPtr if found or NULL
2895 */
2896
2897xmlNotationPtr
2898xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2899 xmlNotationTablePtr table;
2900
2901 if (dtd == NULL) return(NULL);
2902 if (dtd->notations == NULL) return(NULL);
2903 table = (xmlNotationTablePtr) dtd->notations;
2904
2905 return(xmlHashLookup(table, name));
2906}
2907
2908/**
2909 * xmlValidateNotationUse:
2910 * @ctxt: the validation context
2911 * @doc: the document
2912 * @notationName: the notation name to check
2913 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002914 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002915 * - [ VC: Notation Declared ]
2916 *
2917 * returns 1 if valid or 0 otherwise
2918 */
2919
2920int
2921xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2922 const xmlChar *notationName) {
2923 xmlNotationPtr notaDecl;
2924 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2925
2926 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2927 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2928 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2929
2930 if (notaDecl == NULL) {
2931 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2932 notationName);
2933 return(0);
2934 }
2935 return(1);
2936}
2937
2938/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002939 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002940 * @doc: the document
2941 * @name: the element name
2942 *
2943 * Search in the DtDs whether an element accept Mixed content (or ANY)
2944 * basically if it is supposed to accept text childs
2945 *
2946 * returns 0 if no, 1 if yes, and -1 if no element description is available
2947 */
2948
2949int
2950xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2951 xmlElementPtr elemDecl;
2952
2953 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2954
2955 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2956 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2957 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2958 if (elemDecl == NULL) return(-1);
2959 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002960 case XML_ELEMENT_TYPE_UNDEFINED:
2961 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002962 case XML_ELEMENT_TYPE_ELEMENT:
2963 return(0);
2964 case XML_ELEMENT_TYPE_EMPTY:
2965 /*
2966 * return 1 for EMPTY since we want VC error to pop up
2967 * on <empty> </empty> for example
2968 */
2969 case XML_ELEMENT_TYPE_ANY:
2970 case XML_ELEMENT_TYPE_MIXED:
2971 return(1);
2972 }
2973 return(1);
2974}
2975
2976/**
2977 * xmlValidateNameValue:
2978 * @value: an Name value
2979 *
2980 * Validate that the given value match Name production
2981 *
2982 * returns 1 if valid or 0 otherwise
2983 */
2984
Daniel Veillard9b731d72002-04-14 12:56:08 +00002985int
Owen Taylor3473f882001-02-23 17:55:21 +00002986xmlValidateNameValue(const xmlChar *value) {
2987 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002988 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002989
2990 if (value == NULL) return(0);
2991 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002992 val = xmlStringCurrentChar(NULL, cur, &len);
2993 cur += len;
2994 if (!IS_LETTER(val) && (val != '_') &&
2995 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002996 return(0);
2997 }
2998
Daniel Veillardd8224e02002-01-13 15:43:22 +00002999 val = xmlStringCurrentChar(NULL, cur, &len);
3000 cur += len;
3001 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3002 (val == '.') || (val == '-') ||
3003 (val == '_') || (val == ':') ||
3004 (IS_COMBINING(val)) ||
3005 (IS_EXTENDER(val))) {
3006 val = xmlStringCurrentChar(NULL, cur, &len);
3007 cur += len;
3008 }
Owen Taylor3473f882001-02-23 17:55:21 +00003009
Daniel Veillardd8224e02002-01-13 15:43:22 +00003010 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003011
3012 return(1);
3013}
3014
3015/**
3016 * xmlValidateNamesValue:
3017 * @value: an Names value
3018 *
3019 * Validate that the given value match Names production
3020 *
3021 * returns 1 if valid or 0 otherwise
3022 */
3023
Daniel Veillard9b731d72002-04-14 12:56:08 +00003024int
Owen Taylor3473f882001-02-23 17:55:21 +00003025xmlValidateNamesValue(const xmlChar *value) {
3026 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003027 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003028
3029 if (value == NULL) return(0);
3030 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003031 val = xmlStringCurrentChar(NULL, cur, &len);
3032 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003033
Daniel Veillardd8224e02002-01-13 15:43:22 +00003034 if (!IS_LETTER(val) && (val != '_') &&
3035 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003036 return(0);
3037 }
3038
Daniel Veillardd8224e02002-01-13 15:43:22 +00003039 val = xmlStringCurrentChar(NULL, cur, &len);
3040 cur += len;
3041 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3042 (val == '.') || (val == '-') ||
3043 (val == '_') || (val == ':') ||
3044 (IS_COMBINING(val)) ||
3045 (IS_EXTENDER(val))) {
3046 val = xmlStringCurrentChar(NULL, cur, &len);
3047 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003048 }
3049
Daniel Veillardd8224e02002-01-13 15:43:22 +00003050 while (IS_BLANK(val)) {
3051 while (IS_BLANK(val)) {
3052 val = xmlStringCurrentChar(NULL, cur, &len);
3053 cur += len;
3054 }
3055
3056 if (!IS_LETTER(val) && (val != '_') &&
3057 (val != ':')) {
3058 return(0);
3059 }
3060 val = xmlStringCurrentChar(NULL, cur, &len);
3061 cur += len;
3062
3063 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3064 (val == '.') || (val == '-') ||
3065 (val == '_') || (val == ':') ||
3066 (IS_COMBINING(val)) ||
3067 (IS_EXTENDER(val))) {
3068 val = xmlStringCurrentChar(NULL, cur, &len);
3069 cur += len;
3070 }
3071 }
3072
3073 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003074
3075 return(1);
3076}
3077
3078/**
3079 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003080 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003081 *
3082 * Validate that the given value match Nmtoken production
3083 *
3084 * [ VC: Name Token ]
3085 *
3086 * returns 1 if valid or 0 otherwise
3087 */
3088
Daniel Veillard9b731d72002-04-14 12:56:08 +00003089int
Owen Taylor3473f882001-02-23 17:55:21 +00003090xmlValidateNmtokenValue(const xmlChar *value) {
3091 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003092 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003093
3094 if (value == NULL) return(0);
3095 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003096 val = xmlStringCurrentChar(NULL, cur, &len);
3097 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003098
Daniel Veillardd8224e02002-01-13 15:43:22 +00003099 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3100 (val != '.') && (val != '-') &&
3101 (val != '_') && (val != ':') &&
3102 (!IS_COMBINING(val)) &&
3103 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003104 return(0);
3105
Daniel Veillardd8224e02002-01-13 15:43:22 +00003106 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3107 (val == '.') || (val == '-') ||
3108 (val == '_') || (val == ':') ||
3109 (IS_COMBINING(val)) ||
3110 (IS_EXTENDER(val))) {
3111 val = xmlStringCurrentChar(NULL, cur, &len);
3112 cur += len;
3113 }
Owen Taylor3473f882001-02-23 17:55:21 +00003114
Daniel Veillardd8224e02002-01-13 15:43:22 +00003115 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003116
3117 return(1);
3118}
3119
3120/**
3121 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003122 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003123 *
3124 * Validate that the given value match Nmtokens production
3125 *
3126 * [ VC: Name Token ]
3127 *
3128 * returns 1 if valid or 0 otherwise
3129 */
3130
Daniel Veillard9b731d72002-04-14 12:56:08 +00003131int
Owen Taylor3473f882001-02-23 17:55:21 +00003132xmlValidateNmtokensValue(const xmlChar *value) {
3133 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003134 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003135
3136 if (value == NULL) return(0);
3137 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003138 val = xmlStringCurrentChar(NULL, cur, &len);
3139 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003140
Daniel Veillardd8224e02002-01-13 15:43:22 +00003141 while (IS_BLANK(val)) {
3142 val = xmlStringCurrentChar(NULL, cur, &len);
3143 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003144 }
3145
Daniel Veillardd8224e02002-01-13 15:43:22 +00003146 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3147 (val != '.') && (val != '-') &&
3148 (val != '_') && (val != ':') &&
3149 (!IS_COMBINING(val)) &&
3150 (!IS_EXTENDER(val)))
3151 return(0);
3152
3153 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3154 (val == '.') || (val == '-') ||
3155 (val == '_') || (val == ':') ||
3156 (IS_COMBINING(val)) ||
3157 (IS_EXTENDER(val))) {
3158 val = xmlStringCurrentChar(NULL, cur, &len);
3159 cur += len;
3160 }
3161
3162 while (IS_BLANK(val)) {
3163 while (IS_BLANK(val)) {
3164 val = xmlStringCurrentChar(NULL, cur, &len);
3165 cur += len;
3166 }
3167 if (val == 0) return(1);
3168
3169 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3170 (val != '.') && (val != '-') &&
3171 (val != '_') && (val != ':') &&
3172 (!IS_COMBINING(val)) &&
3173 (!IS_EXTENDER(val)))
3174 return(0);
3175
3176 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3177 (val == '.') || (val == '-') ||
3178 (val == '_') || (val == ':') ||
3179 (IS_COMBINING(val)) ||
3180 (IS_EXTENDER(val))) {
3181 val = xmlStringCurrentChar(NULL, cur, &len);
3182 cur += len;
3183 }
3184 }
3185
3186 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003187
3188 return(1);
3189}
3190
3191/**
3192 * xmlValidateNotationDecl:
3193 * @ctxt: the validation context
3194 * @doc: a document instance
3195 * @nota: a notation definition
3196 *
3197 * Try to validate a single notation definition
3198 * basically it does the following checks as described by the
3199 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003200 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003201 * But this function get called anyway ...
3202 *
3203 * returns 1 if valid or 0 otherwise
3204 */
3205
3206int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003207xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3208 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003209 int ret = 1;
3210
3211 return(ret);
3212}
3213
3214/**
3215 * xmlValidateAttributeValue:
3216 * @type: an attribute type
3217 * @value: an attribute value
3218 *
3219 * Validate that the given attribute value match the proper production
3220 *
3221 * [ VC: ID ]
3222 * Values of type ID must match the Name production....
3223 *
3224 * [ VC: IDREF ]
3225 * Values of type IDREF must match the Name production, and values
3226 * of type IDREFS must match Names ...
3227 *
3228 * [ VC: Entity Name ]
3229 * Values of type ENTITY must match the Name production, values
3230 * of type ENTITIES must match Names ...
3231 *
3232 * [ VC: Name Token ]
3233 * Values of type NMTOKEN must match the Nmtoken production; values
3234 * of type NMTOKENS must match Nmtokens.
3235 *
3236 * returns 1 if valid or 0 otherwise
3237 */
3238
3239int
3240xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3241 switch (type) {
3242 case XML_ATTRIBUTE_ENTITIES:
3243 case XML_ATTRIBUTE_IDREFS:
3244 return(xmlValidateNamesValue(value));
3245 case XML_ATTRIBUTE_ENTITY:
3246 case XML_ATTRIBUTE_IDREF:
3247 case XML_ATTRIBUTE_ID:
3248 case XML_ATTRIBUTE_NOTATION:
3249 return(xmlValidateNameValue(value));
3250 case XML_ATTRIBUTE_NMTOKENS:
3251 case XML_ATTRIBUTE_ENUMERATION:
3252 return(xmlValidateNmtokensValue(value));
3253 case XML_ATTRIBUTE_NMTOKEN:
3254 return(xmlValidateNmtokenValue(value));
3255 case XML_ATTRIBUTE_CDATA:
3256 break;
3257 }
3258 return(1);
3259}
3260
3261/**
3262 * xmlValidateAttributeValue2:
3263 * @ctxt: the validation context
3264 * @doc: the document
3265 * @name: the attribute name (used for error reporting only)
3266 * @type: the attribute type
3267 * @value: the attribute value
3268 *
3269 * Validate that the given attribute value match a given type.
3270 * This typically cannot be done before having finished parsing
3271 * the subsets.
3272 *
3273 * [ VC: IDREF ]
3274 * Values of type IDREF must match one of the declared IDs
3275 * Values of type IDREFS must match a sequence of the declared IDs
3276 * each Name must match the value of an ID attribute on some element
3277 * in the XML document; i.e. IDREF values must match the value of
3278 * some ID attribute
3279 *
3280 * [ VC: Entity Name ]
3281 * Values of type ENTITY must match one declared entity
3282 * Values of type ENTITIES must match a sequence of declared entities
3283 *
3284 * [ VC: Notation Attributes ]
3285 * all notation names in the declaration must be declared.
3286 *
3287 * returns 1 if valid or 0 otherwise
3288 */
3289
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003290static int
Owen Taylor3473f882001-02-23 17:55:21 +00003291xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3292 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3293 int ret = 1;
3294 switch (type) {
3295 case XML_ATTRIBUTE_IDREFS:
3296 case XML_ATTRIBUTE_IDREF:
3297 case XML_ATTRIBUTE_ID:
3298 case XML_ATTRIBUTE_NMTOKENS:
3299 case XML_ATTRIBUTE_ENUMERATION:
3300 case XML_ATTRIBUTE_NMTOKEN:
3301 case XML_ATTRIBUTE_CDATA:
3302 break;
3303 case XML_ATTRIBUTE_ENTITY: {
3304 xmlEntityPtr ent;
3305
3306 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003307 if ((ent == NULL) && (doc->standalone == 1)) {
3308 doc->standalone = 0;
3309 ent = xmlGetDocEntity(doc, value);
3310 if (ent != NULL) {
3311 VERROR(ctxt->userData,
3312"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3313 name, value);
3314 /* WAIT to get answer from the Core WG on this
3315 ret = 0;
3316 */
3317 }
3318 }
Owen Taylor3473f882001-02-23 17:55:21 +00003319 if (ent == NULL) {
3320 VERROR(ctxt->userData,
3321 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3322 name, value);
3323 ret = 0;
3324 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3325 VERROR(ctxt->userData,
3326 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3327 name, value);
3328 ret = 0;
3329 }
3330 break;
3331 }
3332 case XML_ATTRIBUTE_ENTITIES: {
3333 xmlChar *dup, *nam = NULL, *cur, save;
3334 xmlEntityPtr ent;
3335
3336 dup = xmlStrdup(value);
3337 if (dup == NULL)
3338 return(0);
3339 cur = dup;
3340 while (*cur != 0) {
3341 nam = cur;
3342 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3343 save = *cur;
3344 *cur = 0;
3345 ent = xmlGetDocEntity(doc, nam);
3346 if (ent == NULL) {
3347 VERROR(ctxt->userData,
3348 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3349 name, nam);
3350 ret = 0;
3351 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3352 VERROR(ctxt->userData,
3353 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3354 name, nam);
3355 ret = 0;
3356 }
3357 if (save == 0)
3358 break;
3359 *cur = save;
3360 while (IS_BLANK(*cur)) cur++;
3361 }
3362 xmlFree(dup);
3363 break;
3364 }
3365 case XML_ATTRIBUTE_NOTATION: {
3366 xmlNotationPtr nota;
3367
3368 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3369 if ((nota == NULL) && (doc->extSubset != NULL))
3370 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3371
3372 if (nota == NULL) {
3373 VERROR(ctxt->userData,
3374 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3375 name, value);
3376 ret = 0;
3377 }
3378 break;
3379 }
3380 }
3381 return(ret);
3382}
3383
3384/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003385 * xmlValidCtxtNormalizeAttributeValue:
3386 * @ctxt: the validation context
3387 * @doc: the document
3388 * @elem: the parent
3389 * @name: the attribute name
3390 * @value: the attribute value
3391 * @ctxt: the validation context or NULL
3392 *
3393 * Does the validation related extra step of the normalization of attribute
3394 * values:
3395 *
3396 * If the declared value is not CDATA, then the XML processor must further
3397 * process the normalized attribute value by discarding any leading and
3398 * trailing space (#x20) characters, and by replacing sequences of space
3399 * (#x20) characters by single space (#x20) character.
3400 *
3401 * Also check VC: Standalone Document Declaration in P32, and update
3402 * ctxt->valid accordingly
3403 *
3404 * returns a new normalized string if normalization is needed, NULL otherwise
3405 * the caller must free the returned value.
3406 */
3407
3408xmlChar *
3409xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3410 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3411 xmlChar *ret, *dst;
3412 const xmlChar *src;
3413 xmlAttributePtr attrDecl = NULL;
3414 int extsubset = 0;
3415
3416 if (doc == NULL) return(NULL);
3417 if (elem == NULL) return(NULL);
3418 if (name == NULL) return(NULL);
3419 if (value == NULL) return(NULL);
3420
3421 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3422 xmlChar qname[500];
3423 snprintf((char *) qname, sizeof(qname), "%s:%s",
3424 elem->ns->prefix, elem->name);
3425 qname[sizeof(qname) - 1] = 0;
3426 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3427 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3428 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3429 if (attrDecl != NULL)
3430 extsubset = 1;
3431 }
3432 }
3433 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3434 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3435 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3436 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3437 if (attrDecl != NULL)
3438 extsubset = 1;
3439 }
3440
3441 if (attrDecl == NULL)
3442 return(NULL);
3443 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3444 return(NULL);
3445
3446 ret = xmlStrdup(value);
3447 if (ret == NULL)
3448 return(NULL);
3449 src = value;
3450 dst = ret;
3451 while (*src == 0x20) src++;
3452 while (*src != 0) {
3453 if (*src == 0x20) {
3454 while (*src == 0x20) src++;
3455 if (*src != 0)
3456 *dst++ = 0x20;
3457 } else {
3458 *dst++ = *src++;
3459 }
3460 }
3461 *dst = 0;
3462 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3463 VERROR(ctxt->userData,
3464"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3465 name, elem->name);
3466 ctxt->valid = 0;
3467 }
3468 return(ret);
3469}
3470
3471/**
Owen Taylor3473f882001-02-23 17:55:21 +00003472 * xmlValidNormalizeAttributeValue:
3473 * @doc: the document
3474 * @elem: the parent
3475 * @name: the attribute name
3476 * @value: the attribute value
3477 *
3478 * Does the validation related extra step of the normalization of attribute
3479 * values:
3480 *
3481 * If the declared value is not CDATA, then the XML processor must further
3482 * process the normalized attribute value by discarding any leading and
3483 * trailing space (#x20) characters, and by replacing sequences of space
3484 * (#x20) characters by single space (#x20) character.
3485 *
3486 * returns a new normalized string if normalization is needed, NULL otherwise
3487 * the caller must free the returned value.
3488 */
3489
3490xmlChar *
3491xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3492 const xmlChar *name, const xmlChar *value) {
3493 xmlChar *ret, *dst;
3494 const xmlChar *src;
3495 xmlAttributePtr attrDecl = NULL;
3496
3497 if (doc == NULL) return(NULL);
3498 if (elem == NULL) return(NULL);
3499 if (name == NULL) return(NULL);
3500 if (value == NULL) return(NULL);
3501
3502 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3503 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003504 snprintf((char *) qname, sizeof(qname), "%s:%s",
3505 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003506 qname[sizeof(qname) - 1] = 0;
3507 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3508 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3509 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3510 }
3511 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3512 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3513 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3514
3515 if (attrDecl == NULL)
3516 return(NULL);
3517 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3518 return(NULL);
3519
3520 ret = xmlStrdup(value);
3521 if (ret == NULL)
3522 return(NULL);
3523 src = value;
3524 dst = ret;
3525 while (*src == 0x20) src++;
3526 while (*src != 0) {
3527 if (*src == 0x20) {
3528 while (*src == 0x20) src++;
3529 if (*src != 0)
3530 *dst++ = 0x20;
3531 } else {
3532 *dst++ = *src++;
3533 }
3534 }
3535 *dst = 0;
3536 return(ret);
3537}
3538
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003539static void
Owen Taylor3473f882001-02-23 17:55:21 +00003540xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003541 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003542 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3543}
3544
3545/**
3546 * xmlValidateAttributeDecl:
3547 * @ctxt: the validation context
3548 * @doc: a document instance
3549 * @attr: an attribute definition
3550 *
3551 * Try to validate a single attribute definition
3552 * basically it does the following checks as described by the
3553 * XML-1.0 recommendation:
3554 * - [ VC: Attribute Default Legal ]
3555 * - [ VC: Enumeration ]
3556 * - [ VC: ID Attribute Default ]
3557 *
3558 * The ID/IDREF uniqueness and matching are done separately
3559 *
3560 * returns 1 if valid or 0 otherwise
3561 */
3562
3563int
3564xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3565 xmlAttributePtr attr) {
3566 int ret = 1;
3567 int val;
3568 CHECK_DTD;
3569 if(attr == NULL) return(1);
3570
3571 /* Attribute Default Legal */
3572 /* Enumeration */
3573 if (attr->defaultValue != NULL) {
3574 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3575 if (val == 0) {
3576 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003577 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003578 attr->name, attr->elem);
3579 }
3580 ret &= val;
3581 }
3582
3583 /* ID Attribute Default */
3584 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3585 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3586 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3587 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003588 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003589 attr->name, attr->elem);
3590 ret = 0;
3591 }
3592
3593 /* One ID per Element Type */
3594 if (attr->atype == XML_ATTRIBUTE_ID) {
3595 int nbId;
3596
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003597 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003598 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3599 attr->elem);
3600 if (elem != NULL) {
3601 nbId = xmlScanIDAttributeDecl(NULL, elem);
3602 } else {
3603 xmlAttributeTablePtr table;
3604
3605 /*
3606 * The attribute may be declared in the internal subset and the
3607 * element in the external subset.
3608 */
3609 nbId = 0;
3610 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3611 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3612 xmlValidateAttributeIdCallback, &nbId);
3613 }
3614 if (nbId > 1) {
3615 VERROR(ctxt->userData,
3616 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3617 attr->elem, nbId, attr->name);
3618 } else if (doc->extSubset != NULL) {
3619 int extId = 0;
3620 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3621 if (elem != NULL) {
3622 extId = xmlScanIDAttributeDecl(NULL, elem);
3623 }
3624 if (extId > 1) {
3625 VERROR(ctxt->userData,
3626 "Element %s has %d ID attribute defined in the external subset : %s\n",
3627 attr->elem, extId, attr->name);
3628 } else if (extId + nbId > 1) {
3629 VERROR(ctxt->userData,
3630"Element %s has ID attributes defined in the internal and external subset : %s\n",
3631 attr->elem, attr->name);
3632 }
3633 }
3634 }
3635
3636 /* Validity Constraint: Enumeration */
3637 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3638 xmlEnumerationPtr tree = attr->tree;
3639 while (tree != NULL) {
3640 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3641 tree = tree->next;
3642 }
3643 if (tree == NULL) {
3644 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003645"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003646 attr->defaultValue, attr->name, attr->elem);
3647 ret = 0;
3648 }
3649 }
3650
3651 return(ret);
3652}
3653
3654/**
3655 * xmlValidateElementDecl:
3656 * @ctxt: the validation context
3657 * @doc: a document instance
3658 * @elem: an element definition
3659 *
3660 * Try to validate a single element definition
3661 * basically it does the following checks as described by the
3662 * XML-1.0 recommendation:
3663 * - [ VC: One ID per Element Type ]
3664 * - [ VC: No Duplicate Types ]
3665 * - [ VC: Unique Element Type Declaration ]
3666 *
3667 * returns 1 if valid or 0 otherwise
3668 */
3669
3670int
3671xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3672 xmlElementPtr elem) {
3673 int ret = 1;
3674 xmlElementPtr tst;
3675
3676 CHECK_DTD;
3677
3678 if (elem == NULL) return(1);
3679
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003680#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003681#ifdef LIBXML_REGEXP_ENABLED
3682 /* Build the regexp associated to the content model */
3683 ret = xmlValidBuildContentModel(ctxt, elem);
3684#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003685#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003686
Owen Taylor3473f882001-02-23 17:55:21 +00003687 /* No Duplicate Types */
3688 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3689 xmlElementContentPtr cur, next;
3690 const xmlChar *name;
3691
3692 cur = elem->content;
3693 while (cur != NULL) {
3694 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3695 if (cur->c1 == NULL) break;
3696 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3697 name = cur->c1->name;
3698 next = cur->c2;
3699 while (next != NULL) {
3700 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3701 if (xmlStrEqual(next->name, name)) {
3702 VERROR(ctxt->userData,
3703 "Definition of %s has duplicate references of %s\n",
3704 elem->name, name);
3705 ret = 0;
3706 }
3707 break;
3708 }
3709 if (next->c1 == NULL) break;
3710 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3711 if (xmlStrEqual(next->c1->name, name)) {
3712 VERROR(ctxt->userData,
3713 "Definition of %s has duplicate references of %s\n",
3714 elem->name, name);
3715 ret = 0;
3716 }
3717 next = next->c2;
3718 }
3719 }
3720 cur = cur->c2;
3721 }
3722 }
3723
3724 /* VC: Unique Element Type Declaration */
3725 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003726 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003727 ((tst->prefix == elem->prefix) ||
3728 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003729 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003730 VERROR(ctxt->userData, "Redefinition of element %s\n",
3731 elem->name);
3732 ret = 0;
3733 }
3734 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003735 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003736 ((tst->prefix == elem->prefix) ||
3737 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003738 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003739 VERROR(ctxt->userData, "Redefinition of element %s\n",
3740 elem->name);
3741 ret = 0;
3742 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003743 /* One ID per Element Type
3744 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003745 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3746 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003747 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003748 return(ret);
3749}
3750
3751/**
3752 * xmlValidateOneAttribute:
3753 * @ctxt: the validation context
3754 * @doc: a document instance
3755 * @elem: an element instance
3756 * @attr: an attribute instance
3757 * @value: the attribute value (without entities processing)
3758 *
3759 * Try to validate a single attribute for an element
3760 * basically it does the following checks as described by the
3761 * XML-1.0 recommendation:
3762 * - [ VC: Attribute Value Type ]
3763 * - [ VC: Fixed Attribute Default ]
3764 * - [ VC: Entity Name ]
3765 * - [ VC: Name Token ]
3766 * - [ VC: ID ]
3767 * - [ VC: IDREF ]
3768 * - [ VC: Entity Name ]
3769 * - [ VC: Notation Attributes ]
3770 *
3771 * The ID/IDREF uniqueness and matching are done separately
3772 *
3773 * returns 1 if valid or 0 otherwise
3774 */
3775
3776int
3777xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3778 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3779 /* xmlElementPtr elemDecl; */
3780 xmlAttributePtr attrDecl = NULL;
3781 int val;
3782 int ret = 1;
3783
3784 CHECK_DTD;
3785 if ((elem == NULL) || (elem->name == NULL)) return(0);
3786 if ((attr == NULL) || (attr->name == NULL)) return(0);
3787
3788 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3789 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003790 snprintf((char *) qname, sizeof(qname), "%s:%s",
3791 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003792 qname[sizeof(qname) - 1] = 0;
3793 if (attr->ns != NULL) {
3794 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3795 attr->name, attr->ns->prefix);
3796 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3797 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3798 attr->name, attr->ns->prefix);
3799 } else {
3800 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3801 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3802 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3803 qname, attr->name);
3804 }
3805 }
3806 if (attrDecl == NULL) {
3807 if (attr->ns != NULL) {
3808 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3809 attr->name, attr->ns->prefix);
3810 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3811 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3812 attr->name, attr->ns->prefix);
3813 } else {
3814 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3815 elem->name, attr->name);
3816 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3817 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3818 elem->name, attr->name);
3819 }
3820 }
3821
3822
3823 /* Validity Constraint: Attribute Value Type */
3824 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003825 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003826 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003827 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003828 attr->name, elem->name);
3829 return(0);
3830 }
3831 attr->atype = attrDecl->atype;
3832
3833 val = xmlValidateAttributeValue(attrDecl->atype, value);
3834 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003835 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003836 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003837 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003838 attr->name, elem->name);
3839 ret = 0;
3840 }
3841
3842 /* Validity constraint: Fixed Attribute Default */
3843 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3844 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003845 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003846 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003847 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003848 attr->name, elem->name, attrDecl->defaultValue);
3849 ret = 0;
3850 }
3851 }
3852
3853 /* Validity Constraint: ID uniqueness */
3854 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3855 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3856 ret = 0;
3857 }
3858
3859 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3860 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3861 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3862 ret = 0;
3863 }
3864
3865 /* Validity Constraint: Notation Attributes */
3866 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3867 xmlEnumerationPtr tree = attrDecl->tree;
3868 xmlNotationPtr nota;
3869
3870 /* First check that the given NOTATION was declared */
3871 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3872 if (nota == NULL)
3873 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3874
3875 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003876 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003877 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003878 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003879 value, attr->name, elem->name);
3880 ret = 0;
3881 }
3882
3883 /* Second, verify that it's among the list */
3884 while (tree != NULL) {
3885 if (xmlStrEqual(tree->name, value)) break;
3886 tree = tree->next;
3887 }
3888 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003889 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003890 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003891"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003892 value, attr->name, elem->name);
3893 ret = 0;
3894 }
3895 }
3896
3897 /* Validity Constraint: Enumeration */
3898 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3899 xmlEnumerationPtr tree = attrDecl->tree;
3900 while (tree != NULL) {
3901 if (xmlStrEqual(tree->name, value)) break;
3902 tree = tree->next;
3903 }
3904 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003905 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003906 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003907 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003908 value, attr->name, elem->name);
3909 ret = 0;
3910 }
3911 }
3912
3913 /* Fixed Attribute Default */
3914 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3915 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003916 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003917 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003918 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003919 attr->name, elem->name, attrDecl->defaultValue);
3920 ret = 0;
3921 }
3922
3923 /* Extra check for the attribute value */
3924 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3925 attrDecl->atype, value);
3926
3927 return(ret);
3928}
3929
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003930/**
3931 * xmlValidateOneNamespace:
3932 * @ctxt: the validation context
3933 * @doc: a document instance
3934 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003935 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003936 * @ns: an namespace declaration instance
3937 * @value: the attribute value (without entities processing)
3938 *
3939 * Try to validate a single namespace declaration for an element
3940 * basically it does the following checks as described by the
3941 * XML-1.0 recommendation:
3942 * - [ VC: Attribute Value Type ]
3943 * - [ VC: Fixed Attribute Default ]
3944 * - [ VC: Entity Name ]
3945 * - [ VC: Name Token ]
3946 * - [ VC: ID ]
3947 * - [ VC: IDREF ]
3948 * - [ VC: Entity Name ]
3949 * - [ VC: Notation Attributes ]
3950 *
3951 * The ID/IDREF uniqueness and matching are done separately
3952 *
3953 * returns 1 if valid or 0 otherwise
3954 */
3955
3956int
3957xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3958xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3959 /* xmlElementPtr elemDecl; */
3960 xmlAttributePtr attrDecl = NULL;
3961 int val;
3962 int ret = 1;
3963
3964 CHECK_DTD;
3965 if ((elem == NULL) || (elem->name == NULL)) return(0);
3966 if ((ns == NULL) || (ns->href == NULL)) return(0);
3967
3968 if (prefix != NULL) {
3969 xmlChar qname[500];
3970 snprintf((char *) qname, sizeof(qname), "%s:%s",
3971 prefix, elem->name);
3972 qname[sizeof(qname) - 1] = 0;
3973 if (ns->prefix != NULL) {
3974 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3975 ns->prefix, BAD_CAST "xmlns");
3976 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3977 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3978 ns->prefix, BAD_CAST "xmlns");
3979 } else {
3980 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname,
3981 BAD_CAST "xmlns");
3982 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3983 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname,
3984 BAD_CAST "xmlns");
3985 }
3986 }
3987 if (attrDecl == NULL) {
3988 if (ns->prefix != NULL) {
3989 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3990 ns->prefix, BAD_CAST "xmlns");
3991 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3992 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3993 ns->prefix, BAD_CAST "xmlns");
3994 } else {
3995 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3996 elem->name, BAD_CAST "xmlns");
3997 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3998 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3999 elem->name, BAD_CAST "xmlns");
4000 }
4001 }
4002
4003
4004 /* Validity Constraint: Attribute Value Type */
4005 if (attrDecl == NULL) {
4006 VECTXT(ctxt, elem);
4007 if (ns->prefix != NULL) {
4008 VERROR(ctxt->userData,
4009 "No declaration for attribute xmlns:%s of element %s\n",
4010 ns->prefix, elem->name);
4011 } else {
4012 VERROR(ctxt->userData,
4013 "No declaration for attribute xmlns of element %s\n",
4014 elem->name);
4015 }
4016 return(0);
4017 }
4018
4019 val = xmlValidateAttributeValue(attrDecl->atype, value);
4020 if (val == 0) {
4021 VECTXT(ctxt, elem);
4022 if (ns->prefix != NULL) {
4023 VERROR(ctxt->userData,
4024 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4025 ns->prefix, elem->name);
4026 } else {
4027 VERROR(ctxt->userData,
4028 "Syntax of value for attribute xmlns of %s is not valid\n",
4029 elem->name);
4030 }
4031 ret = 0;
4032 }
4033
4034 /* Validity constraint: Fixed Attribute Default */
4035 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4036 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4037 VECTXT(ctxt, elem);
4038 if (ns->prefix != NULL) {
4039 VERROR(ctxt->userData,
4040 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4041 ns->prefix, elem->name, attrDecl->defaultValue);
4042 } else {
4043 VERROR(ctxt->userData,
4044 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4045 elem->name, attrDecl->defaultValue);
4046 }
4047 ret = 0;
4048 }
4049 }
4050
4051 /* Validity Constraint: ID uniqueness */
4052 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4053 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4054 ret = 0;
4055 }
4056
4057 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4058 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4059 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4060 ret = 0;
4061 }
4062
4063 /* Validity Constraint: Notation Attributes */
4064 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4065 xmlEnumerationPtr tree = attrDecl->tree;
4066 xmlNotationPtr nota;
4067
4068 /* First check that the given NOTATION was declared */
4069 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4070 if (nota == NULL)
4071 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4072
4073 if (nota == NULL) {
4074 VECTXT(ctxt, elem);
4075 if (ns->prefix != NULL) {
4076 VERROR(ctxt->userData,
4077 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4078 value, ns->prefix, elem->name);
4079 } else {
4080 VERROR(ctxt->userData,
4081 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4082 value, elem->name);
4083 }
4084 ret = 0;
4085 }
4086
4087 /* Second, verify that it's among the list */
4088 while (tree != NULL) {
4089 if (xmlStrEqual(tree->name, value)) break;
4090 tree = tree->next;
4091 }
4092 if (tree == NULL) {
4093 VECTXT(ctxt, elem);
4094 if (ns->prefix != NULL) {
4095 VERROR(ctxt->userData,
4096"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4097 value, ns->prefix, elem->name);
4098 } else {
4099 VERROR(ctxt->userData,
4100"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4101 value, elem->name);
4102 }
4103 ret = 0;
4104 }
4105 }
4106
4107 /* Validity Constraint: Enumeration */
4108 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4109 xmlEnumerationPtr tree = attrDecl->tree;
4110 while (tree != NULL) {
4111 if (xmlStrEqual(tree->name, value)) break;
4112 tree = tree->next;
4113 }
4114 if (tree == NULL) {
4115 VECTXT(ctxt, elem);
4116 if (ns->prefix != NULL) {
4117 VERROR(ctxt->userData,
4118"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4119 value, ns->prefix, elem->name);
4120 } else {
4121 VERROR(ctxt->userData,
4122"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4123 value, elem->name);
4124 }
4125 ret = 0;
4126 }
4127 }
4128
4129 /* Fixed Attribute Default */
4130 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4131 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4132 VECTXT(ctxt, elem);
4133 if (ns->prefix != NULL) {
4134 VERROR(ctxt->userData,
4135 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4136 ns->prefix, elem->name, attrDecl->defaultValue);
4137 } else {
4138 VERROR(ctxt->userData,
4139 "Value for attribute xmlns of %s must be \"%s\"\n",
4140 elem->name, attrDecl->defaultValue);
4141 }
4142 ret = 0;
4143 }
4144
4145 /* Extra check for the attribute value */
4146 if (ns->prefix != NULL) {
4147 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4148 attrDecl->atype, value);
4149 } else {
4150 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4151 attrDecl->atype, value);
4152 }
4153
4154 return(ret);
4155}
4156
Daniel Veillard118aed72002-09-24 14:13:13 +00004157#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004158/**
4159 * xmlValidateSkipIgnorable:
4160 * @ctxt: the validation context
4161 * @child: the child list
4162 *
4163 * Skip ignorable elements w.r.t. the validation process
4164 *
4165 * returns the first element to consider for validation of the content model
4166 */
4167
4168static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004169xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004170 while (child != NULL) {
4171 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004172 /* These things are ignored (skipped) during validation. */
4173 case XML_PI_NODE:
4174 case XML_COMMENT_NODE:
4175 case XML_XINCLUDE_START:
4176 case XML_XINCLUDE_END:
4177 child = child->next;
4178 break;
4179 case XML_TEXT_NODE:
4180 if (xmlIsBlankNode(child))
4181 child = child->next;
4182 else
4183 return(child);
4184 break;
4185 /* keep current node */
4186 default:
4187 return(child);
4188 }
4189 }
4190 return(child);
4191}
4192
4193/**
4194 * xmlValidateElementType:
4195 * @ctxt: the validation context
4196 *
4197 * Try to validate the content model of an element internal function
4198 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004199 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4200 * reference is found and -3 if the validation succeeded but
4201 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004202 */
4203
4204static int
4205xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004206 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004207 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004208
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004209 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004210 if ((NODE == NULL) && (CONT == NULL))
4211 return(1);
4212 if ((NODE == NULL) &&
4213 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4214 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4215 return(1);
4216 }
4217 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004218 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004219 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004220
4221 /*
4222 * We arrive here when more states need to be examined
4223 */
4224cont:
4225
4226 /*
4227 * We just recovered from a rollback generated by a possible
4228 * epsilon transition, go directly to the analysis phase
4229 */
4230 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004231 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004232 DEBUG_VALID_STATE(NODE, CONT)
4233 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004234 goto analyze;
4235 }
4236
4237 DEBUG_VALID_STATE(NODE, CONT)
4238 /*
4239 * we may have to save a backup state here. This is the equivalent
4240 * of handling epsilon transition in NFAs.
4241 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004242 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004243 ((CONT->parent == NULL) ||
4244 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004245 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004246 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004247 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004248 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004249 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4250 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004251 }
4252
4253
4254 /*
4255 * Check first if the content matches
4256 */
4257 switch (CONT->type) {
4258 case XML_ELEMENT_CONTENT_PCDATA:
4259 if (NODE == NULL) {
4260 DEBUG_VALID_MSG("pcdata failed no node");
4261 ret = 0;
4262 break;
4263 }
4264 if (NODE->type == XML_TEXT_NODE) {
4265 DEBUG_VALID_MSG("pcdata found, skip to next");
4266 /*
4267 * go to next element in the content model
4268 * skipping ignorable elems
4269 */
4270 do {
4271 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004272 NODE = xmlValidateSkipIgnorable(NODE);
4273 if ((NODE != NULL) &&
4274 (NODE->type == XML_ENTITY_REF_NODE))
4275 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004276 } while ((NODE != NULL) &&
4277 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004278 (NODE->type != XML_TEXT_NODE) &&
4279 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004280 ret = 1;
4281 break;
4282 } else {
4283 DEBUG_VALID_MSG("pcdata failed");
4284 ret = 0;
4285 break;
4286 }
4287 break;
4288 case XML_ELEMENT_CONTENT_ELEMENT:
4289 if (NODE == NULL) {
4290 DEBUG_VALID_MSG("element failed no node");
4291 ret = 0;
4292 break;
4293 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004294 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4295 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004296 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004297 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4298 ret = (CONT->prefix == NULL);
4299 } else if (CONT->prefix == NULL) {
4300 ret = 0;
4301 } else {
4302 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4303 }
4304 }
4305 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004306 DEBUG_VALID_MSG("element found, skip to next");
4307 /*
4308 * go to next element in the content model
4309 * skipping ignorable elems
4310 */
4311 do {
4312 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004313 NODE = xmlValidateSkipIgnorable(NODE);
4314 if ((NODE != NULL) &&
4315 (NODE->type == XML_ENTITY_REF_NODE))
4316 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004317 } while ((NODE != NULL) &&
4318 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004319 (NODE->type != XML_TEXT_NODE) &&
4320 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004321 } else {
4322 DEBUG_VALID_MSG("element failed");
4323 ret = 0;
4324 break;
4325 }
4326 break;
4327 case XML_ELEMENT_CONTENT_OR:
4328 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004329 * Small optimization.
4330 */
4331 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4332 if ((NODE == NULL) ||
4333 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4334 DEPTH++;
4335 CONT = CONT->c2;
4336 goto cont;
4337 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004338 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4339 ret = (CONT->c1->prefix == NULL);
4340 } else if (CONT->c1->prefix == NULL) {
4341 ret = 0;
4342 } else {
4343 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4344 }
4345 if (ret == 0) {
4346 DEPTH++;
4347 CONT = CONT->c2;
4348 goto cont;
4349 }
Daniel Veillard85349052001-04-20 13:48:21 +00004350 }
4351
4352 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004353 * save the second branch 'or' branch
4354 */
4355 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004356 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4357 OCCURS, ROLLBACK_OR) < 0)
4358 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004359 DEPTH++;
4360 CONT = CONT->c1;
4361 goto cont;
4362 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004363 /*
4364 * Small optimization.
4365 */
4366 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4367 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4368 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4369 if ((NODE == NULL) ||
4370 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4371 DEPTH++;
4372 CONT = CONT->c2;
4373 goto cont;
4374 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004375 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4376 ret = (CONT->c1->prefix == NULL);
4377 } else if (CONT->c1->prefix == NULL) {
4378 ret = 0;
4379 } else {
4380 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4381 }
4382 if (ret == 0) {
4383 DEPTH++;
4384 CONT = CONT->c2;
4385 goto cont;
4386 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004387 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004388 DEPTH++;
4389 CONT = CONT->c1;
4390 goto cont;
4391 }
4392
4393 /*
4394 * At this point handle going up in the tree
4395 */
4396 if (ret == -1) {
4397 DEBUG_VALID_MSG("error found returning");
4398 return(ret);
4399 }
4400analyze:
4401 while (CONT != NULL) {
4402 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004403 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004404 * this level.
4405 */
4406 if (ret == 0) {
4407 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004408 xmlNodePtr cur;
4409
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004410 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004411 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004412 DEBUG_VALID_MSG("Once branch failed, rollback");
4413 if (vstateVPop(ctxt) < 0 ) {
4414 DEBUG_VALID_MSG("exhaustion, failed");
4415 return(0);
4416 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004417 if (cur != ctxt->vstate->node)
4418 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004419 goto cont;
4420 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004421 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004422 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004423 DEBUG_VALID_MSG("Plus branch failed, rollback");
4424 if (vstateVPop(ctxt) < 0 ) {
4425 DEBUG_VALID_MSG("exhaustion, failed");
4426 return(0);
4427 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004428 if (cur != ctxt->vstate->node)
4429 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004430 goto cont;
4431 }
4432 DEBUG_VALID_MSG("Plus branch found");
4433 ret = 1;
4434 break;
4435 case XML_ELEMENT_CONTENT_MULT:
4436#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004437 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004438 DEBUG_VALID_MSG("Mult branch failed");
4439 } else {
4440 DEBUG_VALID_MSG("Mult branch found");
4441 }
4442#endif
4443 ret = 1;
4444 break;
4445 case XML_ELEMENT_CONTENT_OPT:
4446 DEBUG_VALID_MSG("Option branch failed");
4447 ret = 1;
4448 break;
4449 }
4450 } else {
4451 switch (CONT->ocur) {
4452 case XML_ELEMENT_CONTENT_OPT:
4453 DEBUG_VALID_MSG("Option branch succeeded");
4454 ret = 1;
4455 break;
4456 case XML_ELEMENT_CONTENT_ONCE:
4457 DEBUG_VALID_MSG("Once branch succeeded");
4458 ret = 1;
4459 break;
4460 case XML_ELEMENT_CONTENT_PLUS:
4461 if (STATE == ROLLBACK_PARENT) {
4462 DEBUG_VALID_MSG("Plus branch rollback");
4463 ret = 1;
4464 break;
4465 }
4466 if (NODE == NULL) {
4467 DEBUG_VALID_MSG("Plus branch exhausted");
4468 ret = 1;
4469 break;
4470 }
4471 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004472 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004473 goto cont;
4474 case XML_ELEMENT_CONTENT_MULT:
4475 if (STATE == ROLLBACK_PARENT) {
4476 DEBUG_VALID_MSG("Mult branch rollback");
4477 ret = 1;
4478 break;
4479 }
4480 if (NODE == NULL) {
4481 DEBUG_VALID_MSG("Mult branch exhausted");
4482 ret = 1;
4483 break;
4484 }
4485 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004486 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004487 goto cont;
4488 }
4489 }
4490 STATE = 0;
4491
4492 /*
4493 * Then act accordingly at the parent level
4494 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004495 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004496 if (CONT->parent == NULL)
4497 break;
4498
4499 switch (CONT->parent->type) {
4500 case XML_ELEMENT_CONTENT_PCDATA:
4501 DEBUG_VALID_MSG("Error: parent pcdata");
4502 return(-1);
4503 case XML_ELEMENT_CONTENT_ELEMENT:
4504 DEBUG_VALID_MSG("Error: parent element");
4505 return(-1);
4506 case XML_ELEMENT_CONTENT_OR:
4507 if (ret == 1) {
4508 DEBUG_VALID_MSG("Or succeeded");
4509 CONT = CONT->parent;
4510 DEPTH--;
4511 } else {
4512 DEBUG_VALID_MSG("Or failed");
4513 CONT = CONT->parent;
4514 DEPTH--;
4515 }
4516 break;
4517 case XML_ELEMENT_CONTENT_SEQ:
4518 if (ret == 0) {
4519 DEBUG_VALID_MSG("Sequence failed");
4520 CONT = CONT->parent;
4521 DEPTH--;
4522 } else if (CONT == CONT->parent->c1) {
4523 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4524 CONT = CONT->parent->c2;
4525 goto cont;
4526 } else {
4527 DEBUG_VALID_MSG("Sequence succeeded");
4528 CONT = CONT->parent;
4529 DEPTH--;
4530 }
4531 }
4532 }
4533 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004534 xmlNodePtr cur;
4535
4536 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004537 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4538 if (vstateVPop(ctxt) < 0 ) {
4539 DEBUG_VALID_MSG("exhaustion, failed");
4540 return(0);
4541 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004542 if (cur != ctxt->vstate->node)
4543 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004544 goto cont;
4545 }
4546 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004547 xmlNodePtr cur;
4548
4549 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004550 DEBUG_VALID_MSG("Failure, rollback");
4551 if (vstateVPop(ctxt) < 0 ) {
4552 DEBUG_VALID_MSG("exhaustion, failed");
4553 return(0);
4554 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004555 if (cur != ctxt->vstate->node)
4556 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004557 goto cont;
4558 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004559 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004560}
Daniel Veillard23e73572002-09-19 19:56:43 +00004561#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004562
4563/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004564 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004565 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004566 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004567 * @content: An element
4568 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4569 *
4570 * This will dump the list of elements to the buffer
4571 * Intended just for the debug routine
4572 */
4573static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004574xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004575 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004576 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004577
4578 if (node == NULL) return;
4579 if (glob) strcat(buf, "(");
4580 cur = node;
4581 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004582 len = strlen(buf);
4583 if (size - len < 50) {
4584 if ((size - len > 4) && (buf[len - 1] != '.'))
4585 strcat(buf, " ...");
4586 return;
4587 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004588 switch (cur->type) {
4589 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004590 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004591 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004592 if ((size - len > 4) && (buf[len - 1] != '.'))
4593 strcat(buf, " ...");
4594 return;
4595 }
4596 strcat(buf, (char *) cur->ns->prefix);
4597 strcat(buf, ":");
4598 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004599 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004600 if ((size - len > 4) && (buf[len - 1] != '.'))
4601 strcat(buf, " ...");
4602 return;
4603 }
4604 strcat(buf, (char *) cur->name);
4605 if (cur->next != NULL)
4606 strcat(buf, " ");
4607 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004608 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004609 if (xmlIsBlankNode(cur))
4610 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004611 case XML_CDATA_SECTION_NODE:
4612 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004613 strcat(buf, "CDATA");
4614 if (cur->next != NULL)
4615 strcat(buf, " ");
4616 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004617 case XML_ATTRIBUTE_NODE:
4618 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004619#ifdef LIBXML_DOCB_ENABLED
4620 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004621#endif
4622 case XML_HTML_DOCUMENT_NODE:
4623 case XML_DOCUMENT_TYPE_NODE:
4624 case XML_DOCUMENT_FRAG_NODE:
4625 case XML_NOTATION_NODE:
4626 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004627 strcat(buf, "???");
4628 if (cur->next != NULL)
4629 strcat(buf, " ");
4630 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004631 case XML_ENTITY_NODE:
4632 case XML_PI_NODE:
4633 case XML_DTD_NODE:
4634 case XML_COMMENT_NODE:
4635 case XML_ELEMENT_DECL:
4636 case XML_ATTRIBUTE_DECL:
4637 case XML_ENTITY_DECL:
4638 case XML_XINCLUDE_START:
4639 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004640 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004641 }
4642 cur = cur->next;
4643 }
4644 if (glob) strcat(buf, ")");
4645}
4646
4647/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004648 * xmlValidateElementContent:
4649 * @ctxt: the validation context
4650 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004651 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004652 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004653 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004654 *
4655 * Try to validate the content model of an element
4656 *
4657 * returns 1 if valid or 0 if not and -1 in case of error
4658 */
4659
4660static int
4661xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004662 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004663 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004664#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004665 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004666#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004667 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004668 xmlElementContentPtr cont;
4669 const xmlChar *name;
4670
4671 if (elemDecl == NULL)
4672 return(-1);
4673 cont = elemDecl->content;
4674 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004675
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004676#ifdef LIBXML_REGEXP_ENABLED
4677 /* Build the regexp associated to the content model */
4678 if (elemDecl->contModel == NULL)
4679 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4680 if (elemDecl->contModel == NULL) {
4681 ret = -1;
4682 } else {
4683 xmlRegExecCtxtPtr exec;
4684
Daniel Veillard01992e02002-10-09 10:20:30 +00004685 ctxt->nodeMax = 0;
4686 ctxt->nodeNr = 0;
4687 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004688 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4689 if (exec != NULL) {
4690 cur = child;
4691 while (cur != NULL) {
4692 switch (cur->type) {
4693 case XML_ENTITY_REF_NODE:
4694 /*
4695 * Push the current node to be able to roll back
4696 * and process within the entity
4697 */
4698 if ((cur->children != NULL) &&
4699 (cur->children->children != NULL)) {
4700 nodeVPush(ctxt, cur);
4701 cur = cur->children->children;
4702 continue;
4703 }
4704 break;
4705 case XML_TEXT_NODE:
4706 if (xmlIsBlankNode(cur))
4707 break;
4708 ret = 0;
4709 goto fail;
4710 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004711 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004712 ret = 0;
4713 goto fail;
4714 case XML_ELEMENT_NODE:
4715 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4716 xmlChar *QName;
4717 int len;
4718
4719 len = xmlStrlen(cur->name) +
4720 xmlStrlen(cur->ns->prefix) + 2;
4721 QName = xmlMalloc(len);
4722 if (QName == NULL) {
4723 ret = -1;
4724 goto fail;
4725 }
4726 snprintf((char *) QName, len, "%s:%s",
4727 (char *)cur->ns->prefix,
4728 (char *)cur->name);
4729 ret = xmlRegExecPushString(exec, QName, NULL);
4730 xmlFree(QName);
4731 } else {
4732 ret = xmlRegExecPushString(exec, cur->name, NULL);
4733 }
4734 break;
4735 default:
4736 break;
4737 }
4738 /*
4739 * Switch to next element
4740 */
4741 cur = cur->next;
4742 while (cur == NULL) {
4743 cur = nodeVPop(ctxt);
4744 if (cur == NULL)
4745 break;
4746 cur = cur->next;
4747 }
4748 }
4749 ret = xmlRegExecPushString(exec, NULL, NULL);
4750fail:
4751 xmlRegFreeExecCtxt(exec);
4752 }
4753 }
4754#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004755 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004756 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004757 */
4758 ctxt->vstateMax = 8;
4759 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4760 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4761 if (ctxt->vstateTab == NULL) {
4762 xmlGenericError(xmlGenericErrorContext,
4763 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004764 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004765 }
4766 /*
4767 * The first entry in the stack is reserved to the current state
4768 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004769 ctxt->nodeMax = 0;
4770 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004771 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004772 ctxt->vstate = &ctxt->vstateTab[0];
4773 ctxt->vstateNr = 1;
4774 CONT = cont;
4775 NODE = child;
4776 DEPTH = 0;
4777 OCCURS = 0;
4778 STATE = 0;
4779 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004780 if ((ret == -3) && (warn)) {
4781 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004782 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004783 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004784 /*
4785 * An entities reference appeared at this level.
4786 * Buid a minimal representation of this node content
4787 * sufficient to run the validation process on it
4788 */
4789 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004790 cur = child;
4791 while (cur != NULL) {
4792 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004793 case XML_ENTITY_REF_NODE:
4794 /*
4795 * Push the current node to be able to roll back
4796 * and process within the entity
4797 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004798 if ((cur->children != NULL) &&
4799 (cur->children->children != NULL)) {
4800 nodeVPush(ctxt, cur);
4801 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004802 continue;
4803 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004804 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004805 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004806 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004807 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004808 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004809 case XML_CDATA_SECTION_NODE:
4810 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004811 case XML_ELEMENT_NODE:
4812 /*
4813 * Allocate a new node and minimally fills in
4814 * what's required
4815 */
4816 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4817 if (tmp == NULL) {
4818 xmlGenericError(xmlGenericErrorContext,
4819 "xmlValidateElementContent : malloc failed\n");
4820 xmlFreeNodeList(repl);
4821 ret = -1;
4822 goto done;
4823 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004824 tmp->type = cur->type;
4825 tmp->name = cur->name;
4826 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004827 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004828 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004829 if (repl == NULL)
4830 repl = last = tmp;
4831 else {
4832 last->next = tmp;
4833 last = tmp;
4834 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004835 if (cur->type == XML_CDATA_SECTION_NODE) {
4836 /*
4837 * E59 spaces in CDATA does not match the
4838 * nonterminal S
4839 */
4840 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4841 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004842 break;
4843 default:
4844 break;
4845 }
4846 /*
4847 * Switch to next element
4848 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004849 cur = cur->next;
4850 while (cur == NULL) {
4851 cur = nodeVPop(ctxt);
4852 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004853 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004854 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004855 }
4856 }
4857
4858 /*
4859 * Relaunch the validation
4860 */
4861 ctxt->vstate = &ctxt->vstateTab[0];
4862 ctxt->vstateNr = 1;
4863 CONT = cont;
4864 NODE = repl;
4865 DEPTH = 0;
4866 OCCURS = 0;
4867 STATE = 0;
4868 ret = xmlValidateElementType(ctxt);
4869 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004870#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004871 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004872 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4873 char expr[5000];
4874 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004875
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004876 expr[0] = 0;
4877 xmlSnprintfElementContent(expr, 5000, cont, 1);
4878 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004879#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004880 if (repl != NULL)
4881 xmlSnprintfElements(list, 5000, repl, 1);
4882 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004883#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004884 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004885
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004886 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004887 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004888 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004889 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004890 name, expr, list);
4891 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004892 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004893 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004894 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004895 expr, list);
4896 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004897 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004898 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004899 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004900 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004901 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004902 name);
4903 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004904 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004905 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004906 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004907 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004908 }
4909 ret = 0;
4910 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004911 if (ret == -3)
4912 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004913
Daniel Veillard23e73572002-09-19 19:56:43 +00004914#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004915done:
4916 /*
4917 * Deallocate the copy if done, and free up the validation stack
4918 */
4919 while (repl != NULL) {
4920 tmp = repl->next;
4921 xmlFree(repl);
4922 repl = tmp;
4923 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004924 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004925 if (ctxt->vstateTab != NULL) {
4926 xmlFree(ctxt->vstateTab);
4927 ctxt->vstateTab = NULL;
4928 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004929#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004930 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004931 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004932 if (ctxt->nodeTab != NULL) {
4933 xmlFree(ctxt->nodeTab);
4934 ctxt->nodeTab = NULL;
4935 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004936 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004937
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004938}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004939
Owen Taylor3473f882001-02-23 17:55:21 +00004940/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004941 * xmlValidateCdataElement:
4942 * @ctxt: the validation context
4943 * @doc: a document instance
4944 * @elem: an element instance
4945 *
4946 * Check that an element follows #CDATA
4947 *
4948 * returns 1 if valid or 0 otherwise
4949 */
4950static int
4951xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4952 xmlNodePtr elem) {
4953 int ret = 1;
4954 xmlNodePtr cur, child;
4955
Daniel Veillardceb09b92002-10-04 11:46:37 +00004956 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004957 return(0);
4958
4959 child = elem->children;
4960
4961 cur = child;
4962 while (cur != NULL) {
4963 switch (cur->type) {
4964 case XML_ENTITY_REF_NODE:
4965 /*
4966 * Push the current node to be able to roll back
4967 * and process within the entity
4968 */
4969 if ((cur->children != NULL) &&
4970 (cur->children->children != NULL)) {
4971 nodeVPush(ctxt, cur);
4972 cur = cur->children->children;
4973 continue;
4974 }
4975 break;
4976 case XML_COMMENT_NODE:
4977 case XML_PI_NODE:
4978 case XML_TEXT_NODE:
4979 case XML_CDATA_SECTION_NODE:
4980 break;
4981 default:
4982 ret = 0;
4983 goto done;
4984 }
4985 /*
4986 * Switch to next element
4987 */
4988 cur = cur->next;
4989 while (cur == NULL) {
4990 cur = nodeVPop(ctxt);
4991 if (cur == NULL)
4992 break;
4993 cur = cur->next;
4994 }
4995 }
4996done:
4997 ctxt->nodeMax = 0;
4998 ctxt->nodeNr = 0;
4999 if (ctxt->nodeTab != NULL) {
5000 xmlFree(ctxt->nodeTab);
5001 ctxt->nodeTab = NULL;
5002 }
5003 return(ret);
5004}
5005
5006/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005007 * xmlValidateCheckMixed:
5008 * @ctxt: the validation context
5009 * @cont: the mixed content model
5010 * @qname: the qualified name as appearing in the serialization
5011 *
5012 * Check if the given node is part of the content model.
5013 *
5014 * Returns 1 if yes, 0 if no, -1 in case of error
5015 */
5016static int
5017xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5018 xmlElementContentPtr cont, const xmlChar *qname) {
5019 while (cont != NULL) {
5020 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5021 if (xmlStrEqual(cont->name, qname))
5022 return(1);
5023 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5024 (cont->c1 != NULL) &&
5025 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5026 if (xmlStrEqual(cont->c1->name, qname))
5027 return(1);
5028 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5029 (cont->c1 == NULL) ||
5030 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5031 /* Internal error !!! */
5032 xmlGenericError(xmlGenericErrorContext,
5033 "Internal: MIXED struct bad\n");
5034 break;
5035 }
5036 cont = cont->c2;
5037 }
5038 return(0);
5039}
5040
5041/**
5042 * xmlValidGetElemDecl:
5043 * @ctxt: the validation context
5044 * @doc: a document instance
5045 * @elem: an element instance
5046 * @extsubset: pointer, (out) indicate if the declaration was found
5047 * in the external subset.
5048 *
5049 * Finds a declaration associated to an element in the document.
5050 *
5051 * returns the pointer to the declaration or NULL if not found.
5052 */
5053static xmlElementPtr
5054xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5055 xmlNodePtr elem, int *extsubset) {
5056 xmlElementPtr elemDecl = NULL;
5057 const xmlChar *prefix = NULL;
5058
5059 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5060 if (extsubset != NULL)
5061 *extsubset = 0;
5062
5063 /*
5064 * Fetch the declaration for the qualified name
5065 */
5066 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5067 prefix = elem->ns->prefix;
5068
5069 if (prefix != NULL) {
5070 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5071 elem->name, prefix);
5072 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5073 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5074 elem->name, prefix);
5075 if ((elemDecl != NULL) && (extsubset != NULL))
5076 *extsubset = 1;
5077 }
5078 }
5079
5080 /*
5081 * Fetch the declaration for the non qualified name
5082 * This is "non-strict" validation should be done on the
5083 * full QName but in that case being flexible makes sense.
5084 */
5085 if (elemDecl == NULL) {
5086 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5087 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5088 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5089 if ((elemDecl != NULL) && (extsubset != NULL))
5090 *extsubset = 1;
5091 }
5092 }
5093 if (elemDecl == NULL) {
5094 VECTXT(ctxt, elem);
5095 VERROR(ctxt->userData, "No declaration for element %s\n",
5096 elem->name);
5097 }
5098 return(elemDecl);
5099}
5100
5101/**
5102 * xmlValidatePushElement:
5103 * @ctxt: the validation context
5104 * @doc: a document instance
5105 * @elem: an element instance
5106 * @qname: the qualified name as appearing in the serialization
5107 *
5108 * Push a new element start on the validation stack.
5109 *
5110 * returns 1 if no validation problem was found or 0 otherwise
5111 */
5112int
5113xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5114 xmlNodePtr elem, const xmlChar *qname) {
5115 int ret = 1;
5116 xmlElementPtr eDecl;
5117 int extsubset = 0;
5118
5119 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5120 xmlValidStatePtr state = ctxt->vstate;
5121 xmlElementPtr elemDecl;
5122
5123 /*
5124 * Check the new element agaisnt the content model of the new elem.
5125 */
5126 if (state->elemDecl != NULL) {
5127 elemDecl = state->elemDecl;
5128
5129 switch(elemDecl->etype) {
5130 case XML_ELEMENT_TYPE_UNDEFINED:
5131 ret = 0;
5132 break;
5133 case XML_ELEMENT_TYPE_EMPTY:
5134 VECTXT(ctxt, state->node);
5135 VERROR(ctxt->userData,
5136 "Element %s was declared EMPTY this one has content\n",
5137 state->node->name);
5138 ret = 0;
5139 break;
5140 case XML_ELEMENT_TYPE_ANY:
5141 /* I don't think anything is required then */
5142 break;
5143 case XML_ELEMENT_TYPE_MIXED:
5144 /* simple case of declared as #PCDATA */
5145 if ((elemDecl->content != NULL) &&
5146 (elemDecl->content->type ==
5147 XML_ELEMENT_CONTENT_PCDATA)) {
5148 VECTXT(ctxt, state->node);
5149 VERROR(ctxt->userData,
5150 "Element %s was declared #PCDATA but contains non text nodes\n",
5151 state->node->name);
5152 ret = 0;
5153 } else {
5154 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5155 qname);
5156 if (ret != 1) {
5157 VECTXT(ctxt, state->node);
5158 VERROR(ctxt->userData,
5159 "Element %s is not declared in %s list of possible children\n",
5160 qname, state->node->name);
5161 }
5162 }
5163 break;
5164 case XML_ELEMENT_TYPE_ELEMENT:
5165 /*
5166 * TODO:
5167 * VC: Standalone Document Declaration
5168 * - element types with element content, if white space
5169 * occurs directly within any instance of those types.
5170 */
5171 if (state->exec != NULL) {
5172 ret = xmlRegExecPushString(state->exec, qname, NULL);
5173 if (ret < 0) {
5174 VECTXT(ctxt, state->node);
5175 VERROR(ctxt->userData,
5176 "Element %s content does not follow the DTD\nMisplaced %s\n",
5177 state->node->name, qname);
5178 ret = 0;
5179 } else {
5180 ret = 1;
5181 }
5182 }
5183 break;
5184 }
5185 }
5186 }
5187 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5188 vstateVPush(ctxt, eDecl, elem);
5189 return(ret);
5190}
5191
5192/**
5193 * xmlValidatePushCData:
5194 * @ctxt: the validation context
5195 * @data: some character data read
5196 * @len: the lenght of the data
5197 *
5198 * check the CData parsed for validation in the current stack
5199 *
5200 * returns 1 if no validation problem was found or 0 otherwise
5201 */
5202int
5203xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5204 int ret = 1;
5205
5206 if (len <= 0)
5207 return(ret);
5208 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5209 xmlValidStatePtr state = ctxt->vstate;
5210 xmlElementPtr elemDecl;
5211
5212 /*
5213 * Check the new element agaisnt the content model of the new elem.
5214 */
5215 if (state->elemDecl != NULL) {
5216 elemDecl = state->elemDecl;
5217
5218 switch(elemDecl->etype) {
5219 case XML_ELEMENT_TYPE_UNDEFINED:
5220 ret = 0;
5221 break;
5222 case XML_ELEMENT_TYPE_EMPTY:
5223 VECTXT(ctxt, state->node);
5224 VERROR(ctxt->userData,
5225 "Element %s was declared EMPTY this one has content\n",
5226 state->node->name);
5227 ret = 0;
5228 break;
5229 case XML_ELEMENT_TYPE_ANY:
5230 break;
5231 case XML_ELEMENT_TYPE_MIXED:
5232 break;
5233 case XML_ELEMENT_TYPE_ELEMENT:
5234 if (len > 0) {
5235 int i;
5236
5237 for (i = 0;i < len;i++) {
5238 if (!IS_BLANK(data[i])) {
5239 VECTXT(ctxt, state->node);
5240 VERROR(ctxt->userData,
5241 "Element %s content does not follow the DTD\nText not allowed\n",
5242 state->node->name);
5243 ret = 0;
5244 goto done;
5245 }
5246 }
5247 /*
5248 * TODO:
5249 * VC: Standalone Document Declaration
5250 * element types with element content, if white space
5251 * occurs directly within any instance of those types.
5252 */
5253 }
5254 break;
5255 }
5256 }
5257 }
5258done:
5259 return(ret);
5260}
5261
5262/**
5263 * xmlValidatePopElement:
5264 * @ctxt: the validation context
5265 * @doc: a document instance
5266 * @elem: an element instance
5267 * @qname: the qualified name as appearing in the serialization
5268 *
5269 * Pop the element end from the validation stack.
5270 *
5271 * returns 1 if no validation problem was found or 0 otherwise
5272 */
5273int
5274xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
5275 xmlNodePtr elem, const xmlChar *qname ATTRIBUTE_UNUSED) {
5276 int ret = 1;
5277
5278 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5279 xmlValidStatePtr state = ctxt->vstate;
5280 xmlElementPtr elemDecl;
5281
5282 /*
5283 * Check the new element agaisnt the content model of the new elem.
5284 */
5285 if (state->elemDecl != NULL) {
5286 elemDecl = state->elemDecl;
5287
5288 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5289 if (state->exec != NULL) {
5290 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5291 if (ret == 0) {
5292 VECTXT(ctxt, state->node);
5293 VERROR(ctxt->userData,
5294 "Element %s content does not follow the DTD\nExpecting more child\n",
5295 state->node->name);
5296 } else {
5297 /*
5298 * previous validation errors should not generate
5299 * a new one here
5300 */
5301 ret = 1;
5302 }
5303 }
5304 }
5305 }
5306 vstateVPop(ctxt);
5307 }
5308 return(ret);
5309}
5310
5311/**
Owen Taylor3473f882001-02-23 17:55:21 +00005312 * xmlValidateOneElement:
5313 * @ctxt: the validation context
5314 * @doc: a document instance
5315 * @elem: an element instance
5316 *
5317 * Try to validate a single element and it's attributes,
5318 * basically it does the following checks as described by the
5319 * XML-1.0 recommendation:
5320 * - [ VC: Element Valid ]
5321 * - [ VC: Required Attribute ]
5322 * Then call xmlValidateOneAttribute() for each attribute present.
5323 *
5324 * The ID/IDREF checkings are done separately
5325 *
5326 * returns 1 if valid or 0 otherwise
5327 */
5328
5329int
5330xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5331 xmlNodePtr elem) {
5332 xmlElementPtr elemDecl = NULL;
5333 xmlElementContentPtr cont;
5334 xmlAttributePtr attr;
5335 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005336 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005337 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005338 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005339
5340 CHECK_DTD;
5341
5342 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005343 switch (elem->type) {
5344 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005345 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005346 VERROR(ctxt->userData,
5347 "Attribute element not expected here\n");
5348 return(0);
5349 case XML_TEXT_NODE:
5350 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005351 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005352 VERROR(ctxt->userData, "Text element has childs !\n");
5353 return(0);
5354 }
5355 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005356 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005357 VERROR(ctxt->userData, "Text element has attributes !\n");
5358 return(0);
5359 }
5360 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005361 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005362 VERROR(ctxt->userData, "Text element has namespace !\n");
5363 return(0);
5364 }
5365 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005366 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005367 VERROR(ctxt->userData,
5368 "Text element carries namespace definitions !\n");
5369 return(0);
5370 }
5371 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005372 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005373 VERROR(ctxt->userData,
5374 "Text element has no content !\n");
5375 return(0);
5376 }
5377 return(1);
5378 case XML_XINCLUDE_START:
5379 case XML_XINCLUDE_END:
5380 return(1);
5381 case XML_CDATA_SECTION_NODE:
5382 case XML_ENTITY_REF_NODE:
5383 case XML_PI_NODE:
5384 case XML_COMMENT_NODE:
5385 return(1);
5386 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005387 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005388 VERROR(ctxt->userData,
5389 "Entity element not expected here\n");
5390 return(0);
5391 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005392 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005393 VERROR(ctxt->userData,
5394 "Notation element not expected here\n");
5395 return(0);
5396 case XML_DOCUMENT_NODE:
5397 case XML_DOCUMENT_TYPE_NODE:
5398 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005399 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005400 VERROR(ctxt->userData,
5401 "Document element not expected here\n");
5402 return(0);
5403 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005404 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005405 VERROR(ctxt->userData,
5406 "\n");
5407 return(0);
5408 case XML_ELEMENT_NODE:
5409 break;
5410 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005411 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005412 VERROR(ctxt->userData,
5413 "unknown element type %d\n", elem->type);
5414 return(0);
5415 }
Owen Taylor3473f882001-02-23 17:55:21 +00005416
5417 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005418 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005419 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005420 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5421 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005422 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005423
Daniel Veillardea7751d2002-12-20 00:16:24 +00005424 /*
5425 * If vstateNr is not zero that means continuous validation is
5426 * activated, do not try to check the content model at that level.
5427 */
5428 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005429 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005430 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005431 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005432 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005433 VERROR(ctxt->userData, "No declaration for element %s\n",
5434 elem->name);
5435 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005436 case XML_ELEMENT_TYPE_EMPTY:
5437 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005438 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005439 VERROR(ctxt->userData,
5440 "Element %s was declared EMPTY this one has content\n",
5441 elem->name);
5442 ret = 0;
5443 }
5444 break;
5445 case XML_ELEMENT_TYPE_ANY:
5446 /* I don't think anything is required then */
5447 break;
5448 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005449
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005450 /* simple case of declared as #PCDATA */
5451 if ((elemDecl->content != NULL) &&
5452 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5453 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5454 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005455 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005456 VERROR(ctxt->userData,
5457 "Element %s was declared #PCDATA but contains non text nodes\n",
5458 elem->name);
5459 }
5460 break;
5461 }
Owen Taylor3473f882001-02-23 17:55:21 +00005462 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005463 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005464 while (child != NULL) {
5465 if (child->type == XML_ELEMENT_NODE) {
5466 name = child->name;
5467 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5468 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005469 snprintf((char *) qname, sizeof(qname), "%s:%s",
5470 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005471 qname[sizeof(qname) - 1] = 0;
5472 cont = elemDecl->content;
5473 while (cont != NULL) {
5474 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5475 if (xmlStrEqual(cont->name, qname)) break;
5476 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5477 (cont->c1 != NULL) &&
5478 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5479 if (xmlStrEqual(cont->c1->name, qname)) break;
5480 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5481 (cont->c1 == NULL) ||
5482 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5483 /* Internal error !!! */
5484 xmlGenericError(xmlGenericErrorContext,
5485 "Internal: MIXED struct bad\n");
5486 break;
5487 }
5488 cont = cont->c2;
5489 }
5490 if (cont != NULL)
5491 goto child_ok;
5492 }
5493 cont = elemDecl->content;
5494 while (cont != NULL) {
5495 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5496 if (xmlStrEqual(cont->name, name)) break;
5497 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5498 (cont->c1 != NULL) &&
5499 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5500 if (xmlStrEqual(cont->c1->name, name)) break;
5501 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5502 (cont->c1 == NULL) ||
5503 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5504 /* Internal error !!! */
5505 xmlGenericError(xmlGenericErrorContext,
5506 "Internal: MIXED struct bad\n");
5507 break;
5508 }
5509 cont = cont->c2;
5510 }
5511 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005512 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005513 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005514 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005515 name, elem->name);
5516 ret = 0;
5517 }
5518 }
5519child_ok:
5520 child = child->next;
5521 }
5522 break;
5523 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005524 if ((doc->standalone == 1) && (extsubset == 1)) {
5525 /*
5526 * VC: Standalone Document Declaration
5527 * - element types with element content, if white space
5528 * occurs directly within any instance of those types.
5529 */
5530 child = elem->children;
5531 while (child != NULL) {
5532 if (child->type == XML_TEXT_NODE) {
5533 const xmlChar *content = child->content;
5534
5535 while (IS_BLANK(*content))
5536 content++;
5537 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005538 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005539 VERROR(ctxt->userData,
5540"standalone: %s declared in the external subset contains white spaces nodes\n",
5541 elem->name);
5542 ret = 0;
5543 break;
5544 }
5545 }
5546 child =child->next;
5547 }
5548 }
Owen Taylor3473f882001-02-23 17:55:21 +00005549 child = elem->children;
5550 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005551 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005552 if (tmp <= 0)
5553 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005554 break;
5555 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005556 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005557
5558 /* [ VC: Required Attribute ] */
5559 attr = elemDecl->attributes;
5560 while (attr != NULL) {
5561 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005562 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005563
Daniel Veillarde4301c82002-02-13 13:32:35 +00005564 if ((attr->prefix == NULL) &&
5565 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5566 xmlNsPtr ns;
5567
5568 ns = elem->nsDef;
5569 while (ns != NULL) {
5570 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005571 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005572 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005573 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005574 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5575 xmlNsPtr ns;
5576
5577 ns = elem->nsDef;
5578 while (ns != NULL) {
5579 if (xmlStrEqual(attr->name, ns->prefix))
5580 goto found;
5581 ns = ns->next;
5582 }
5583 } else {
5584 xmlAttrPtr attrib;
5585
5586 attrib = elem->properties;
5587 while (attrib != NULL) {
5588 if (xmlStrEqual(attrib->name, attr->name)) {
5589 if (attr->prefix != NULL) {
5590 xmlNsPtr nameSpace = attrib->ns;
5591
5592 if (nameSpace == NULL)
5593 nameSpace = elem->ns;
5594 /*
5595 * qualified names handling is problematic, having a
5596 * different prefix should be possible but DTDs don't
5597 * allow to define the URI instead of the prefix :-(
5598 */
5599 if (nameSpace == NULL) {
5600 if (qualified < 0)
5601 qualified = 0;
5602 } else if (!xmlStrEqual(nameSpace->prefix,
5603 attr->prefix)) {
5604 if (qualified < 1)
5605 qualified = 1;
5606 } else
5607 goto found;
5608 } else {
5609 /*
5610 * We should allow applications to define namespaces
5611 * for their application even if the DTD doesn't
5612 * carry one, otherwise, basically we would always
5613 * break.
5614 */
5615 goto found;
5616 }
5617 }
5618 attrib = attrib->next;
5619 }
Owen Taylor3473f882001-02-23 17:55:21 +00005620 }
5621 if (qualified == -1) {
5622 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005623 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005624 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005625 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005626 elem->name, attr->name);
5627 ret = 0;
5628 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005629 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005630 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005631 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005632 elem->name, attr->prefix,attr->name);
5633 ret = 0;
5634 }
5635 } else if (qualified == 0) {
5636 VWARNING(ctxt->userData,
5637 "Element %s required attribute %s:%s has no prefix\n",
5638 elem->name, attr->prefix,attr->name);
5639 } else if (qualified == 1) {
5640 VWARNING(ctxt->userData,
5641 "Element %s required attribute %s:%s has different prefix\n",
5642 elem->name, attr->prefix,attr->name);
5643 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005644 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5645 /*
5646 * Special tests checking #FIXED namespace declarations
5647 * have the right value since this is not done as an
5648 * attribute checking
5649 */
5650 if ((attr->prefix == NULL) &&
5651 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5652 xmlNsPtr ns;
5653
5654 ns = elem->nsDef;
5655 while (ns != NULL) {
5656 if (ns->prefix == NULL) {
5657 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005658 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005659 VERROR(ctxt->userData,
5660 "Element %s namespace name for default namespace does not match the DTD\n",
5661 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005662 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005663 }
5664 goto found;
5665 }
5666 ns = ns->next;
5667 }
5668 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5669 xmlNsPtr ns;
5670
5671 ns = elem->nsDef;
5672 while (ns != NULL) {
5673 if (xmlStrEqual(attr->name, ns->prefix)) {
5674 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005675 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005676 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005677 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005678 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005679 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005680 }
5681 goto found;
5682 }
5683 ns = ns->next;
5684 }
5685 }
Owen Taylor3473f882001-02-23 17:55:21 +00005686 }
5687found:
5688 attr = attr->nexth;
5689 }
5690 return(ret);
5691}
5692
5693/**
5694 * xmlValidateRoot:
5695 * @ctxt: the validation context
5696 * @doc: a document instance
5697 *
5698 * Try to validate a the root element
5699 * basically it does the following check as described by the
5700 * XML-1.0 recommendation:
5701 * - [ VC: Root Element Type ]
5702 * it doesn't try to recurse or apply other check to the element
5703 *
5704 * returns 1 if valid or 0 otherwise
5705 */
5706
5707int
5708xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5709 xmlNodePtr root;
5710 if (doc == NULL) return(0);
5711
5712 root = xmlDocGetRootElement(doc);
5713 if ((root == NULL) || (root->name == NULL)) {
5714 VERROR(ctxt->userData, "Not valid: no root element\n");
5715 return(0);
5716 }
5717
5718 /*
5719 * When doing post validation against a separate DTD, those may
5720 * no internal subset has been generated
5721 */
5722 if ((doc->intSubset != NULL) &&
5723 (doc->intSubset->name != NULL)) {
5724 /*
5725 * Check first the document root against the NQName
5726 */
5727 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5728 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5729 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005730 snprintf((char *) qname, sizeof(qname), "%s:%s",
5731 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005732 qname[sizeof(qname) - 1] = 0;
5733 if (xmlStrEqual(doc->intSubset->name, qname))
5734 goto name_ok;
5735 }
5736 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5737 (xmlStrEqual(root->name, BAD_CAST "html")))
5738 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005739 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005740 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005741 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005742 root->name, doc->intSubset->name);
5743 return(0);
5744
5745 }
5746 }
5747name_ok:
5748 return(1);
5749}
5750
5751
5752/**
5753 * xmlValidateElement:
5754 * @ctxt: the validation context
5755 * @doc: a document instance
5756 * @elem: an element instance
5757 *
5758 * Try to validate the subtree under an element
5759 *
5760 * returns 1 if valid or 0 otherwise
5761 */
5762
5763int
5764xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5765 xmlNodePtr child;
5766 xmlAttrPtr attr;
5767 xmlChar *value;
5768 int ret = 1;
5769
5770 if (elem == NULL) return(0);
5771
5772 /*
5773 * XInclude elements were added after parsing in the infoset,
5774 * they don't really mean anything validation wise.
5775 */
5776 if ((elem->type == XML_XINCLUDE_START) ||
5777 (elem->type == XML_XINCLUDE_END))
5778 return(1);
5779
5780 CHECK_DTD;
5781
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005782 /*
5783 * Entities references have to be handled separately
5784 */
5785 if (elem->type == XML_ENTITY_REF_NODE) {
5786 return(1);
5787 }
5788
Owen Taylor3473f882001-02-23 17:55:21 +00005789 ret &= xmlValidateOneElement(ctxt, doc, elem);
5790 attr = elem->properties;
5791 while(attr != NULL) {
5792 value = xmlNodeListGetString(doc, attr->children, 0);
5793 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5794 if (value != NULL)
5795 xmlFree(value);
5796 attr= attr->next;
5797 }
5798 child = elem->children;
5799 while (child != NULL) {
5800 ret &= xmlValidateElement(ctxt, doc, child);
5801 child = child->next;
5802 }
5803
5804 return(ret);
5805}
5806
Daniel Veillard8730c562001-02-26 10:49:57 +00005807/**
5808 * xmlValidateRef:
5809 * @ref: A reference to be validated
5810 * @ctxt: Validation context
5811 * @name: Name of ID we are searching for
5812 *
5813 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005814static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005815xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005816 const xmlChar *name) {
5817 xmlAttrPtr id;
5818 xmlAttrPtr attr;
5819
5820 if (ref == NULL)
5821 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005822 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005823 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005824 attr = ref->attr;
5825 if (attr == NULL) {
5826 xmlChar *dup, *str = NULL, *cur, save;
5827
5828 dup = xmlStrdup(name);
5829 if (dup == NULL) {
5830 ctxt->valid = 0;
5831 return;
5832 }
5833 cur = dup;
5834 while (*cur != 0) {
5835 str = cur;
5836 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5837 save = *cur;
5838 *cur = 0;
5839 id = xmlGetID(ctxt->doc, str);
5840 if (id == NULL) {
5841 VERROR(ctxt->userData,
5842 "attribute %s line %d references an unknown ID \"%s\"\n",
5843 ref->name, ref->lineno, str);
5844 ctxt->valid = 0;
5845 }
5846 if (save == 0)
5847 break;
5848 *cur = save;
5849 while (IS_BLANK(*cur)) cur++;
5850 }
5851 xmlFree(dup);
5852 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005853 id = xmlGetID(ctxt->doc, name);
5854 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005855 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005856 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005857 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005858 attr->name, name);
5859 ctxt->valid = 0;
5860 }
5861 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5862 xmlChar *dup, *str = NULL, *cur, save;
5863
5864 dup = xmlStrdup(name);
5865 if (dup == NULL) {
5866 ctxt->valid = 0;
5867 return;
5868 }
5869 cur = dup;
5870 while (*cur != 0) {
5871 str = cur;
5872 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5873 save = *cur;
5874 *cur = 0;
5875 id = xmlGetID(ctxt->doc, str);
5876 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005877 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005878 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005879 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005880 attr->name, str);
5881 ctxt->valid = 0;
5882 }
5883 if (save == 0)
5884 break;
5885 *cur = save;
5886 while (IS_BLANK(*cur)) cur++;
5887 }
5888 xmlFree(dup);
5889 }
5890}
5891
5892/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005893 * xmlWalkValidateList:
5894 * @data: Contents of current link
5895 * @user: Value supplied by the user
5896 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005897 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005898 */
5899static int
5900xmlWalkValidateList(const void *data, const void *user)
5901{
5902 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5903 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5904 return 1;
5905}
5906
5907/**
5908 * xmlValidateCheckRefCallback:
5909 * @ref_list: List of references
5910 * @ctxt: Validation context
5911 * @name: Name of ID we are searching for
5912 *
5913 */
5914static void
5915xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5916 const xmlChar *name) {
5917 xmlValidateMemo memo;
5918
5919 if (ref_list == NULL)
5920 return;
5921 memo.ctxt = ctxt;
5922 memo.name = name;
5923
5924 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5925
5926}
5927
5928/**
Owen Taylor3473f882001-02-23 17:55:21 +00005929 * xmlValidateDocumentFinal:
5930 * @ctxt: the validation context
5931 * @doc: a document instance
5932 *
5933 * Does the final step for the document validation once all the
5934 * incremental validation steps have been completed
5935 *
5936 * basically it does the following checks described by the XML Rec
5937 *
5938 *
5939 * returns 1 if valid or 0 otherwise
5940 */
5941
5942int
5943xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5944 xmlRefTablePtr table;
5945
5946 if (doc == NULL) {
5947 xmlGenericError(xmlGenericErrorContext,
5948 "xmlValidateDocumentFinal: doc == NULL\n");
5949 return(0);
5950 }
5951
5952 /*
5953 * Check all the NOTATION/NOTATIONS attributes
5954 */
5955 /*
5956 * Check all the ENTITY/ENTITIES attributes definition for validity
5957 */
5958 /*
5959 * Check all the IDREF/IDREFS attributes definition for validity
5960 */
5961 table = (xmlRefTablePtr) doc->refs;
5962 ctxt->doc = doc;
5963 ctxt->valid = 1;
5964 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5965 return(ctxt->valid);
5966}
5967
5968/**
5969 * xmlValidateDtd:
5970 * @ctxt: the validation context
5971 * @doc: a document instance
5972 * @dtd: a dtd instance
5973 *
5974 * Try to validate the document against the dtd instance
5975 *
5976 * basically it does check all the definitions in the DtD.
5977 *
5978 * returns 1 if valid or 0 otherwise
5979 */
5980
5981int
5982xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5983 int ret;
5984 xmlDtdPtr oldExt;
5985 xmlNodePtr root;
5986
5987 if (dtd == NULL) return(0);
5988 if (doc == NULL) return(0);
5989 oldExt = doc->extSubset;
5990 doc->extSubset = dtd;
5991 ret = xmlValidateRoot(ctxt, doc);
5992 if (ret == 0) {
5993 doc->extSubset = oldExt;
5994 return(ret);
5995 }
5996 if (doc->ids != NULL) {
5997 xmlFreeIDTable(doc->ids);
5998 doc->ids = NULL;
5999 }
6000 if (doc->refs != NULL) {
6001 xmlFreeRefTable(doc->refs);
6002 doc->refs = NULL;
6003 }
6004 root = xmlDocGetRootElement(doc);
6005 ret = xmlValidateElement(ctxt, doc, root);
6006 ret &= xmlValidateDocumentFinal(ctxt, doc);
6007 doc->extSubset = oldExt;
6008 return(ret);
6009}
6010
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006011static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006012xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6013 const xmlChar *name ATTRIBUTE_UNUSED) {
6014 if (cur == NULL)
6015 return;
6016 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6017 xmlChar *notation = cur->content;
6018
Daniel Veillard878eab02002-02-19 13:46:09 +00006019 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006020 int ret;
6021
6022 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6023 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006024 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006025 }
6026 }
6027 }
6028}
6029
6030static void
Owen Taylor3473f882001-02-23 17:55:21 +00006031xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006032 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006033 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006034 xmlDocPtr doc;
6035 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006036
Owen Taylor3473f882001-02-23 17:55:21 +00006037 if (cur == NULL)
6038 return;
6039 switch (cur->atype) {
6040 case XML_ATTRIBUTE_CDATA:
6041 case XML_ATTRIBUTE_ID:
6042 case XML_ATTRIBUTE_IDREF :
6043 case XML_ATTRIBUTE_IDREFS:
6044 case XML_ATTRIBUTE_NMTOKEN:
6045 case XML_ATTRIBUTE_NMTOKENS:
6046 case XML_ATTRIBUTE_ENUMERATION:
6047 break;
6048 case XML_ATTRIBUTE_ENTITY:
6049 case XML_ATTRIBUTE_ENTITIES:
6050 case XML_ATTRIBUTE_NOTATION:
6051 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006052
6053 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6054 cur->atype, cur->defaultValue);
6055 if ((ret == 0) && (ctxt->valid == 1))
6056 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006057 }
6058 if (cur->tree != NULL) {
6059 xmlEnumerationPtr tree = cur->tree;
6060 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006061 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006062 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006063 if ((ret == 0) && (ctxt->valid == 1))
6064 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006065 tree = tree->next;
6066 }
6067 }
6068 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006069 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6070 doc = cur->doc;
6071 if ((doc == NULL) || (cur->elem == NULL)) {
6072 VERROR(ctxt->userData,
6073 "xmlValidateAttributeCallback(%s): internal error\n",
6074 cur->name);
6075 return;
6076 }
6077 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6078 if (elem == NULL)
6079 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6080 if (elem == NULL) {
6081 VERROR(ctxt->userData,
6082 "attribute %s: could not find decl for element %s\n",
6083 cur->name, cur->elem);
6084 return;
6085 }
6086 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6087 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006088 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006089 cur->name, cur->elem);
6090 ctxt->valid = 0;
6091 }
6092 }
Owen Taylor3473f882001-02-23 17:55:21 +00006093}
6094
6095/**
6096 * xmlValidateDtdFinal:
6097 * @ctxt: the validation context
6098 * @doc: a document instance
6099 *
6100 * Does the final step for the dtds validation once all the
6101 * subsets have been parsed
6102 *
6103 * basically it does the following checks described by the XML Rec
6104 * - check that ENTITY and ENTITIES type attributes default or
6105 * possible values matches one of the defined entities.
6106 * - check that NOTATION type attributes default or
6107 * possible values matches one of the defined notations.
6108 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006109 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006110 */
6111
6112int
6113xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006114 xmlDtdPtr dtd;
6115 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006116 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006117
6118 if (doc == NULL) return(0);
6119 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6120 return(0);
6121 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006122 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006123 dtd = doc->intSubset;
6124 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6125 table = (xmlAttributeTablePtr) dtd->attributes;
6126 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006127 }
6128 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006129 entities = (xmlEntitiesTablePtr) dtd->entities;
6130 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6131 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006132 }
6133 dtd = doc->extSubset;
6134 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6135 table = (xmlAttributeTablePtr) dtd->attributes;
6136 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006137 }
6138 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006139 entities = (xmlEntitiesTablePtr) dtd->entities;
6140 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6141 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006142 }
6143 return(ctxt->valid);
6144}
6145
6146/**
6147 * xmlValidateDocument:
6148 * @ctxt: the validation context
6149 * @doc: a document instance
6150 *
6151 * Try to validate the document instance
6152 *
6153 * basically it does the all the checks described by the XML Rec
6154 * i.e. validates the internal and external subset (if present)
6155 * and validate the document tree.
6156 *
6157 * returns 1 if valid or 0 otherwise
6158 */
6159
6160int
6161xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6162 int ret;
6163 xmlNodePtr root;
6164
Daniel Veillard2fd85422002-10-16 14:32:41 +00006165 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6166 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006167 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006168 }
Owen Taylor3473f882001-02-23 17:55:21 +00006169 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6170 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6171 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6172 doc->intSubset->SystemID);
6173 if (doc->extSubset == NULL) {
6174 if (doc->intSubset->SystemID != NULL) {
6175 VERROR(ctxt->userData,
6176 "Could not load the external subset \"%s\"\n",
6177 doc->intSubset->SystemID);
6178 } else {
6179 VERROR(ctxt->userData,
6180 "Could not load the external subset \"%s\"\n",
6181 doc->intSubset->ExternalID);
6182 }
6183 return(0);
6184 }
6185 }
6186
6187 if (doc->ids != NULL) {
6188 xmlFreeIDTable(doc->ids);
6189 doc->ids = NULL;
6190 }
6191 if (doc->refs != NULL) {
6192 xmlFreeRefTable(doc->refs);
6193 doc->refs = NULL;
6194 }
6195 ret = xmlValidateDtdFinal(ctxt, doc);
6196 if (!xmlValidateRoot(ctxt, doc)) return(0);
6197
6198 root = xmlDocGetRootElement(doc);
6199 ret &= xmlValidateElement(ctxt, doc, root);
6200 ret &= xmlValidateDocumentFinal(ctxt, doc);
6201 return(ret);
6202}
6203
6204
6205/************************************************************************
6206 * *
6207 * Routines for dynamic validation editing *
6208 * *
6209 ************************************************************************/
6210
6211/**
6212 * xmlValidGetPotentialChildren:
6213 * @ctree: an element content tree
6214 * @list: an array to store the list of child names
6215 * @len: a pointer to the number of element in the list
6216 * @max: the size of the array
6217 *
6218 * Build/extend a list of potential children allowed by the content tree
6219 *
6220 * returns the number of element in the list, or -1 in case of error.
6221 */
6222
6223int
6224xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6225 int *len, int max) {
6226 int i;
6227
6228 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6229 return(-1);
6230 if (*len >= max) return(*len);
6231
6232 switch (ctree->type) {
6233 case XML_ELEMENT_CONTENT_PCDATA:
6234 for (i = 0; i < *len;i++)
6235 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6236 list[(*len)++] = BAD_CAST "#PCDATA";
6237 break;
6238 case XML_ELEMENT_CONTENT_ELEMENT:
6239 for (i = 0; i < *len;i++)
6240 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6241 list[(*len)++] = ctree->name;
6242 break;
6243 case XML_ELEMENT_CONTENT_SEQ:
6244 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6245 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6246 break;
6247 case XML_ELEMENT_CONTENT_OR:
6248 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6249 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6250 break;
6251 }
6252
6253 return(*len);
6254}
6255
6256/**
6257 * xmlValidGetValidElements:
6258 * @prev: an element to insert after
6259 * @next: an element to insert next
6260 * @list: an array to store the list of child names
6261 * @max: the size of the array
6262 *
6263 * This function returns the list of authorized children to insert
6264 * within an existing tree while respecting the validity constraints
6265 * forced by the Dtd. The insertion point is defined using @prev and
6266 * @next in the following ways:
6267 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6268 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6269 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6270 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6271 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6272 *
6273 * pointers to the element names are inserted at the beginning of the array
6274 * and do not need to be freed.
6275 *
6276 * returns the number of element in the list, or -1 in case of error. If
6277 * the function returns the value @max the caller is invited to grow the
6278 * receiving array and retry.
6279 */
6280
6281int
6282xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6283 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006284 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006285 int nb_valid_elements = 0;
6286 const xmlChar *elements[256];
6287 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006288 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006289
6290 xmlNode *ref_node;
6291 xmlNode *parent;
6292 xmlNode *test_node;
6293
6294 xmlNode *prev_next;
6295 xmlNode *next_prev;
6296 xmlNode *parent_childs;
6297 xmlNode *parent_last;
6298
6299 xmlElement *element_desc;
6300
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006301 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006302
Owen Taylor3473f882001-02-23 17:55:21 +00006303 if (prev == NULL && next == NULL)
6304 return(-1);
6305
6306 if (list == NULL) return(-1);
6307 if (max <= 0) return(-1);
6308
6309 nb_valid_elements = 0;
6310 ref_node = prev ? prev : next;
6311 parent = ref_node->parent;
6312
6313 /*
6314 * Retrieves the parent element declaration
6315 */
6316 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6317 parent->name);
6318 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6319 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6320 parent->name);
6321 if (element_desc == NULL) return(-1);
6322
6323 /*
6324 * Do a backup of the current tree structure
6325 */
6326 prev_next = prev ? prev->next : NULL;
6327 next_prev = next ? next->prev : NULL;
6328 parent_childs = parent->children;
6329 parent_last = parent->last;
6330
6331 /*
6332 * Creates a dummy node and insert it into the tree
6333 */
6334 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6335 test_node->doc = ref_node->doc;
6336 test_node->parent = parent;
6337 test_node->prev = prev;
6338 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006339 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006340
6341 if (prev) prev->next = test_node;
6342 else parent->children = test_node;
6343
6344 if (next) next->prev = test_node;
6345 else parent->last = test_node;
6346
6347 /*
6348 * Insert each potential child node and check if the parent is
6349 * still valid
6350 */
6351 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6352 elements, &nb_elements, 256);
6353
6354 for (i = 0;i < nb_elements;i++) {
6355 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006356 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006357 int j;
6358
6359 for (j = 0; j < nb_valid_elements;j++)
6360 if (xmlStrEqual(elements[i], list[j])) break;
6361 list[nb_valid_elements++] = elements[i];
6362 if (nb_valid_elements >= max) break;
6363 }
6364 }
6365
6366 /*
6367 * Restore the tree structure
6368 */
6369 if (prev) prev->next = prev_next;
6370 if (next) next->prev = next_prev;
6371 parent->children = parent_childs;
6372 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006373
6374 /*
6375 * Free up the dummy node
6376 */
6377 test_node->name = name;
6378 xmlFreeNode(test_node);
6379
Owen Taylor3473f882001-02-23 17:55:21 +00006380 return(nb_valid_elements);
6381}