|
1 /* |
|
2 * libxml2_valid.c : part of the code use to do the DTD handling and the validity |
|
3 * checking |
|
4 * |
|
5 * See Copyright for the status of this software. |
|
6 * |
|
7 * daniel@veillard.com |
|
8 * Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved. |
|
9 */ |
|
10 |
|
11 #define IN_LIBXML |
|
12 |
|
13 #include "xmlenglibxml.h" |
|
14 |
|
15 #include <string.h> |
|
16 |
|
17 #ifdef HAVE_STDLIB_H |
|
18 #include <stdlib.h> |
|
19 #endif |
|
20 |
|
21 #include <stdapis/libxml2/libxml2_globals.h> |
|
22 #include <stdapis/libxml2/libxml2_parserinternals.h> |
|
23 #include "libxml2_xmlerror2.h" |
|
24 |
|
25 static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, |
|
26 int create); |
|
27 /* #define DEBUG_VALID_ALGO */ |
|
28 /* #define DEBUG_REGEXP_ALGO */ |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 /************************************************************************ |
|
36 * * |
|
37 * Error handling routines * |
|
38 * * |
|
39 ************************************************************************/ |
|
40 |
|
41 /** |
|
42 * xmlVErrMemory: |
|
43 * @param ctxt an XML validation parser context |
|
44 * @param extra extra informations |
|
45 * |
|
46 * Handle an out of memory error |
|
47 */ |
|
48 static void |
|
49 xmlVErrMemory(xmlValidCtxtPtr ctxt, const char *extra) |
|
50 { |
|
51 xmlGenericErrorFunc channel; |
|
52 xmlParserCtxtPtr pctxt; |
|
53 void* data; |
|
54 |
|
55 if (ctxt) { |
|
56 channel = ctxt->error; |
|
57 data = ctxt->userData; |
|
58 pctxt = (xmlParserCtxtPtr)ctxt->userData; |
|
59 } else { |
|
60 channel = NULL; |
|
61 pctxt = NULL; |
|
62 data = NULL; |
|
63 } |
|
64 if (extra) |
|
65 __xmlRaiseError(NULL, channel, data, |
|
66 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY, |
|
67 XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0, |
|
68 EMBED_ERRTXT("Memory allocation failed : %s\n"), extra); |
|
69 else |
|
70 __xmlRaiseError(NULL, channel, data, |
|
71 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY, |
|
72 XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, |
|
73 EMBED_ERRTXT("Memory allocation failed\n")); |
|
74 } |
|
75 |
|
76 /** |
|
77 * xmlErrValid: |
|
78 * @param ctxt an XML validation parser context |
|
79 * @param error the error number |
|
80 * @param extra extra informations |
|
81 * |
|
82 * Handle a validation error |
|
83 */ |
|
84 static void |
|
85 xmlErrValid(xmlValidCtxtPtr ctxt, xmlParserErrors error, |
|
86 const char *msg, const char *extra) |
|
87 { |
|
88 xmlGenericErrorFunc channel = NULL; |
|
89 xmlParserCtxtPtr pctxt = NULL; |
|
90 void *data = NULL; |
|
91 |
|
92 if (ctxt != NULL) { |
|
93 channel = ctxt->error; |
|
94 data = ctxt->userData; |
|
95 pctxt = (xmlParserCtxtPtr)ctxt->userData; |
|
96 } |
|
97 if (extra) |
|
98 __xmlRaiseError(NULL, channel, data, |
|
99 pctxt, NULL, XML_FROM_VALID, error, |
|
100 XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0, |
|
101 msg, extra); |
|
102 else |
|
103 __xmlRaiseError(NULL, channel, data, |
|
104 pctxt, NULL, XML_FROM_VALID, error, |
|
105 XML_ERR_ERROR, NULL, 0, NULL, NULL, NULL, 0, 0, |
|
106 msg); |
|
107 } |
|
108 |
|
109 #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) || defined(XMLENGINE_XMLSCHEMA_DATATYPES) |
|
110 /** |
|
111 * xmlErrValidNode: |
|
112 * @param ctxt an XML validation parser context |
|
113 * @param node the node raising the error |
|
114 * @param error the error number |
|
115 * @param str1 extra informations |
|
116 * @param str2 extra informations |
|
117 * @param str3 extra informations |
|
118 * |
|
119 * Handle a validation error, provide contextual informations |
|
120 */ |
|
121 static void |
|
122 xmlErrValidNode(xmlValidCtxtPtr ctxt, |
|
123 xmlNodePtr node, xmlParserErrors error, |
|
124 const char *msg, const xmlChar * str1, |
|
125 const xmlChar * str2, const xmlChar * str3) |
|
126 { |
|
127 xmlStructuredErrorFunc schannel = NULL; |
|
128 xmlGenericErrorFunc channel = NULL; |
|
129 xmlParserCtxtPtr pctxt = NULL; |
|
130 void *data = NULL; |
|
131 |
|
132 if (ctxt != NULL) { |
|
133 channel = ctxt->error; |
|
134 data = ctxt->userData; |
|
135 pctxt = (xmlParserCtxtPtr)ctxt->userData; |
|
136 } |
|
137 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, |
|
138 XML_ERR_ERROR, NULL, 0, |
|
139 (const char *) str1, |
|
140 (const char *) str1, |
|
141 (const char *) str3, 0, 0, msg, str1, str2, str3); |
|
142 } |
|
143 #endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */ |
|
144 |
|
145 #ifdef LIBXML_VALID_ENABLED |
|
146 /** |
|
147 * xmlErrValidNodeNr: |
|
148 * @param ctxt an XML validation parser context |
|
149 * @param node the node raising the error |
|
150 * @param error the error number |
|
151 * @param str1 extra informations |
|
152 * @param int2 extra informations |
|
153 * @param str3 extra informations |
|
154 * |
|
155 * Handle a validation error, provide contextual informations |
|
156 */ |
|
157 static void |
|
158 xmlErrValidNodeNr(xmlValidCtxtPtr ctxt, |
|
159 xmlNodePtr node, xmlParserErrors error, |
|
160 const char *msg, const xmlChar * str1, |
|
161 int int2, const xmlChar * str3) |
|
162 { |
|
163 xmlStructuredErrorFunc schannel = NULL; |
|
164 xmlGenericErrorFunc channel = NULL; |
|
165 xmlParserCtxtPtr pctxt = NULL; |
|
166 void *data = NULL; |
|
167 |
|
168 if (ctxt != NULL) { |
|
169 channel = ctxt->error; |
|
170 data = ctxt->userData; |
|
171 pctxt = ctxt->userData; |
|
172 } |
|
173 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, |
|
174 XML_ERR_ERROR, NULL, 0, |
|
175 (const char *) str1, |
|
176 (const char *) str3, |
|
177 NULL, int2, 0, msg, str1, int2, str3); |
|
178 } |
|
179 |
|
180 /** |
|
181 * xmlErrValidWarning: |
|
182 * @param ctxt an XML validation parser context |
|
183 * @param node the node raising the error |
|
184 * @param error the error number |
|
185 * @param str1 extra information |
|
186 * @param str2 extra information |
|
187 * @param str3 extra information |
|
188 * |
|
189 * Handle a validation error, provide contextual information |
|
190 */ |
|
191 static void |
|
192 xmlErrValidWarning(xmlValidCtxtPtr ctxt, |
|
193 xmlNodePtr node, xmlParserErrors error, |
|
194 const char *msg, const xmlChar * str1, |
|
195 const xmlChar * str2, const xmlChar * str3) |
|
196 { |
|
197 xmlStructuredErrorFunc schannel = NULL; |
|
198 xmlGenericErrorFunc channel = NULL; |
|
199 xmlParserCtxtPtr pctxt = NULL; |
|
200 void *data = NULL; |
|
201 |
|
202 if (ctxt != NULL) { |
|
203 channel = ctxt->error; |
|
204 data = ctxt->userData; |
|
205 pctxt = ctxt->userData; |
|
206 } |
|
207 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, |
|
208 XML_ERR_WARNING, NULL, 0, |
|
209 (const char *) str1, |
|
210 (const char *) str1, |
|
211 (const char *) str3, 0, 0, msg, str1, str2, str3); |
|
212 } |
|
213 |
|
214 |
|
215 |
|
216 #ifdef LIBXML_REGEXP_ENABLED |
|
217 /* |
|
218 * If regexp are enabled we can do continuous validation without the |
|
219 * need of a tree to validate the content model. this is done in each |
|
220 * callbacks. |
|
221 * Each xmlValidState represent the validation state associated to the |
|
222 * set of nodes currently open from the document root to the current element. |
|
223 */ |
|
224 |
|
225 |
|
226 typedef struct _xmlValidState { |
|
227 xmlElementPtr elemDecl; /* pointer to the content model */ |
|
228 xmlNodePtr node; /* pointer to the current node */ |
|
229 xmlRegExecCtxtPtr exec; /* regexp runtime */ |
|
230 } _xmlValidState; |
|
231 |
|
232 |
|
233 static int |
|
234 vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) { |
|
235 if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) { |
|
236 ctxt->vstateMax = 10; |
|
237 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax * |
|
238 sizeof(ctxt->vstateTab[0])); |
|
239 if (ctxt->vstateTab == NULL) { |
|
240 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
241 return(-1); |
|
242 } |
|
243 } |
|
244 |
|
245 if (ctxt->vstateNr >= ctxt->vstateMax) { |
|
246 xmlValidState *tmp; |
|
247 |
|
248 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab, |
|
249 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); |
|
250 if (tmp == NULL) { |
|
251 xmlVErrMemory(ctxt, EMBED_ERRTXT("realloc failed")); |
|
252 return(-1); |
|
253 } |
|
254 ctxt->vstateMax *= 2; |
|
255 ctxt->vstateTab = tmp; |
|
256 } |
|
257 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr]; |
|
258 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl; |
|
259 ctxt->vstateTab[ctxt->vstateNr].node = node; |
|
260 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) { |
|
261 if (elemDecl->contModel == NULL) |
|
262 xmlValidBuildContentModel(ctxt, elemDecl); |
|
263 if (elemDecl->contModel != NULL) { |
|
264 ctxt->vstateTab[ctxt->vstateNr].exec = |
|
265 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL); |
|
266 } else { |
|
267 ctxt->vstateTab[ctxt->vstateNr].exec = NULL; |
|
268 xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl, |
|
269 XML_ERR_INTERNAL_ERROR, |
|
270 EMBED_ERRTXT("Failed to build content model regexp for %s\n"), |
|
271 node->name, NULL, NULL); |
|
272 } |
|
273 } |
|
274 return(ctxt->vstateNr++); |
|
275 } |
|
276 |
|
277 static int |
|
278 vstateVPop(xmlValidCtxtPtr ctxt) { |
|
279 xmlElementPtr elemDecl; |
|
280 |
|
281 if (ctxt->vstateNr < 1) return(-1); |
|
282 ctxt->vstateNr--; |
|
283 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl; |
|
284 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL; |
|
285 ctxt->vstateTab[ctxt->vstateNr].node = NULL; |
|
286 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) { |
|
287 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec); |
|
288 } |
|
289 ctxt->vstateTab[ctxt->vstateNr].exec = NULL; |
|
290 if (ctxt->vstateNr >= 1) |
|
291 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1]; |
|
292 else |
|
293 ctxt->vstate = NULL; |
|
294 return(ctxt->vstateNr); |
|
295 } |
|
296 |
|
297 #else /* not LIBXML_REGEXP_ENABLED */ |
|
298 /* |
|
299 * If regexp are not enabled, it uses a home made algorithm less |
|
300 * complex and easier to |
|
301 * debug/maintain than a generic NFA -> DFA state based algo. The |
|
302 * only restriction is on the deepness of the tree limited by the |
|
303 * size of the occurs bitfield |
|
304 * |
|
305 * this is the content of a saved state for rollbacks |
|
306 */ |
|
307 |
|
308 #define ROLLBACK_OR 0 |
|
309 #define ROLLBACK_PARENT 1 |
|
310 |
|
311 typedef struct _xmlValidState { |
|
312 xmlElementContentPtr cont; /* pointer to the content model subtree */ |
|
313 xmlNodePtr node; /* pointer to the current node in the list */ |
|
314 long occurs;/* bitfield for multiple occurrences */ |
|
315 unsigned char depth; /* current depth in the overall tree */ |
|
316 unsigned char state; /* ROLLBACK_XXX */ |
|
317 } _xmlValidState; |
|
318 |
|
319 #define MAX_RECURSE 25000 |
|
320 #define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8) |
|
321 #define CONT ctxt->vstate->cont |
|
322 #define NODE ctxt->vstate->node |
|
323 #define DEPTH ctxt->vstate->depth |
|
324 #define OCCURS ctxt->vstate->occurs |
|
325 #define STATE ctxt->vstate->state |
|
326 |
|
327 #define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH)) |
|
328 #define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1)) |
|
329 |
|
330 #define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH) |
|
331 #define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1) |
|
332 |
|
333 static int |
|
334 vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont, |
|
335 xmlNodePtr node, unsigned char depth, long occurs, |
|
336 unsigned char state) { |
|
337 int i = ctxt->vstateNr - 1; |
|
338 |
|
339 if (ctxt->vstateNr > MAX_RECURSE) { |
|
340 return(-1); |
|
341 } |
|
342 if (ctxt->vstateTab == NULL) { |
|
343 ctxt->vstateMax = 8; |
|
344 ctxt->vstateTab = (xmlValidState *) xmlMalloc( |
|
345 ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); |
|
346 if (ctxt->vstateTab == NULL) { |
|
347 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
348 return(-1); |
|
349 } |
|
350 } |
|
351 if (ctxt->vstateNr >= ctxt->vstateMax) { |
|
352 xmlValidState *tmp; |
|
353 |
|
354 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab, |
|
355 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); |
|
356 if (tmp == NULL) { |
|
357 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
358 return(-1); |
|
359 } |
|
360 ctxt->vstateMax *= 2; |
|
361 ctxt->vstateTab = tmp; |
|
362 ctxt->vstate = &ctxt->vstateTab[0]; |
|
363 } |
|
364 /* |
|
365 * Don't push on the stack a state already here |
|
366 */ |
|
367 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) && |
|
368 (ctxt->vstateTab[i].node == node) && |
|
369 (ctxt->vstateTab[i].depth == depth) && |
|
370 (ctxt->vstateTab[i].occurs == occurs) && |
|
371 (ctxt->vstateTab[i].state == state)) |
|
372 return(ctxt->vstateNr); |
|
373 ctxt->vstateTab[ctxt->vstateNr].cont = cont; |
|
374 ctxt->vstateTab[ctxt->vstateNr].node = node; |
|
375 ctxt->vstateTab[ctxt->vstateNr].depth = depth; |
|
376 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs; |
|
377 ctxt->vstateTab[ctxt->vstateNr].state = state; |
|
378 return(ctxt->vstateNr++); |
|
379 } |
|
380 |
|
381 static int |
|
382 vstateVPop(xmlValidCtxtPtr ctxt) { |
|
383 if (ctxt->vstateNr <= 1) return(-1); |
|
384 ctxt->vstateNr--; |
|
385 ctxt->vstate = &ctxt->vstateTab[0]; |
|
386 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont; |
|
387 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node; |
|
388 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth; |
|
389 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs; |
|
390 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state; |
|
391 return(ctxt->vstateNr); |
|
392 } |
|
393 |
|
394 #endif /* ! LIBXML_REGEXP_ENABLED */ |
|
395 |
|
396 static int |
|
397 nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value) |
|
398 { |
|
399 if (ctxt->nodeMax <= 0) { |
|
400 ctxt->nodeMax = 4; |
|
401 ctxt->nodeTab = |
|
402 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax * |
|
403 sizeof(ctxt->nodeTab[0])); |
|
404 if (ctxt->nodeTab == NULL) { |
|
405 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
406 ctxt->nodeMax = 0; |
|
407 return (0); |
|
408 } |
|
409 } |
|
410 if (ctxt->nodeNr >= ctxt->nodeMax) { |
|
411 xmlNodePtr *tmp; |
|
412 tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab, |
|
413 ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0])); |
|
414 if (tmp == NULL) { |
|
415 xmlVErrMemory(ctxt, EMBED_ERRTXT("realloc failed")); |
|
416 return (0); |
|
417 } |
|
418 ctxt->nodeMax *= 2; |
|
419 ctxt->nodeTab = tmp; |
|
420 } |
|
421 ctxt->nodeTab[ctxt->nodeNr] = value; |
|
422 ctxt->node = value; |
|
423 return (ctxt->nodeNr++); |
|
424 } |
|
425 static xmlNodePtr |
|
426 nodeVPop(xmlValidCtxtPtr ctxt) |
|
427 { |
|
428 xmlNodePtr ret; |
|
429 |
|
430 if (ctxt->nodeNr <= 0) |
|
431 return (0); |
|
432 ctxt->nodeNr--; |
|
433 if (ctxt->nodeNr > 0) |
|
434 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1]; |
|
435 else |
|
436 ctxt->node = NULL; |
|
437 ret = ctxt->nodeTab[ctxt->nodeNr]; |
|
438 ctxt->nodeTab[ctxt->nodeNr] = 0; |
|
439 return (ret); |
|
440 } |
|
441 |
|
442 #ifdef DEBUG_VALID_ALGO |
|
443 static void |
|
444 xmlValidPrintNode(xmlNodePtr cur) { |
|
445 if (cur == NULL) { |
|
446 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("null")); |
|
447 return; |
|
448 } |
|
449 |
|
450 switch (cur->type) { |
|
451 case XML_ELEMENT_NODE: |
|
452 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("%s "), cur->name); |
|
453 break; |
|
454 case XML_TEXT_NODE: |
|
455 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("text ")); |
|
456 break; |
|
457 case XML_CDATA_SECTION_NODE: |
|
458 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("cdata ")); |
|
459 break; |
|
460 case XML_ENTITY_REF_NODE: |
|
461 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("&%s; "), cur->name); |
|
462 break; |
|
463 case XML_PI_NODE: |
|
464 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("pi(%s) "), cur->name); |
|
465 break; |
|
466 case XML_COMMENT_NODE: |
|
467 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("comment ")); |
|
468 break; |
|
469 case XML_ATTRIBUTE_NODE: |
|
470 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?attr? ")); |
|
471 break; |
|
472 case XML_ENTITY_NODE: |
|
473 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?ent? ")); |
|
474 break; |
|
475 case XML_DOCUMENT_NODE: |
|
476 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?doc? ")); |
|
477 break; |
|
478 case XML_DOCUMENT_TYPE_NODE: |
|
479 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?doctype? ")); |
|
480 break; |
|
481 case XML_DOCUMENT_FRAG_NODE: |
|
482 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?frag? ")); |
|
483 break; |
|
484 case XML_NOTATION_NODE: |
|
485 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?nota? ")); |
|
486 break; |
|
487 case XML_HTML_DOCUMENT_NODE: |
|
488 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?html? ")); |
|
489 break; |
|
490 #ifdef LIBXML_DOCB_ENABLED |
|
491 case XML_DOCB_DOCUMENT_NODE: |
|
492 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?docb? ")); |
|
493 break; |
|
494 #endif |
|
495 case XML_DTD_NODE: |
|
496 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?dtd? ")); |
|
497 break; |
|
498 case XML_ELEMENT_DECL: |
|
499 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?edecl? ")); |
|
500 break; |
|
501 case XML_ATTRIBUTE_DECL: |
|
502 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?adecl? ")); |
|
503 break; |
|
504 case XML_ENTITY_DECL: |
|
505 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?entdecl? ")); |
|
506 break; |
|
507 case XML_NAMESPACE_DECL: |
|
508 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("?nsdecl? ")); |
|
509 break; |
|
510 case XML_XINCLUDE_START: |
|
511 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("incstart ")); |
|
512 break; |
|
513 case XML_XINCLUDE_END: |
|
514 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("incend ")); |
|
515 break; |
|
516 } |
|
517 } |
|
518 |
|
519 static void |
|
520 xmlValidPrintNodeList(xmlNodePtr cur) { |
|
521 if (cur == NULL) |
|
522 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("null ")); |
|
523 while (cur != NULL) { |
|
524 xmlValidPrintNode(cur); |
|
525 cur = cur->next; |
|
526 } |
|
527 } |
|
528 |
|
529 static void |
|
530 xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) { |
|
531 char expr[5000]; |
|
532 |
|
533 expr[0] = 0; |
|
534 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("valid: ")); |
|
535 xmlValidPrintNodeList(cur); |
|
536 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("against ")); |
|
537 xmlSnprintfElementContent(expr, 5000, cont, 1); |
|
538 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("%s\n"), expr); |
|
539 } |
|
540 |
|
541 static void |
|
542 xmlValidDebugState(xmlValidStatePtr state) { |
|
543 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("(")); |
|
544 if (state->cont == NULL) |
|
545 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("null,")); |
|
546 else |
|
547 switch (state->cont->type) { |
|
548 case XML_ELEMENT_CONTENT_PCDATA: |
|
549 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("pcdata,")); |
|
550 break; |
|
551 case XML_ELEMENT_CONTENT_ELEMENT: |
|
552 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("%s,"), |
|
553 state->cont->name); |
|
554 break; |
|
555 case XML_ELEMENT_CONTENT_SEQ: |
|
556 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("seq,")); |
|
557 break; |
|
558 case XML_ELEMENT_CONTENT_OR: |
|
559 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("or,")); |
|
560 break; |
|
561 } |
|
562 xmlValidPrintNode(state->node); |
|
563 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT(",%d,%X,%d)"), |
|
564 state->depth, state->occurs, state->state); |
|
565 } |
|
566 |
|
567 static void |
|
568 xmlValidStateDebug(xmlValidCtxtPtr ctxt) { |
|
569 int i, j; |
|
570 |
|
571 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("state: ")); |
|
572 xmlValidDebugState(ctxt->vstate); |
|
573 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT(" stack: %d "), |
|
574 ctxt->vstateNr - 1); |
|
575 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--) |
|
576 xmlValidDebugState(&ctxt->vstateTab[j]); |
|
577 xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("\n")); |
|
578 } |
|
579 |
|
580 /***** |
|
581 #define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c); |
|
582 *****/ |
|
583 |
|
584 #define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt); |
|
585 #define DEBUG_VALID_MSG(m) \ |
|
586 xmlGenericError(xmlGenericErrorContext, "%s\n", m); |
|
587 |
|
588 #else |
|
589 #define DEBUG_VALID_STATE(n,c) |
|
590 #define DEBUG_VALID_MSG(m) |
|
591 #endif |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 #define CHECK_DTD \ |
|
597 if (doc == NULL) return(0); \ |
|
598 else if ((doc->intSubset == NULL) && \ |
|
599 (doc->extSubset == NULL)) return(0) |
|
600 |
|
601 xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem); |
|
602 |
|
603 #ifdef LIBXML_REGEXP_ENABLED |
|
604 |
|
605 /************************************************************************ |
|
606 * * |
|
607 * Content model validation based on the regexps * |
|
608 * * |
|
609 ************************************************************************/ |
|
610 |
|
611 /** |
|
612 * xmlValidBuildAContentModel: |
|
613 * @param content the content model |
|
614 * @param ctxt the schema parser context |
|
615 * @param name the element name whose content is being built |
|
616 * |
|
617 * Generate the automata sequence needed for that type |
|
618 * |
|
619 * Returns 1 if successful or 0 in case of error. |
|
620 */ |
|
621 static int |
|
622 xmlValidBuildAContentModel(xmlElementContentPtr content, |
|
623 xmlValidCtxtPtr ctxt, |
|
624 const xmlChar *name) { |
|
625 if (content == NULL) { |
|
626 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR, |
|
627 EMBED_ERRTXT("Found NULL content in content model of %s\n"), |
|
628 name, NULL, NULL); |
|
629 return(0); |
|
630 } |
|
631 switch (content->type) { |
|
632 case XML_ELEMENT_CONTENT_PCDATA: |
|
633 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR, |
|
634 EMBED_ERRTXT("Found PCDATA in content model of %s\n"), |
|
635 name, NULL, NULL); |
|
636 return(0); |
|
637 break; |
|
638 case XML_ELEMENT_CONTENT_ELEMENT: { |
|
639 xmlAutomataStatePtr oldstate = ctxt->state; |
|
640 xmlChar fn[50]; |
|
641 xmlChar *fullname; |
|
642 |
|
643 fullname = xmlBuildQName(content->name, content->prefix, fn, 50); |
|
644 if (fullname == NULL) { |
|
645 xmlVErrMemory(ctxt, EMBED_ERRTXT("Building content model")); |
|
646 return(0); |
|
647 } |
|
648 |
|
649 switch (content->ocur) { |
|
650 case XML_ELEMENT_CONTENT_ONCE: |
|
651 ctxt->state = xmlAutomataNewTransition(ctxt->am, |
|
652 ctxt->state, NULL, fullname, NULL); |
|
653 break; |
|
654 case XML_ELEMENT_CONTENT_OPT: |
|
655 ctxt->state = xmlAutomataNewTransition(ctxt->am, |
|
656 ctxt->state, NULL, fullname, NULL); |
|
657 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); |
|
658 break; |
|
659 case XML_ELEMENT_CONTENT_PLUS: |
|
660 ctxt->state = xmlAutomataNewTransition(ctxt->am, |
|
661 ctxt->state, NULL, fullname, NULL); |
|
662 xmlAutomataNewTransition(ctxt->am, ctxt->state, |
|
663 ctxt->state, fullname, NULL); |
|
664 break; |
|
665 case XML_ELEMENT_CONTENT_MULT: |
|
666 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, |
|
667 ctxt->state, NULL); |
|
668 xmlAutomataNewTransition(ctxt->am, |
|
669 ctxt->state, ctxt->state, fullname, NULL); |
|
670 break; |
|
671 } |
|
672 if ((fullname != fn) && (fullname != content->name)) |
|
673 xmlFree(fullname); |
|
674 break; |
|
675 } |
|
676 case XML_ELEMENT_CONTENT_SEQ: { |
|
677 xmlAutomataStatePtr oldstate, oldend; |
|
678 xmlElementContentOccur ocur; |
|
679 |
|
680 /* |
|
681 * Simply iterate over the content |
|
682 */ |
|
683 oldstate = ctxt->state; |
|
684 ocur = content->ocur; |
|
685 if (ocur != XML_ELEMENT_CONTENT_ONCE) { |
|
686 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL); |
|
687 oldstate = ctxt->state; |
|
688 } |
|
689 do { |
|
690 xmlValidBuildAContentModel(content->c1, ctxt, name); |
|
691 content = content->c2; |
|
692 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) && |
|
693 (content->ocur == XML_ELEMENT_CONTENT_ONCE)); |
|
694 xmlValidBuildAContentModel(content, ctxt, name); |
|
695 oldend = ctxt->state; |
|
696 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL); |
|
697 switch (ocur) { |
|
698 case XML_ELEMENT_CONTENT_ONCE: |
|
699 break; |
|
700 case XML_ELEMENT_CONTENT_OPT: |
|
701 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); |
|
702 break; |
|
703 case XML_ELEMENT_CONTENT_MULT: |
|
704 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); |
|
705 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); |
|
706 break; |
|
707 case XML_ELEMENT_CONTENT_PLUS: |
|
708 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); |
|
709 break; |
|
710 } |
|
711 break; |
|
712 } |
|
713 case XML_ELEMENT_CONTENT_OR: { |
|
714 xmlAutomataStatePtr oldstate, oldend; |
|
715 xmlElementContentOccur ocur; |
|
716 |
|
717 ocur = content->ocur; |
|
718 if ((ocur == XML_ELEMENT_CONTENT_PLUS) || |
|
719 (ocur == XML_ELEMENT_CONTENT_MULT)) { |
|
720 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, |
|
721 ctxt->state, NULL); |
|
722 } |
|
723 oldstate = ctxt->state; |
|
724 oldend = xmlAutomataNewState(ctxt->am); |
|
725 |
|
726 /* |
|
727 * iterate over the subtypes and remerge the end with an |
|
728 * epsilon transition |
|
729 */ |
|
730 do { |
|
731 ctxt->state = oldstate; |
|
732 xmlValidBuildAContentModel(content->c1, ctxt, name); |
|
733 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend); |
|
734 content = content->c2; |
|
735 } while ((content->type == XML_ELEMENT_CONTENT_OR) && |
|
736 (content->ocur == XML_ELEMENT_CONTENT_ONCE)); |
|
737 ctxt->state = oldstate; |
|
738 xmlValidBuildAContentModel(content, ctxt, name); |
|
739 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend); |
|
740 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL); |
|
741 switch (ocur) { |
|
742 case XML_ELEMENT_CONTENT_ONCE: |
|
743 break; |
|
744 case XML_ELEMENT_CONTENT_OPT: |
|
745 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); |
|
746 break; |
|
747 case XML_ELEMENT_CONTENT_MULT: |
|
748 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); |
|
749 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); |
|
750 break; |
|
751 case XML_ELEMENT_CONTENT_PLUS: |
|
752 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); |
|
753 break; |
|
754 } |
|
755 break; |
|
756 } |
|
757 default: |
|
758 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
759 EMBED_ERRTXT("ContentModel broken for element %s\n"), |
|
760 (const char *) name); |
|
761 return(0); |
|
762 } |
|
763 return(1); |
|
764 } |
|
765 /** |
|
766 * xmlValidBuildContentModel: |
|
767 * @param ctxt a validation context |
|
768 * @param elem an element declaration node |
|
769 * |
|
770 * (Re)Build the automata associated to the content model of this |
|
771 * element |
|
772 * |
|
773 * Returns 1 in case of success, 0 in case of error |
|
774 */ |
|
775 int |
|
776 xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { |
|
777 |
|
778 if ((ctxt == NULL) || (elem == NULL)) |
|
779 return(0); |
|
780 if (elem->type != XML_ELEMENT_DECL) |
|
781 return(0); |
|
782 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT) |
|
783 return(1); |
|
784 |
|
785 if (elem->contModel != NULL) { |
|
786 if (!xmlRegexpIsDeterminist(elem->contModel)) { |
|
787 ctxt->valid = 0; |
|
788 return(0); |
|
789 } |
|
790 return(1); |
|
791 } |
|
792 |
|
793 ctxt->am = xmlNewAutomata(); |
|
794 if (ctxt->am == NULL) { |
|
795 xmlErrValidNode(ctxt, (xmlNodePtr) elem, |
|
796 XML_ERR_INTERNAL_ERROR, |
|
797 EMBED_ERRTXT("Cannot create automata for element %s\n"), |
|
798 elem->name, NULL, NULL); |
|
799 return(0); |
|
800 } |
|
801 ctxt->state = xmlAutomataGetInitState(ctxt->am); |
|
802 xmlValidBuildAContentModel(elem->content, ctxt, elem->name); |
|
803 xmlAutomataSetFinalState(ctxt->am, ctxt->state); |
|
804 elem->contModel = xmlAutomataCompile(ctxt->am); |
|
805 if (xmlRegexpIsDeterminist(elem->contModel) != 1) { |
|
806 |
|
807 #if 0 |
|
808 char expr[5000]; |
|
809 expr[0] = 0; |
|
810 xmlSnprintfElementContent(expr, 5000, elem->content, 1); |
|
811 xmlErrValidNode(ctxt, (xmlNodePtr) elem, |
|
812 XML_DTD_CONTENT_NOT_DETERMINIST, |
|
813 EMBED_ERRTXT("Content model of %s is not determinist: %s\n"), |
|
814 elem->name, BAD_CAST expr, NULL); |
|
815 #endif |
|
816 char *expr = (char *) malloc(5000); |
|
817 expr[0] = 0; |
|
818 xmlSnprintfElementContent(expr, 5000, elem->content, 1); |
|
819 xmlErrValidNode(ctxt, (xmlNodePtr) elem, |
|
820 XML_DTD_CONTENT_NOT_DETERMINIST, |
|
821 EMBED_ERRTXT("Content model of %s is not determinist: %s\n"), |
|
822 elem->name, BAD_CAST expr, NULL); |
|
823 free(expr); |
|
824 |
|
825 #ifdef DEBUG_REGEXP_ALGO |
|
826 xmlRegexpPrint(stderr, elem->contModel); |
|
827 #endif |
|
828 ctxt->valid = 0; |
|
829 ctxt->state = NULL; |
|
830 xmlFreeAutomata(ctxt->am); |
|
831 ctxt->am = NULL; |
|
832 return(0); |
|
833 } |
|
834 ctxt->state = NULL; |
|
835 xmlFreeAutomata(ctxt->am); |
|
836 ctxt->am = NULL; |
|
837 return(1); |
|
838 } |
|
839 |
|
840 #endif /* LIBXML_REGEXP_ENABLED */ |
|
841 |
|
842 /**************************************************************** |
|
843 * * |
|
844 * Util functions for data allocation/deallocation * |
|
845 * * |
|
846 ****************************************************************/ |
|
847 |
|
848 /** |
|
849 * xmlNewValidCtxt: |
|
850 * |
|
851 * Allocate a validation context structure. |
|
852 * |
|
853 * Returns NULL if not, otherwise the new validation context structure |
|
854 */ |
|
855 xmlValidCtxtPtr xmlNewValidCtxt(void) { |
|
856 xmlValidCtxtPtr ret; |
|
857 |
|
858 if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) { |
|
859 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
860 return (NULL); |
|
861 } |
|
862 |
|
863 (void) memset(ret, 0, sizeof (xmlValidCtxt)); |
|
864 |
|
865 return (ret); |
|
866 } |
|
867 |
|
868 /** |
|
869 * xmlFreeValidCtxt: |
|
870 * @param cur the validation context to free |
|
871 * |
|
872 * Free a validation context structure. |
|
873 */ |
|
874 void |
|
875 xmlFreeValidCtxt(xmlValidCtxtPtr cur) { |
|
876 xmlFree(cur); |
|
877 } |
|
878 |
|
879 #endif /* LIBXML_VALID_ENABLED */ |
|
880 |
|
881 /** |
|
882 * xmlNewElementContent: |
|
883 * @param name the subelement name or NULL |
|
884 * @param type the type of element content decl |
|
885 * |
|
886 * Allocate an element content structure. |
|
887 * |
|
888 * Returns NULL if not, otherwise the new element content structure |
|
889 * |
|
890 * OOM: possible --> returns NULL for valid arguments |
|
891 */ |
|
892 XMLPUBFUNEXPORT xmlElementContentPtr |
|
893 xmlNewElementContent(const xmlChar* name, xmlElementContentType type) |
|
894 { |
|
895 LOAD_GS_DIRECT |
|
896 xmlElementContentPtr ret; |
|
897 |
|
898 switch(type) |
|
899 { |
|
900 case XML_ELEMENT_CONTENT_ELEMENT: |
|
901 if (!name) { |
|
902 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
903 EMBED_ERRTXT("xmlNewElementContent : name == NULL !\n"), |
|
904 NULL); |
|
905 } |
|
906 break; |
|
907 |
|
908 case XML_ELEMENT_CONTENT_PCDATA: |
|
909 case XML_ELEMENT_CONTENT_SEQ: |
|
910 case XML_ELEMENT_CONTENT_OR: |
|
911 if (name) { |
|
912 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
913 EMBED_ERRTXT("xmlNewElementContent : name != NULL !\n"), |
|
914 NULL); |
|
915 } |
|
916 break; |
|
917 |
|
918 default: |
|
919 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
920 EMBED_ERRTXT("Internal: ELEMENT content corrupted invalid type\n"), |
|
921 NULL); |
|
922 return(NULL); |
|
923 } |
|
924 |
|
925 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent)); |
|
926 if (!ret) { |
|
927 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
928 return(NULL); |
|
929 } |
|
930 memset(ret, 0, sizeof(xmlElementContent)); |
|
931 ret->type = type; |
|
932 ret->ocur = XML_ELEMENT_CONTENT_ONCE; |
|
933 |
|
934 if (name) { |
|
935 xmlChar* prefix = NULL; |
|
936 ret->name = xmlSplitQName2(name, &prefix); |
|
937 if (!ret->name) |
|
938 { |
|
939 ret->name = xmlStrdup(name); |
|
940 if(OOM_FLAG) |
|
941 { |
|
942 xmlFree(ret); |
|
943 return(NULL); |
|
944 } |
|
945 } |
|
946 ret->prefix = prefix; |
|
947 } |
|
948 /* Unneded code: these fields are NULL after memset() |
|
949 else { |
|
950 ret->name = NULL; |
|
951 ret->prefix = NULL; |
|
952 } |
|
953 |
|
954 ret->c1 = ret->c2 = ret->parent = NULL; |
|
955 */ |
|
956 return(ret); |
|
957 } |
|
958 |
|
959 /** |
|
960 * xmlCopyElementContent: |
|
961 * @param cur An element content pointer. |
|
962 * |
|
963 * Build a copy of an element content description. |
|
964 * |
|
965 * Returns the new xmlElementContentPtr or NULL in case of error. |
|
966 */ |
|
967 XMLPUBFUNEXPORT xmlElementContentPtr |
|
968 xmlCopyElementContent(xmlElementContentPtr cur) { |
|
969 xmlElementContentPtr ret; |
|
970 |
|
971 if (cur == NULL) return(NULL); |
|
972 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type); |
|
973 if (ret == NULL) { |
|
974 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
975 return(NULL); |
|
976 } |
|
977 if (cur->prefix != NULL) |
|
978 ret->prefix = xmlStrdup(cur->prefix); |
|
979 ret->ocur = cur->ocur; |
|
980 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1); |
|
981 if (ret->c1 != NULL) |
|
982 ret->c1->parent = ret; |
|
983 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2); |
|
984 if (ret->c2 != NULL) |
|
985 ret->c2->parent = ret; |
|
986 return(ret); |
|
987 } |
|
988 |
|
989 /** |
|
990 * xmlFreeElementContent: |
|
991 * @param cur the element content tree to free |
|
992 * |
|
993 * Free an element content structure. This is a recursive call ! |
|
994 */ |
|
995 XMLPUBFUNEXPORT void |
|
996 xmlFreeElementContent(xmlElementContentPtr cur) { |
|
997 if (cur == NULL) return; |
|
998 switch (cur->type) { |
|
999 case XML_ELEMENT_CONTENT_PCDATA: |
|
1000 case XML_ELEMENT_CONTENT_ELEMENT: |
|
1001 case XML_ELEMENT_CONTENT_SEQ: |
|
1002 case XML_ELEMENT_CONTENT_OR: |
|
1003 break; |
|
1004 default: |
|
1005 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
1006 EMBED_ERRTXT("Internal: ELEMENT content corrupted invalid type\n"), |
|
1007 NULL); |
|
1008 return; |
|
1009 } |
|
1010 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1); |
|
1011 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2); |
|
1012 if (cur->name != NULL) xmlFree((xmlChar *) cur->name); |
|
1013 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix); |
|
1014 xmlFree(cur); |
|
1015 } |
|
1016 |
|
1017 #ifdef LIBXML_OUTPUT_ENABLED |
|
1018 /** |
|
1019 * xmlDumpElementContent: |
|
1020 * @param buf An XML buffer |
|
1021 * @param content An element table |
|
1022 * @param glob 1 if one must print the englobing parenthesis, 0 otherwise |
|
1023 * |
|
1024 * This will dump the content of the element table as an XML DTD definition |
|
1025 */ |
|
1026 static void |
|
1027 xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) { |
|
1028 if (content == NULL) return; |
|
1029 |
|
1030 if (glob) xmlBufferWriteChar(buf, "("); |
|
1031 switch (content->type) { |
|
1032 case XML_ELEMENT_CONTENT_PCDATA: |
|
1033 xmlBufferWriteChar(buf, "#PCDATA"); |
|
1034 break; |
|
1035 case XML_ELEMENT_CONTENT_ELEMENT: |
|
1036 if (content->prefix != NULL) { |
|
1037 xmlBufferWriteCHAR(buf, content->prefix); |
|
1038 xmlBufferWriteChar(buf, ":"); |
|
1039 } |
|
1040 xmlBufferWriteCHAR(buf, content->name); |
|
1041 break; |
|
1042 case XML_ELEMENT_CONTENT_SEQ: |
|
1043 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || |
|
1044 (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) |
|
1045 xmlDumpElementContent(buf, content->c1, 1); |
|
1046 else |
|
1047 xmlDumpElementContent(buf, content->c1, 0); |
|
1048 xmlBufferWriteChar(buf, " , "); |
|
1049 if (content->c2->type == XML_ELEMENT_CONTENT_OR) |
|
1050 xmlDumpElementContent(buf, content->c2, 1); |
|
1051 else |
|
1052 xmlDumpElementContent(buf, content->c2, 0); |
|
1053 break; |
|
1054 case XML_ELEMENT_CONTENT_OR: |
|
1055 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || |
|
1056 (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) |
|
1057 xmlDumpElementContent(buf, content->c1, 1); |
|
1058 else |
|
1059 xmlDumpElementContent(buf, content->c1, 0); |
|
1060 xmlBufferWriteChar(buf, " | "); |
|
1061 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ) |
|
1062 xmlDumpElementContent(buf, content->c2, 1); |
|
1063 else |
|
1064 xmlDumpElementContent(buf, content->c2, 0); |
|
1065 break; |
|
1066 default: |
|
1067 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
1068 EMBED_ERRTXT("Internal: ELEMENT content corrupted invalid type\n"), |
|
1069 NULL); |
|
1070 } |
|
1071 if (glob) |
|
1072 xmlBufferWriteChar(buf, ")"); |
|
1073 switch (content->ocur) { |
|
1074 case XML_ELEMENT_CONTENT_ONCE: |
|
1075 break; |
|
1076 case XML_ELEMENT_CONTENT_OPT: |
|
1077 xmlBufferWriteChar(buf, "?"); |
|
1078 break; |
|
1079 case XML_ELEMENT_CONTENT_MULT: |
|
1080 xmlBufferWriteChar(buf, "*"); |
|
1081 break; |
|
1082 case XML_ELEMENT_CONTENT_PLUS: |
|
1083 xmlBufferWriteChar(buf, "+"); |
|
1084 break; |
|
1085 } |
|
1086 } |
|
1087 |
|
1088 |
|
1089 /** |
|
1090 * xmlSprintfElementContent: |
|
1091 * @param buf an output buffer |
|
1092 * @param content An element table |
|
1093 * @param glob 1 if one must print the enclosing parenthesis, 0 otherwise |
|
1094 * |
|
1095 * Deprecated, unsafe, use xmlSnprintfElementContent |
|
1096 */ |
|
1097 XMLPUBFUNEXPORT void |
|
1098 xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED, |
|
1099 xmlElementContentPtr content ATTRIBUTE_UNUSED, |
|
1100 int glob ATTRIBUTE_UNUSED) { |
|
1101 } |
|
1102 |
|
1103 #endif /* LIBXML_OUTPUT_ENABLED */ |
|
1104 |
|
1105 /** |
|
1106 * xmlSnprintfElementContent: |
|
1107 * @param buf an output buffer |
|
1108 * @param size the buffer size |
|
1109 * @param content An element table |
|
1110 * @param glob 1 if one must print the enclobing parenthesis, 0 otherwise |
|
1111 * |
|
1112 * This will dump the content of the element content definition |
|
1113 * Intended just for the debug routine |
|
1114 */ |
|
1115 XMLPUBFUNEXPORT void |
|
1116 xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) { |
|
1117 int len; |
|
1118 |
|
1119 if (content == NULL) return; |
|
1120 len = strlen(buf); |
|
1121 if (size - len < 50) { |
|
1122 if ((size - len > 4) && (buf[len - 1] != '.')) |
|
1123 strcat(buf, " ..."); |
|
1124 return; |
|
1125 } |
|
1126 if (glob) strcat(buf, "("); |
|
1127 switch (content->type) { |
|
1128 case XML_ELEMENT_CONTENT_PCDATA: |
|
1129 strcat(buf, "#PCDATA"); |
|
1130 break; |
|
1131 case XML_ELEMENT_CONTENT_ELEMENT: |
|
1132 if (content->prefix != NULL) { |
|
1133 if (size - len < xmlStrlen(content->prefix) + 10) { |
|
1134 strcat(buf, " ..."); |
|
1135 return; |
|
1136 } |
|
1137 strcat(buf, (char *) content->prefix); |
|
1138 strcat(buf, ":"); |
|
1139 } |
|
1140 if (size - len < xmlStrlen(content->name) + 10) { |
|
1141 strcat(buf, " ..."); |
|
1142 return; |
|
1143 } |
|
1144 if (content->name != NULL) |
|
1145 strcat(buf, (char *) content->name); |
|
1146 break; |
|
1147 case XML_ELEMENT_CONTENT_SEQ: |
|
1148 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || |
|
1149 (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) |
|
1150 xmlSnprintfElementContent(buf, size, content->c1, 1); |
|
1151 else |
|
1152 xmlSnprintfElementContent(buf, size, content->c1, 0); |
|
1153 len = strlen(buf); |
|
1154 if (size - len < 50) { |
|
1155 if ((size - len > 4) && (buf[len - 1] != '.')) |
|
1156 strcat(buf, " ..."); |
|
1157 return; |
|
1158 } |
|
1159 strcat(buf, " , "); |
|
1160 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) || |
|
1161 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) && |
|
1162 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT)) |
|
1163 xmlSnprintfElementContent(buf, size, content->c2, 1); |
|
1164 else |
|
1165 xmlSnprintfElementContent(buf, size, content->c2, 0); |
|
1166 break; |
|
1167 case XML_ELEMENT_CONTENT_OR: |
|
1168 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || |
|
1169 (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) |
|
1170 xmlSnprintfElementContent(buf, size, content->c1, 1); |
|
1171 else |
|
1172 xmlSnprintfElementContent(buf, size, content->c1, 0); |
|
1173 len = strlen(buf); |
|
1174 if (size - len < 50) { |
|
1175 if ((size - len > 4) && (buf[len - 1] != '.')) |
|
1176 strcat(buf, " ..."); |
|
1177 return; |
|
1178 } |
|
1179 strcat(buf, " | "); |
|
1180 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) || |
|
1181 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) && |
|
1182 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT)) |
|
1183 xmlSnprintfElementContent(buf, size, content->c2, 1); |
|
1184 else |
|
1185 xmlSnprintfElementContent(buf, size, content->c2, 0); |
|
1186 break; |
|
1187 } |
|
1188 if (glob) |
|
1189 strcat(buf, ")"); |
|
1190 switch (content->ocur) { |
|
1191 case XML_ELEMENT_CONTENT_ONCE: |
|
1192 break; |
|
1193 case XML_ELEMENT_CONTENT_OPT: |
|
1194 strcat(buf, "?"); |
|
1195 break; |
|
1196 case XML_ELEMENT_CONTENT_MULT: |
|
1197 strcat(buf, "*"); |
|
1198 break; |
|
1199 case XML_ELEMENT_CONTENT_PLUS: |
|
1200 strcat(buf, "+"); |
|
1201 break; |
|
1202 } |
|
1203 } |
|
1204 |
|
1205 /**************************************************************** |
|
1206 * * |
|
1207 * Registration of DTD declarations * |
|
1208 * * |
|
1209 ****************************************************************/ |
|
1210 |
|
1211 /** |
|
1212 * xmlCreateElementTable: |
|
1213 * |
|
1214 * create and initialize an empty element hash table. |
|
1215 * |
|
1216 * Returns the xmlElementTablePtr just created or NULL in case of error. |
|
1217 */ |
|
1218 static xmlElementTablePtr |
|
1219 xmlCreateElementTable(void) { |
|
1220 return(xmlHashCreate(0)); |
|
1221 } |
|
1222 |
|
1223 /** |
|
1224 * xmlFreeElement: |
|
1225 * @param elem An element |
|
1226 * |
|
1227 * Deallocate the memory used by an element definition |
|
1228 */ |
|
1229 static void |
|
1230 xmlFreeElement(xmlElementPtr elem) { |
|
1231 if (elem == NULL) return; |
|
1232 xmlUnlinkNode((xmlNodePtr) elem); |
|
1233 xmlFreeElementContent(elem->content); |
|
1234 if (elem->name != NULL) |
|
1235 xmlFree((xmlChar *) elem->name); |
|
1236 if (elem->prefix != NULL) |
|
1237 xmlFree((xmlChar *) elem->prefix); |
|
1238 #ifdef LIBXML_REGEXP_ENABLED |
|
1239 if (elem->contModel != NULL) |
|
1240 xmlRegFreeRegexp(elem->contModel); |
|
1241 #endif |
|
1242 xmlFree(elem); |
|
1243 } |
|
1244 |
|
1245 |
|
1246 /** |
|
1247 * xmlAddElementDecl: |
|
1248 * @param ctxt the validation context |
|
1249 * @param dtd pointer to the DTD |
|
1250 * @param name the entity name |
|
1251 * @param type the element type |
|
1252 * @param content the element content tree or NULL |
|
1253 * |
|
1254 * Register a new element declaration |
|
1255 * |
|
1256 * Returns NULL if not, otherwise the entity |
|
1257 */ |
|
1258 XMLPUBFUNEXPORT xmlElementPtr |
|
1259 xmlAddElementDecl(xmlValidCtxtPtr ctxt, |
|
1260 xmlDtdPtr dtd, const xmlChar *name, |
|
1261 xmlElementTypeVal type, |
|
1262 xmlElementContentPtr content) { |
|
1263 xmlElementPtr ret; |
|
1264 xmlElementTablePtr table; |
|
1265 xmlAttributePtr oldAttributes = NULL; |
|
1266 xmlChar *ns, *uqname; |
|
1267 |
|
1268 if (dtd == NULL) { |
|
1269 return(NULL); |
|
1270 } |
|
1271 if (name == NULL) { |
|
1272 return(NULL); |
|
1273 } |
|
1274 switch (type) { |
|
1275 case XML_ELEMENT_TYPE_EMPTY: |
|
1276 if (content != NULL) { |
|
1277 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
1278 EMBED_ERRTXT("xmlAddElementDecl: content != NULL for EMPTY\n"), |
|
1279 NULL); |
|
1280 return(NULL); |
|
1281 } |
|
1282 break; |
|
1283 case XML_ELEMENT_TYPE_ANY: |
|
1284 if (content != NULL) { |
|
1285 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
1286 EMBED_ERRTXT("xmlAddElementDecl: content != NULL for ANY\n"), |
|
1287 NULL); |
|
1288 return(NULL); |
|
1289 } |
|
1290 break; |
|
1291 case XML_ELEMENT_TYPE_MIXED: |
|
1292 if (content == NULL) { |
|
1293 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
1294 EMBED_ERRTXT("xmlAddElementDecl: content == NULL for MIXED\n"), |
|
1295 NULL); |
|
1296 return(NULL); |
|
1297 } |
|
1298 break; |
|
1299 case XML_ELEMENT_TYPE_ELEMENT: |
|
1300 if (content == NULL) { |
|
1301 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
1302 EMBED_ERRTXT("xmlAddElementDecl: content == NULL for ELEMENT\n"), |
|
1303 NULL); |
|
1304 return(NULL); |
|
1305 } |
|
1306 break; |
|
1307 default: |
|
1308 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
1309 EMBED_ERRTXT("Internal: ELEMENT decl corrupted invalid type\n"), |
|
1310 NULL); |
|
1311 return(NULL); |
|
1312 } |
|
1313 |
|
1314 /* |
|
1315 * check if name is a QName |
|
1316 */ |
|
1317 uqname = xmlSplitQName2(name, &ns); |
|
1318 if (uqname != NULL) |
|
1319 name = uqname; |
|
1320 |
|
1321 /* |
|
1322 * Create the Element table if needed. |
|
1323 */ |
|
1324 table = (xmlElementTablePtr) dtd->elements; |
|
1325 if (table == NULL) { |
|
1326 table = xmlCreateElementTable(); |
|
1327 dtd->elements = (void *) table; |
|
1328 } |
|
1329 if (table == NULL) { |
|
1330 xmlVErrMemory(ctxt, |
|
1331 EMBED_ERRTXT("xmlAddElementDecl: Table creation failed!\n")); |
|
1332 if (uqname != NULL) |
|
1333 xmlFree(uqname); |
|
1334 if (ns != NULL) |
|
1335 xmlFree(ns); |
|
1336 return(NULL); |
|
1337 } |
|
1338 |
|
1339 /* |
|
1340 * lookup old attributes inserted on an undefined element in the |
|
1341 * internal subset. |
|
1342 */ |
|
1343 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) { |
|
1344 ret = (xmlElementPtr)xmlHashLookup2((xmlHashTablePtr)dtd->doc->intSubset->elements, name, ns); |
|
1345 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) { |
|
1346 oldAttributes = ret->attributes; |
|
1347 ret->attributes = NULL; |
|
1348 xmlHashRemoveEntry2((xmlHashTablePtr)dtd->doc->intSubset->elements, name, ns, NULL); |
|
1349 xmlFreeElement(ret); |
|
1350 } |
|
1351 } |
|
1352 |
|
1353 /* |
|
1354 * The element may already be present if one of its attribute |
|
1355 * was registered first |
|
1356 */ |
|
1357 ret = (xmlElementPtr)xmlHashLookup2(table, name, ns); |
|
1358 if (ret != NULL) { |
|
1359 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) { |
|
1360 #ifdef LIBXML_VALID_ENABLED |
|
1361 /* |
|
1362 * The element is already defined in this DTD. |
|
1363 */ |
|
1364 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED, |
|
1365 EMBED_ERRTXT("Redefinition of element %s\n"), |
|
1366 name, NULL, NULL); |
|
1367 #endif /* LIBXML_VALID_ENABLED */ |
|
1368 if (uqname != NULL) |
|
1369 xmlFree(uqname); |
|
1370 if (ns != NULL) |
|
1371 xmlFree(ns); |
|
1372 return(NULL); |
|
1373 } |
|
1374 } else { |
|
1375 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); |
|
1376 if (ret == NULL) { |
|
1377 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
1378 if (uqname != NULL) |
|
1379 xmlFree(uqname); |
|
1380 if (ns != NULL) |
|
1381 xmlFree(ns); |
|
1382 return(NULL); |
|
1383 } |
|
1384 memset(ret, 0, sizeof(xmlElement)); |
|
1385 ret->type = XML_ELEMENT_DECL; |
|
1386 |
|
1387 /* |
|
1388 * fill the structure. |
|
1389 */ |
|
1390 ret->name = xmlStrdup(name); |
|
1391 if (ret->name == NULL) { |
|
1392 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
1393 if (uqname != NULL) |
|
1394 xmlFree(uqname); |
|
1395 if (ns != NULL) |
|
1396 xmlFree(ns); |
|
1397 xmlFree(ret); |
|
1398 return(NULL); |
|
1399 } |
|
1400 ret->prefix = ns; |
|
1401 |
|
1402 /* |
|
1403 * Validity Check: |
|
1404 * Insertion must not fail |
|
1405 */ |
|
1406 if (xmlHashAddEntry2(table, name, ns, ret)) { |
|
1407 #ifdef LIBXML_VALID_ENABLED |
|
1408 /* |
|
1409 * The element is already defined in this DTD. |
|
1410 */ |
|
1411 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED, |
|
1412 EMBED_ERRTXT("Redefinition of element %s\n"), |
|
1413 name, NULL, NULL); |
|
1414 #endif /* LIBXML_VALID_ENABLED */ |
|
1415 xmlFreeElement(ret); |
|
1416 if (uqname != NULL) |
|
1417 xmlFree(uqname); |
|
1418 return(NULL); |
|
1419 } |
|
1420 /* |
|
1421 * For new element, may have attributes from earlier |
|
1422 * definition in internal subset |
|
1423 */ |
|
1424 ret->attributes = oldAttributes; |
|
1425 } |
|
1426 |
|
1427 /* |
|
1428 * Finish to fill the structure. |
|
1429 */ |
|
1430 ret->etype = type; |
|
1431 ret->content = xmlCopyElementContent(content); |
|
1432 |
|
1433 /* |
|
1434 * Link it to the DTD |
|
1435 */ |
|
1436 ret->parent = dtd; |
|
1437 ret->doc = dtd->doc; |
|
1438 if (dtd->last == NULL) { |
|
1439 dtd->children = dtd->last = (xmlNodePtr) ret; |
|
1440 } else { |
|
1441 dtd->last->next = (xmlNodePtr) ret; |
|
1442 ret->prev = dtd->last; |
|
1443 dtd->last = (xmlNodePtr) ret; |
|
1444 } |
|
1445 if (uqname != NULL) |
|
1446 xmlFree(uqname); |
|
1447 return(ret); |
|
1448 } |
|
1449 |
|
1450 /** |
|
1451 * xmlFreeElementTable: |
|
1452 * @param table An element table |
|
1453 * |
|
1454 * Deallocate the memory used by an element hash table. |
|
1455 */ |
|
1456 XMLPUBFUNEXPORT void |
|
1457 xmlFreeElementTable(xmlElementTablePtr table) { |
|
1458 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement); |
|
1459 } |
|
1460 |
|
1461 #ifdef LIBXML_TREE_ENABLED |
|
1462 /** |
|
1463 * xmlCopyElement: |
|
1464 * @param elem An element |
|
1465 * |
|
1466 * Build a copy of an element. |
|
1467 * |
|
1468 * Returns the new xmlElementPtr or NULL in case of error. |
|
1469 */ |
|
1470 static xmlElementPtr |
|
1471 xmlCopyElement(xmlElementPtr elem) { |
|
1472 xmlElementPtr cur; |
|
1473 |
|
1474 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); |
|
1475 if (cur == NULL) { |
|
1476 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
1477 return(NULL); |
|
1478 } |
|
1479 memset(cur, 0, sizeof(xmlElement)); |
|
1480 cur->type = XML_ELEMENT_DECL; |
|
1481 cur->etype = elem->etype; |
|
1482 if (elem->name != NULL) |
|
1483 cur->name = xmlStrdup(elem->name); |
|
1484 else |
|
1485 cur->name = NULL; |
|
1486 if (elem->prefix != NULL) |
|
1487 cur->prefix = xmlStrdup(elem->prefix); |
|
1488 else |
|
1489 cur->prefix = NULL; |
|
1490 cur->content = xmlCopyElementContent(elem->content); |
|
1491 |
|
1492 cur->attributes = NULL; |
|
1493 return(cur); |
|
1494 } |
|
1495 |
|
1496 /** |
|
1497 * xmlCopyElementTable: |
|
1498 * @param table An element table |
|
1499 * |
|
1500 * Build a copy of an element table. |
|
1501 * |
|
1502 * Returns the new xmlElementTablePtr or NULL in case of error. |
|
1503 * |
|
1504 * OOM: possible --> OOM flag is set when returns NULL |
|
1505 */ |
|
1506 XMLPUBFUNEXPORT xmlElementTablePtr |
|
1507 xmlCopyElementTable(xmlElementTablePtr table) { |
|
1508 |
|
1509 return((xmlElementTablePtr) xmlHashCopy(table, |
|
1510 (xmlHashCopier) xmlCopyElement)); |
|
1511 } |
|
1512 #endif /* LIBXML_TREE_ENABLED */ |
|
1513 |
|
1514 #ifdef LIBXML_OUTPUT_ENABLED |
|
1515 /** |
|
1516 * xmlDumpElementDecl: |
|
1517 * @param buf the XML buffer output |
|
1518 * @param elem An element table |
|
1519 * |
|
1520 * This will dump the content of the element declaration as an XML |
|
1521 * DTD definition |
|
1522 */ |
|
1523 XMLPUBFUNEXPORT void |
|
1524 xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) { |
|
1525 switch (elem->etype) { |
|
1526 case XML_ELEMENT_TYPE_EMPTY: |
|
1527 xmlBufferWriteChar(buf, "<!ELEMENT "); |
|
1528 if (elem->prefix != NULL) { |
|
1529 xmlBufferWriteCHAR(buf, elem->prefix); |
|
1530 xmlBufferWriteChar(buf, ":"); |
|
1531 } |
|
1532 xmlBufferWriteCHAR(buf, elem->name); |
|
1533 xmlBufferWriteChar(buf, " EMPTY>\n"); |
|
1534 break; |
|
1535 case XML_ELEMENT_TYPE_ANY: |
|
1536 xmlBufferWriteChar(buf, "<!ELEMENT "); |
|
1537 if (elem->prefix != NULL) { |
|
1538 xmlBufferWriteCHAR(buf, elem->prefix); |
|
1539 xmlBufferWriteChar(buf, ":"); |
|
1540 } |
|
1541 xmlBufferWriteCHAR(buf, elem->name); |
|
1542 xmlBufferWriteChar(buf, " ANY>\n"); |
|
1543 break; |
|
1544 case XML_ELEMENT_TYPE_MIXED: |
|
1545 xmlBufferWriteChar(buf, "<!ELEMENT "); |
|
1546 if (elem->prefix != NULL) { |
|
1547 xmlBufferWriteCHAR(buf, elem->prefix); |
|
1548 xmlBufferWriteChar(buf, ":"); |
|
1549 } |
|
1550 xmlBufferWriteCHAR(buf, elem->name); |
|
1551 xmlBufferWriteChar(buf, " "); |
|
1552 xmlDumpElementContent(buf, elem->content, 1); |
|
1553 xmlBufferWriteChar(buf, ">\n"); |
|
1554 break; |
|
1555 case XML_ELEMENT_TYPE_ELEMENT: |
|
1556 xmlBufferWriteChar(buf, "<!ELEMENT "); |
|
1557 if (elem->prefix != NULL) { |
|
1558 xmlBufferWriteCHAR(buf, elem->prefix); |
|
1559 xmlBufferWriteChar(buf, ":"); |
|
1560 } |
|
1561 xmlBufferWriteCHAR(buf, elem->name); |
|
1562 xmlBufferWriteChar(buf, " "); |
|
1563 xmlDumpElementContent(buf, elem->content, 1); |
|
1564 xmlBufferWriteChar(buf, ">\n"); |
|
1565 break; |
|
1566 default: |
|
1567 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
1568 EMBED_ERRTXT("Internal: ELEMENT struct corrupted invalid type\n"), |
|
1569 NULL); |
|
1570 } |
|
1571 } |
|
1572 |
|
1573 /** |
|
1574 * xmlDumpElementDeclScan: |
|
1575 * @param elem An element table |
|
1576 * @param buf the XML buffer output |
|
1577 * |
|
1578 * This routine is used by the hash scan function. It just reverses |
|
1579 * the arguments. |
|
1580 */ |
|
1581 static void |
|
1582 xmlDumpElementDeclScan(xmlElementPtr elem, xmlBufferPtr buf) { |
|
1583 xmlDumpElementDecl(buf, elem); |
|
1584 } |
|
1585 |
|
1586 /** |
|
1587 * xmlDumpElementTable: |
|
1588 * @param buf the XML buffer output |
|
1589 * @param table An element table |
|
1590 * |
|
1591 * This will dump the content of the element table as an XML DTD definition |
|
1592 */ |
|
1593 XMLPUBFUNEXPORT void |
|
1594 xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) { |
|
1595 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDeclScan, buf); |
|
1596 } |
|
1597 #endif /* LIBXML_OUTPUT_ENABLED */ |
|
1598 |
|
1599 /** |
|
1600 * xmlCreateEnumeration: |
|
1601 * @param name the enumeration name or NULL |
|
1602 * |
|
1603 * create and initialize an enumeration attribute node. |
|
1604 * |
|
1605 * Returns the xmlEnumerationPtr just created or NULL in case |
|
1606 * of error. |
|
1607 */ |
|
1608 XMLPUBFUNEXPORT xmlEnumerationPtr |
|
1609 xmlCreateEnumeration(const xmlChar *name) { |
|
1610 xmlEnumerationPtr ret; |
|
1611 |
|
1612 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration)); |
|
1613 if (ret == NULL) { |
|
1614 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
1615 return(NULL); |
|
1616 } |
|
1617 memset(ret, 0, sizeof(xmlEnumeration)); |
|
1618 |
|
1619 if (name != NULL) |
|
1620 ret->name = xmlStrdup(name); |
|
1621 return(ret); |
|
1622 } |
|
1623 |
|
1624 /** |
|
1625 * xmlFreeEnumeration: |
|
1626 * @param cur the tree to free. |
|
1627 * |
|
1628 * free an enumeration attribute node (recursive). |
|
1629 */ |
|
1630 XMLPUBFUNEXPORT void |
|
1631 xmlFreeEnumeration(xmlEnumerationPtr cur) { |
|
1632 if (cur == NULL) return; |
|
1633 |
|
1634 if (cur->next != NULL) xmlFreeEnumeration(cur->next); |
|
1635 |
|
1636 if (cur->name != NULL) xmlFree((xmlChar *) cur->name); |
|
1637 xmlFree(cur); |
|
1638 } |
|
1639 |
|
1640 #ifdef LIBXML_TREE_ENABLED |
|
1641 /** |
|
1642 * xmlCopyEnumeration: |
|
1643 * @param cur the tree to copy. |
|
1644 * |
|
1645 * Copy an enumeration attribute node (recursive). |
|
1646 * |
|
1647 * Returns the xmlEnumerationPtr just created or NULL in case |
|
1648 * of error. |
|
1649 */ |
|
1650 XMLPUBFUNEXPORT xmlEnumerationPtr |
|
1651 xmlCopyEnumeration(xmlEnumerationPtr cur) { |
|
1652 xmlEnumerationPtr ret; |
|
1653 |
|
1654 if (cur == NULL) return(NULL); |
|
1655 ret = xmlCreateEnumeration((xmlChar *) cur->name); |
|
1656 |
|
1657 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next); |
|
1658 else ret->next = NULL; |
|
1659 |
|
1660 return(ret); |
|
1661 } |
|
1662 #endif /* LIBXML_TREE_ENABLED */ |
|
1663 |
|
1664 #ifdef LIBXML_OUTPUT_ENABLED |
|
1665 /** |
|
1666 * xmlDumpEnumeration: |
|
1667 * @param buf the XML buffer output |
|
1668 * @param enum An enumeration |
|
1669 * |
|
1670 * This will dump the content of the enumeration |
|
1671 */ |
|
1672 static void |
|
1673 xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) { |
|
1674 if (cur == NULL) return; |
|
1675 |
|
1676 xmlBufferWriteCHAR(buf, cur->name); |
|
1677 if (cur->next == NULL) |
|
1678 xmlBufferWriteChar(buf, ")"); |
|
1679 else { |
|
1680 xmlBufferWriteChar(buf, " | "); |
|
1681 xmlDumpEnumeration(buf, cur->next); |
|
1682 } |
|
1683 } |
|
1684 #endif /* LIBXML_OUTPUT_ENABLED */ |
|
1685 |
|
1686 /** |
|
1687 * xmlCreateAttributeTable: |
|
1688 * |
|
1689 * create and initialize an empty attribute hash table. |
|
1690 * |
|
1691 * Returns the xmlAttributeTablePtr just created or NULL in case |
|
1692 * of error. |
|
1693 */ |
|
1694 static xmlAttributeTablePtr |
|
1695 xmlCreateAttributeTable(void) { |
|
1696 return(xmlHashCreate(0)); |
|
1697 } |
|
1698 |
|
1699 #ifdef LIBXML_VALID_ENABLED |
|
1700 /** |
|
1701 * xmlScanAttributeDeclCallback: |
|
1702 * @param attr the attribute decl |
|
1703 * @param list the list to update |
|
1704 * |
|
1705 * Callback called by xmlScanAttributeDecl when a new attribute |
|
1706 * has to be entered in the list. |
|
1707 */ |
|
1708 static void |
|
1709 xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list, |
|
1710 const xmlChar* name ATTRIBUTE_UNUSED) { |
|
1711 attr->nexth = *list; |
|
1712 *list = attr; |
|
1713 } |
|
1714 |
|
1715 /** |
|
1716 * xmlScanAttributeDecl: |
|
1717 * @param dtd pointer to the DTD |
|
1718 * @param elem the element name |
|
1719 * |
|
1720 * When inserting a new element scan the DtD for existing attributes |
|
1721 * for that element and initialize the Attribute chain |
|
1722 * |
|
1723 * Returns the pointer to the first attribute decl in the chain, |
|
1724 * possibly NULL. |
|
1725 */ |
|
1726 xmlAttributePtr |
|
1727 xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) { |
|
1728 xmlAttributePtr ret = NULL; |
|
1729 xmlAttributeTablePtr table; |
|
1730 |
|
1731 if (dtd == NULL) { |
|
1732 return(NULL); |
|
1733 } |
|
1734 if (elem == NULL) { |
|
1735 return(NULL); |
|
1736 } |
|
1737 table = (xmlAttributeTablePtr) dtd->attributes; |
|
1738 if (table == NULL) |
|
1739 return(NULL); |
|
1740 |
|
1741 /* WRONG !!! */ |
|
1742 xmlHashScan3(table, NULL, NULL, elem, |
|
1743 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret); |
|
1744 return(ret); |
|
1745 } |
|
1746 |
|
1747 /** |
|
1748 * xmlScanIDAttributeDecl: |
|
1749 * @param ctxt the validation context |
|
1750 * @param elem the element name |
|
1751 * |
|
1752 * Verify that the element don't have too many ID attributes |
|
1753 * declared. |
|
1754 * |
|
1755 * Returns the number of ID attributes found. |
|
1756 */ |
|
1757 static int |
|
1758 xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { |
|
1759 xmlAttributePtr cur; |
|
1760 int ret = 0; |
|
1761 |
|
1762 if (elem == NULL) return(0); |
|
1763 cur = elem->attributes; |
|
1764 while (cur != NULL) { |
|
1765 if (cur->atype == XML_ATTRIBUTE_ID) { |
|
1766 ret ++; |
|
1767 if (ret > 1) |
|
1768 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID, |
|
1769 EMBED_ERRTXT("Element %s has too many ID attributes defined : %s\n"), |
|
1770 elem->name, cur->name, NULL); |
|
1771 } |
|
1772 cur = cur->nexth; |
|
1773 } |
|
1774 return(ret); |
|
1775 } |
|
1776 #endif /* LIBXML_VALID_ENABLED */ |
|
1777 |
|
1778 /** |
|
1779 * xmlFreeAttribute: |
|
1780 * @param elem An attribute |
|
1781 * |
|
1782 * Deallocate the memory used by an attribute definition |
|
1783 */ |
|
1784 static void |
|
1785 xmlFreeAttribute(xmlAttributePtr attr) { |
|
1786 if (attr == NULL) return; |
|
1787 xmlUnlinkNode((xmlNodePtr) attr); |
|
1788 if (attr->tree != NULL) |
|
1789 xmlFreeEnumeration(attr->tree); |
|
1790 if (attr->elem != NULL) |
|
1791 xmlFree((xmlChar *) attr->elem); |
|
1792 if (attr->name != NULL) |
|
1793 xmlFree((xmlChar *) attr->name); |
|
1794 if (attr->defaultValue != NULL) |
|
1795 xmlFree((xmlChar *) attr->defaultValue); |
|
1796 if (attr->prefix != NULL) |
|
1797 xmlFree((xmlChar *) attr->prefix); |
|
1798 xmlFree(attr); |
|
1799 } |
|
1800 |
|
1801 |
|
1802 /** |
|
1803 * xmlAddAttributeDecl: |
|
1804 * @param ctxt the validation context |
|
1805 * @param dtd pointer to the DTD |
|
1806 * @param elem the element name |
|
1807 * @param name the attribute name |
|
1808 * @param ns the attribute namespace prefix |
|
1809 * @param type the attribute type |
|
1810 * @param def the attribute default type |
|
1811 * @param defaultValue the attribute default value |
|
1812 * @param tree if it's an enumeration, the associated list |
|
1813 * |
|
1814 * Register a new attribute declaration |
|
1815 * Note that tree becomes the ownership of the DTD |
|
1816 * |
|
1817 * Returns NULL if not new, otherwise the attribute decl |
|
1818 */ |
|
1819 XMLPUBFUNEXPORT xmlAttributePtr |
|
1820 xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, |
|
1821 xmlDtdPtr dtd, const xmlChar *elem, |
|
1822 const xmlChar *name, const xmlChar *ns, |
|
1823 xmlAttributeType type, xmlAttributeDefault def, |
|
1824 const xmlChar *defaultValue, xmlEnumerationPtr tree) { |
|
1825 xmlAttributePtr ret; |
|
1826 xmlAttributeTablePtr table; |
|
1827 xmlElementPtr elemDef; |
|
1828 LOAD_GS_SAFE_DTD(dtd) |
|
1829 |
|
1830 if (dtd == NULL) { |
|
1831 xmlFreeEnumeration(tree); |
|
1832 return(NULL); |
|
1833 } |
|
1834 if (name == NULL) { |
|
1835 xmlFreeEnumeration(tree); |
|
1836 return(NULL); |
|
1837 } |
|
1838 if (elem == NULL) { |
|
1839 xmlFreeEnumeration(tree); |
|
1840 return(NULL); |
|
1841 } |
|
1842 |
|
1843 #ifdef LIBXML_VALID_ENABLED |
|
1844 /* |
|
1845 * Check the type and possibly the default value. |
|
1846 */ |
|
1847 switch (type) { |
|
1848 case XML_ATTRIBUTE_CDATA: |
|
1849 break; |
|
1850 case XML_ATTRIBUTE_ID: |
|
1851 break; |
|
1852 case XML_ATTRIBUTE_IDREF: |
|
1853 break; |
|
1854 case XML_ATTRIBUTE_IDREFS: |
|
1855 break; |
|
1856 case XML_ATTRIBUTE_ENTITY: |
|
1857 break; |
|
1858 case XML_ATTRIBUTE_ENTITIES: |
|
1859 break; |
|
1860 case XML_ATTRIBUTE_NMTOKEN: |
|
1861 break; |
|
1862 case XML_ATTRIBUTE_NMTOKENS: |
|
1863 break; |
|
1864 case XML_ATTRIBUTE_ENUMERATION: |
|
1865 break; |
|
1866 case XML_ATTRIBUTE_NOTATION: |
|
1867 break; |
|
1868 default: |
|
1869 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
1870 EMBED_ERRTXT("Internal: ATTRIBUTE struct corrupted invalid type\n"), |
|
1871 NULL); |
|
1872 xmlFreeEnumeration(tree); |
|
1873 return(NULL); |
|
1874 } |
|
1875 if ((defaultValue != NULL) && |
|
1876 (!xmlValidateAttributeValue(type, defaultValue))) { |
|
1877 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT, |
|
1878 EMBED_ERRTXT("Attribute %s of %s: invalid default value\n"), |
|
1879 elem, name, defaultValue); |
|
1880 defaultValue = NULL; |
|
1881 ctxt->valid = 0; |
|
1882 } |
|
1883 #endif /* LIBXML_VALID_ENABLED */ |
|
1884 |
|
1885 /* |
|
1886 * Check first that an attribute defined in the external subset wasn't |
|
1887 * already defined in the internal subset |
|
1888 */ |
|
1889 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) && |
|
1890 (dtd->doc->intSubset != NULL) && |
|
1891 (dtd->doc->intSubset->attributes != NULL)) { |
|
1892 ret = (xmlAttributePtr)xmlHashLookup3((xmlHashTablePtr)dtd->doc->intSubset->attributes, name, ns, elem); |
|
1893 if (ret != NULL) |
|
1894 return(NULL); |
|
1895 } |
|
1896 |
|
1897 /* |
|
1898 * Create the Attribute table if needed. |
|
1899 */ |
|
1900 table = (xmlAttributeTablePtr) dtd->attributes; |
|
1901 if (table == NULL) { |
|
1902 table = xmlCreateAttributeTable(); |
|
1903 dtd->attributes = (void *) table; |
|
1904 } |
|
1905 if (table == NULL) { |
|
1906 xmlVErrMemory(ctxt, |
|
1907 EMBED_ERRTXT("xmlAddAttributeDecl: Table creation failed!\n")); |
|
1908 return(NULL); |
|
1909 } |
|
1910 |
|
1911 |
|
1912 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute)); |
|
1913 if (ret == NULL) { |
|
1914 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
1915 return(NULL); |
|
1916 } |
|
1917 memset(ret, 0, sizeof(xmlAttribute)); |
|
1918 ret->type = XML_ATTRIBUTE_DECL; |
|
1919 |
|
1920 /* |
|
1921 * fill the structure. |
|
1922 */ |
|
1923 ret->atype = type; |
|
1924 ret->name = xmlStrdup(name); |
|
1925 ret->prefix = xmlStrdup(ns); |
|
1926 ret->elem = xmlStrdup(elem); |
|
1927 ret->def = def; |
|
1928 ret->tree = tree; |
|
1929 if (defaultValue) |
|
1930 ret->defaultValue = xmlStrdup(defaultValue); |
|
1931 |
|
1932 if(OOM_FLAG) { |
|
1933 xmlFreeAttribute(ret); |
|
1934 return(NULL); |
|
1935 } |
|
1936 |
|
1937 /* |
|
1938 * Validity Check: |
|
1939 * Search the DTD for previous declarations of the ATTLIST |
|
1940 */ |
|
1941 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) { |
|
1942 #ifdef LIBXML_VALID_ENABLED |
|
1943 /* |
|
1944 * The attribute is already defined in this DTD. |
|
1945 */ |
|
1946 xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED, |
|
1947 EMBED_ERRTXT("Attribute %s of element %s: already defined\n"), |
|
1948 name, elem, NULL); |
|
1949 #endif /* LIBXML_VALID_ENABLED */ |
|
1950 xmlFreeAttribute(ret); |
|
1951 return(NULL); |
|
1952 } |
|
1953 |
|
1954 /* |
|
1955 * Validity Check: |
|
1956 * Multiple ID per element |
|
1957 */ |
|
1958 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1); |
|
1959 if (elemDef != NULL) { |
|
1960 |
|
1961 #ifdef LIBXML_VALID_ENABLED |
|
1962 if ((type == XML_ATTRIBUTE_ID) && |
|
1963 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) { |
|
1964 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID, |
|
1965 EMBED_ERRTXT("Element %s has too may ID attributes defined : %s\n"), |
|
1966 elem, name, NULL); |
|
1967 ctxt->valid = 0; |
|
1968 } |
|
1969 #endif /* LIBXML_VALID_ENABLED */ |
|
1970 |
|
1971 /* |
|
1972 * Insert namespace default def first they need to be |
|
1973 * processed first. |
|
1974 */ |
|
1975 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) || |
|
1976 ((ret->prefix != NULL && |
|
1977 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) { |
|
1978 ret->nexth = elemDef->attributes; |
|
1979 elemDef->attributes = ret; |
|
1980 } else { |
|
1981 xmlAttributePtr tmp = elemDef->attributes; |
|
1982 |
|
1983 while ((tmp != NULL) && |
|
1984 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) || |
|
1985 ((ret->prefix != NULL && |
|
1986 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) { |
|
1987 if (tmp->nexth == NULL) |
|
1988 break; |
|
1989 tmp = tmp->nexth; |
|
1990 } |
|
1991 if (tmp != NULL) { |
|
1992 ret->nexth = tmp->nexth; |
|
1993 tmp->nexth = ret; |
|
1994 } else { |
|
1995 ret->nexth = elemDef->attributes; |
|
1996 elemDef->attributes = ret; |
|
1997 } |
|
1998 } |
|
1999 } |
|
2000 |
|
2001 /* |
|
2002 * Link it to the DTD |
|
2003 */ |
|
2004 ret->parent = dtd; |
|
2005 ret->doc = dtd->doc; |
|
2006 if (dtd->last == NULL) { |
|
2007 dtd->children = dtd->last = (xmlNodePtr) ret; |
|
2008 } else { |
|
2009 dtd->last->next = (xmlNodePtr) ret; |
|
2010 ret->prev = dtd->last; |
|
2011 dtd->last = (xmlNodePtr) ret; |
|
2012 } |
|
2013 return(ret); |
|
2014 } |
|
2015 |
|
2016 /** |
|
2017 * xmlFreeAttributeTable: |
|
2018 * @param table An attribute table |
|
2019 * |
|
2020 * Deallocate the memory used by an entities hash table. |
|
2021 */ |
|
2022 XMLPUBFUNEXPORT void |
|
2023 xmlFreeAttributeTable(xmlAttributeTablePtr table) { |
|
2024 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute); |
|
2025 } |
|
2026 |
|
2027 #ifdef LIBXML_TREE_ENABLED |
|
2028 /** |
|
2029 * xmlCopyAttribute: |
|
2030 * @param attr An attribute |
|
2031 * |
|
2032 * Build a copy of an attribute. |
|
2033 * |
|
2034 * Returns the new xmlAttributePtr or NULL in case of error. |
|
2035 */ |
|
2036 static xmlAttributePtr |
|
2037 xmlCopyAttribute(xmlAttributePtr attr) { |
|
2038 xmlAttributePtr cur; |
|
2039 |
|
2040 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute)); |
|
2041 if (cur == NULL) { |
|
2042 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
2043 return(NULL); |
|
2044 } |
|
2045 memset(cur, 0, sizeof(xmlAttribute)); |
|
2046 cur->type = XML_ATTRIBUTE_DECL; |
|
2047 cur->atype = attr->atype; |
|
2048 cur->def = attr->def; |
|
2049 cur->tree = xmlCopyEnumeration(attr->tree); |
|
2050 if (attr->elem != NULL) |
|
2051 cur->elem = xmlStrdup(attr->elem); |
|
2052 if (attr->name != NULL) |
|
2053 cur->name = xmlStrdup(attr->name); |
|
2054 if (attr->prefix != NULL) |
|
2055 cur->prefix = xmlStrdup(attr->prefix); |
|
2056 if (attr->defaultValue != NULL) |
|
2057 cur->defaultValue = xmlStrdup(attr->defaultValue); |
|
2058 return(cur); |
|
2059 } |
|
2060 |
|
2061 /** |
|
2062 * xmlCopyAttributeTable: |
|
2063 * @param table An attribute table |
|
2064 * |
|
2065 * Build a copy of an attribute table. |
|
2066 * |
|
2067 * Returns the new xmlAttributeTablePtr or NULL in case of error. |
|
2068 * |
|
2069 * OOM: possible --> returns NULL with OOM flag set |
|
2070 */ |
|
2071 XMLPUBFUNEXPORT xmlAttributeTablePtr |
|
2072 xmlCopyAttributeTable(xmlAttributeTablePtr table) { |
|
2073 |
|
2074 return((xmlAttributeTablePtr) xmlHashCopy(table, |
|
2075 (xmlHashCopier) xmlCopyAttribute)); |
|
2076 } |
|
2077 #endif /* LIBXML_TREE_ENABLED */ |
|
2078 |
|
2079 #ifdef LIBXML_OUTPUT_ENABLED |
|
2080 /** |
|
2081 * xmlDumpAttributeDecl: |
|
2082 * @param buf the XML buffer output |
|
2083 * @param attr An attribute declaration |
|
2084 * |
|
2085 * This will dump the content of the attribute declaration as an XML |
|
2086 * DTD definition |
|
2087 */ |
|
2088 XMLPUBFUNEXPORT void |
|
2089 xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) { |
|
2090 xmlBufferWriteChar(buf, "<!ATTLIST "); |
|
2091 xmlBufferWriteCHAR(buf, attr->elem); |
|
2092 xmlBufferWriteChar(buf, " "); |
|
2093 if (attr->prefix != NULL) { |
|
2094 xmlBufferWriteCHAR(buf, attr->prefix); |
|
2095 xmlBufferWriteChar(buf, ":"); |
|
2096 } |
|
2097 xmlBufferWriteCHAR(buf, attr->name); |
|
2098 switch (attr->atype) { |
|
2099 case XML_ATTRIBUTE_CDATA: |
|
2100 xmlBufferWriteChar(buf, " CDATA"); |
|
2101 break; |
|
2102 case XML_ATTRIBUTE_ID: |
|
2103 xmlBufferWriteChar(buf, " ID"); |
|
2104 break; |
|
2105 case XML_ATTRIBUTE_IDREF: |
|
2106 xmlBufferWriteChar(buf, " IDREF"); |
|
2107 break; |
|
2108 case XML_ATTRIBUTE_IDREFS: |
|
2109 xmlBufferWriteChar(buf, " IDREFS"); |
|
2110 break; |
|
2111 case XML_ATTRIBUTE_ENTITY: |
|
2112 xmlBufferWriteChar(buf, " ENTITY"); |
|
2113 break; |
|
2114 case XML_ATTRIBUTE_ENTITIES: |
|
2115 xmlBufferWriteChar(buf, " ENTITIES"); |
|
2116 break; |
|
2117 case XML_ATTRIBUTE_NMTOKEN: |
|
2118 xmlBufferWriteChar(buf, " NMTOKEN"); |
|
2119 break; |
|
2120 case XML_ATTRIBUTE_NMTOKENS: |
|
2121 xmlBufferWriteChar(buf, " NMTOKENS"); |
|
2122 break; |
|
2123 case XML_ATTRIBUTE_ENUMERATION: |
|
2124 xmlBufferWriteChar(buf, " ("); |
|
2125 xmlDumpEnumeration(buf, attr->tree); |
|
2126 break; |
|
2127 case XML_ATTRIBUTE_NOTATION: |
|
2128 xmlBufferWriteChar(buf, " NOTATION ("); |
|
2129 xmlDumpEnumeration(buf, attr->tree); |
|
2130 break; |
|
2131 default: |
|
2132 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
2133 EMBED_ERRTXT("Internal: ATTRIBUTE struct corrupted invalid type\n"), |
|
2134 NULL); |
|
2135 } |
|
2136 switch (attr->def) { |
|
2137 case XML_ATTRIBUTE_NONE: |
|
2138 break; |
|
2139 case XML_ATTRIBUTE_REQUIRED: |
|
2140 xmlBufferWriteChar(buf, " #REQUIRED"); |
|
2141 break; |
|
2142 case XML_ATTRIBUTE_IMPLIED: |
|
2143 xmlBufferWriteChar(buf, " #IMPLIED"); |
|
2144 break; |
|
2145 case XML_ATTRIBUTE_FIXED: |
|
2146 xmlBufferWriteChar(buf, " #FIXED"); |
|
2147 break; |
|
2148 default: |
|
2149 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
2150 EMBED_ERRTXT("Internal: ATTRIBUTE struct corrupted invalid def\n"), |
|
2151 NULL); |
|
2152 } |
|
2153 if (attr->defaultValue != NULL) { |
|
2154 xmlBufferWriteChar(buf, " "); |
|
2155 xmlBufferWriteQuotedString(buf, attr->defaultValue); |
|
2156 } |
|
2157 xmlBufferWriteChar(buf, ">\n"); |
|
2158 } |
|
2159 |
|
2160 /** |
|
2161 * xmlDumpAttributeDeclScan: |
|
2162 * @param attr An attribute declaration |
|
2163 * @param buf the XML buffer output |
|
2164 * |
|
2165 * This is used with the hash scan function - just reverses arguments |
|
2166 */ |
|
2167 static void |
|
2168 xmlDumpAttributeDeclScan(xmlAttributePtr attr, xmlBufferPtr buf) { |
|
2169 xmlDumpAttributeDecl(buf, attr); |
|
2170 } |
|
2171 |
|
2172 /** |
|
2173 * xmlDumpAttributeTable: |
|
2174 * @param buf the XML buffer output |
|
2175 * @param table An attribute table |
|
2176 * |
|
2177 * This will dump the content of the attribute table as an XML DTD definition |
|
2178 */ |
|
2179 XMLPUBFUNEXPORT void |
|
2180 xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) { |
|
2181 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDeclScan, buf); |
|
2182 } |
|
2183 #endif /* LIBXML_OUTPUT_ENABLED */ |
|
2184 |
|
2185 /************************************************************************ |
|
2186 * * |
|
2187 * NOTATIONs * |
|
2188 * * |
|
2189 ************************************************************************/ |
|
2190 /** |
|
2191 * xmlCreateNotationTable: |
|
2192 * |
|
2193 * create and initialize an empty notation hash table. |
|
2194 * |
|
2195 * Returns the xmlNotationTablePtr just created or NULL in case |
|
2196 * of error. |
|
2197 */ |
|
2198 static xmlNotationTablePtr |
|
2199 xmlCreateNotationTable(void) { |
|
2200 return(xmlHashCreate(0)); |
|
2201 } |
|
2202 |
|
2203 /** |
|
2204 * xmlFreeNotation: |
|
2205 * @param not A notation |
|
2206 * |
|
2207 * Deallocate the memory used by an notation definition |
|
2208 */ |
|
2209 static void |
|
2210 xmlFreeNotation(xmlNotationPtr nota) { |
|
2211 if (nota == NULL) return; |
|
2212 if (nota->name != NULL) |
|
2213 xmlFree((xmlChar *) nota->name); |
|
2214 if (nota->PublicID != NULL) |
|
2215 xmlFree((xmlChar *) nota->PublicID); |
|
2216 if (nota->SystemID != NULL) |
|
2217 xmlFree((xmlChar *) nota->SystemID); |
|
2218 xmlFree(nota); |
|
2219 } |
|
2220 |
|
2221 |
|
2222 /** |
|
2223 * xmlAddNotationDecl: |
|
2224 * @param dtd pointer to the DTD |
|
2225 * @param ctxt the validation context |
|
2226 * @param name the entity name |
|
2227 * @param PublicID the public identifier or NULL |
|
2228 * @param SystemID the system identifier or NULL |
|
2229 * |
|
2230 * Register a new notation declaration |
|
2231 * |
|
2232 * Returns NULL if not, otherwise the entity |
|
2233 */ |
|
2234 XMLPUBFUNEXPORT xmlNotationPtr |
|
2235 xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, |
|
2236 const xmlChar *name, |
|
2237 const xmlChar *PublicID, const xmlChar *SystemID) { |
|
2238 xmlNotationPtr ret; |
|
2239 xmlNotationTablePtr table; |
|
2240 |
|
2241 if (dtd == NULL) { |
|
2242 return(NULL); |
|
2243 } |
|
2244 if (name == NULL) { |
|
2245 return(NULL); |
|
2246 } |
|
2247 if ((PublicID == NULL) && (SystemID == NULL)) { |
|
2248 return(NULL); |
|
2249 } |
|
2250 |
|
2251 /* |
|
2252 * Create the Notation table if needed. |
|
2253 */ |
|
2254 table = (xmlNotationTablePtr) dtd->notations; |
|
2255 if (table == NULL) |
|
2256 dtd->notations = table = xmlCreateNotationTable(); |
|
2257 if (table == NULL) { |
|
2258 xmlVErrMemory(ctxt, |
|
2259 EMBED_ERRTXT("xmlAddNotationDecl: Table creation failed!\n")); |
|
2260 return(NULL); |
|
2261 } |
|
2262 |
|
2263 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation)); |
|
2264 if (ret == NULL) { |
|
2265 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
2266 return(NULL); |
|
2267 } |
|
2268 memset(ret, 0, sizeof(xmlNotation)); |
|
2269 |
|
2270 /* |
|
2271 * fill the structure. |
|
2272 */ |
|
2273 ret->name = xmlStrdup(name); |
|
2274 if (SystemID != NULL) |
|
2275 ret->SystemID = xmlStrdup(SystemID); |
|
2276 if (PublicID != NULL) |
|
2277 ret->PublicID = xmlStrdup(PublicID); |
|
2278 |
|
2279 /* |
|
2280 * Validity Check: |
|
2281 * Check the DTD for previous declarations of the ATTLIST |
|
2282 */ |
|
2283 if (xmlHashAddEntry(table, name, ret)) { |
|
2284 #ifdef LIBXML_VALID_ENABLED |
|
2285 xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED, |
|
2286 EMBED_ERRTXT("xmlAddNotationDecl: %s already defined\n"), |
|
2287 (const char *) name); |
|
2288 #endif /* LIBXML_VALID_ENABLED */ |
|
2289 xmlFreeNotation(ret); |
|
2290 return(NULL); |
|
2291 } |
|
2292 return(ret); |
|
2293 } |
|
2294 |
|
2295 /** |
|
2296 * xmlFreeNotationTable: |
|
2297 * @param table An notation table |
|
2298 * |
|
2299 * Deallocate the memory used by an entities hash table. |
|
2300 */ |
|
2301 XMLPUBFUNEXPORT void |
|
2302 xmlFreeNotationTable(xmlNotationTablePtr table) { |
|
2303 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation); |
|
2304 } |
|
2305 |
|
2306 #ifdef LIBXML_TREE_ENABLED |
|
2307 /** |
|
2308 * xmlCopyNotation: |
|
2309 * @param nota A notation |
|
2310 * |
|
2311 * Build a copy of a notation. |
|
2312 * |
|
2313 * Returns the new xmlNotationPtr or NULL in case of error. |
|
2314 */ |
|
2315 static xmlNotationPtr |
|
2316 xmlCopyNotation(xmlNotationPtr nota) { |
|
2317 xmlNotationPtr cur; |
|
2318 |
|
2319 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation)); |
|
2320 if (cur == NULL) { |
|
2321 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
2322 return(NULL); |
|
2323 } |
|
2324 if (nota->name != NULL) |
|
2325 cur->name = xmlStrdup(nota->name); |
|
2326 else |
|
2327 cur->name = NULL; |
|
2328 if (nota->PublicID != NULL) |
|
2329 cur->PublicID = xmlStrdup(nota->PublicID); |
|
2330 else |
|
2331 cur->PublicID = NULL; |
|
2332 if (nota->SystemID != NULL) |
|
2333 cur->SystemID = xmlStrdup(nota->SystemID); |
|
2334 else |
|
2335 cur->SystemID = NULL; |
|
2336 return(cur); |
|
2337 } |
|
2338 |
|
2339 /** |
|
2340 * xmlCopyNotationTable: |
|
2341 * @param table A notation table |
|
2342 * |
|
2343 * Build a copy of a notation table. |
|
2344 * |
|
2345 * Returns the new xmlNotationTablePtr or NULL in case of error. |
|
2346 * |
|
2347 * OOM: possible --> returns NULL and OOM flag is set |
|
2348 */ |
|
2349 XMLPUBFUNEXPORT xmlNotationTablePtr |
|
2350 xmlCopyNotationTable(xmlNotationTablePtr table) { |
|
2351 |
|
2352 return((xmlNotationTablePtr) xmlHashCopy(table, |
|
2353 (xmlHashCopier) xmlCopyNotation)); |
|
2354 } |
|
2355 #endif /* LIBXML_TREE_ENABLED */ |
|
2356 |
|
2357 #ifdef LIBXML_OUTPUT_ENABLED |
|
2358 /** |
|
2359 * xmlDumpNotationDecl: |
|
2360 * @param buf the XML buffer output |
|
2361 * @param nota A notation declaration |
|
2362 * |
|
2363 * This will dump the content the notation declaration as an XML DTD definition |
|
2364 */ |
|
2365 XMLPUBFUNEXPORT void |
|
2366 xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) { |
|
2367 xmlBufferWriteChar(buf, "<!NOTATION "); |
|
2368 xmlBufferWriteCHAR(buf, nota->name); |
|
2369 if (nota->PublicID != NULL) { |
|
2370 xmlBufferWriteChar(buf, " PUBLIC "); |
|
2371 xmlBufferWriteQuotedString(buf, nota->PublicID); |
|
2372 if (nota->SystemID != NULL) { |
|
2373 xmlBufferWriteChar(buf, " "); |
|
2374 xmlBufferWriteCHAR(buf, nota->SystemID); |
|
2375 } |
|
2376 } else { |
|
2377 xmlBufferWriteChar(buf, " SYSTEM "); |
|
2378 xmlBufferWriteCHAR(buf, nota->SystemID); |
|
2379 } |
|
2380 xmlBufferWriteChar(buf, " >\n"); |
|
2381 } |
|
2382 |
|
2383 /** |
|
2384 * xmlDumpNotationDeclScan: |
|
2385 * @param nota A notation declaration |
|
2386 * @param buf the XML buffer output |
|
2387 * |
|
2388 * This is called with the hash scan function, and just reverses args |
|
2389 */ |
|
2390 static void |
|
2391 xmlDumpNotationDeclScan(xmlNotationPtr nota, xmlBufferPtr buf) { |
|
2392 xmlDumpNotationDecl(buf, nota); |
|
2393 } |
|
2394 |
|
2395 /** |
|
2396 * xmlDumpNotationTable: |
|
2397 * @param buf the XML buffer output |
|
2398 * @param table A notation table |
|
2399 * |
|
2400 * This will dump the content of the notation table as an XML DTD definition |
|
2401 */ |
|
2402 XMLPUBFUNEXPORT void |
|
2403 xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) { |
|
2404 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDeclScan, buf); |
|
2405 } |
|
2406 #endif /* LIBXML_OUTPUT_ENABLED */ |
|
2407 |
|
2408 /************************************************************************ |
|
2409 * * |
|
2410 * IDs * |
|
2411 * * |
|
2412 ************************************************************************/ |
|
2413 /** |
|
2414 * DICT_FREE: |
|
2415 * @param str a string |
|
2416 * |
|
2417 * Free a string if it is not owned by the "dict" dictionnary in the |
|
2418 * current scope |
|
2419 */ |
|
2420 #define DICT_FREE(str) \ |
|
2421 if ((str) && ((!dict) || \ |
|
2422 (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \ |
|
2423 xmlFree((char *)(str)); |
|
2424 |
|
2425 /** |
|
2426 * xmlCreateIDTable: |
|
2427 * |
|
2428 * create and initialize an empty id hash table. |
|
2429 * |
|
2430 * Returns the xmlIDTablePtr just created or NULL in case |
|
2431 * of error. |
|
2432 */ |
|
2433 static xmlIDTablePtr |
|
2434 xmlCreateIDTable(void) { |
|
2435 return(xmlHashCreate(0)); |
|
2436 } |
|
2437 |
|
2438 /** |
|
2439 * xmlFreeID: |
|
2440 * @param not A id |
|
2441 * |
|
2442 * Deallocate the memory used by an id definition |
|
2443 */ |
|
2444 static void |
|
2445 xmlFreeID(xmlIDPtr id) { |
|
2446 xmlDictPtr dict = NULL; |
|
2447 |
|
2448 if (id == NULL) return; |
|
2449 |
|
2450 if (id->doc) |
|
2451 dict = id->doc->dict; |
|
2452 |
|
2453 if (id->value) |
|
2454 DICT_FREE(id->value) |
|
2455 if (id->name) |
|
2456 DICT_FREE(id->name) |
|
2457 xmlFree(id); |
|
2458 } |
|
2459 |
|
2460 |
|
2461 /** |
|
2462 * xmlAddID: |
|
2463 * @param ctxt the validation context |
|
2464 * @param doc pointer to the document |
|
2465 * @param value the value name |
|
2466 * @param attr the attribute holding the ID |
|
2467 * |
|
2468 * Register a new id declaration |
|
2469 * |
|
2470 * Returns NULL if not, otherwise the new xmlIDPtr |
|
2471 * |
|
2472 * OOM: possible --> check OOM flag if returns NULL |
|
2473 */ |
|
2474 XMLPUBFUNEXPORT xmlIDPtr |
|
2475 xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar* value, xmlAttrPtr attr) |
|
2476 { |
|
2477 xmlIDPtr ret; |
|
2478 xmlIDTablePtr table; |
|
2479 |
|
2480 if (!doc || !value || !attr) |
|
2481 return(NULL); |
|
2482 |
|
2483 /* |
|
2484 * Create the ID table if needed. |
|
2485 */ |
|
2486 table = (xmlIDTablePtr) doc->ids; |
|
2487 if (table == NULL) |
|
2488 doc->ids = table = xmlCreateIDTable(); |
|
2489 if (!table) { |
|
2490 xmlVErrMemory(ctxt, |
|
2491 EMBED_ERRTXT("xmlAddID: Table creation failed!\n")); |
|
2492 return(NULL); |
|
2493 } |
|
2494 |
|
2495 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID)); |
|
2496 if (!ret) { |
|
2497 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
2498 return(NULL); |
|
2499 } |
|
2500 memset(ret, 0, sizeof(xmlID)); |
|
2501 |
|
2502 /* |
|
2503 * fill the structure. |
|
2504 */ |
|
2505 ret->value = xmlStrdup(value); |
|
2506 if(!ret->value) |
|
2507 goto OOM; // note: value is not NULL for sure! |
|
2508 |
|
2509 ret->doc = doc; |
|
2510 if (ctxt && (ctxt->vstateNr != 0)) |
|
2511 { |
|
2512 /* |
|
2513 * Operating in streaming mode, attr is gonna disapear |
|
2514 */ |
|
2515 if (doc->dict) |
|
2516 ret->name = xmlDictLookup(doc->dict, attr->name, -1); |
|
2517 else |
|
2518 { |
|
2519 ret->name = xmlStrdup(attr->name); |
|
2520 if(!ret->name) |
|
2521 goto OOM; // attr->name may not be NULL |
|
2522 } |
|
2523 ret->attr = NULL; |
|
2524 } else { |
|
2525 ret->attr = attr; |
|
2526 ret->name = NULL; |
|
2527 } |
|
2528 |
|
2529 #ifdef LIBXML_ENABLE_NODE_LINEINFO |
|
2530 ret->lineno = xmlGetLineNo(attr->parent); |
|
2531 #endif |
|
2532 |
|
2533 if (xmlHashAddEntry(table, value, ret) < 0) { |
|
2534 #ifdef LIBXML_VALID_ENABLED |
|
2535 /* |
|
2536 * The id is already defined in this DTD. |
|
2537 */ |
|
2538 if ((ctxt != NULL) && (ctxt->error != NULL)) { |
|
2539 xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED, |
|
2540 EMBED_ERRTXT("ID %s already defined\n"), |
|
2541 value, NULL, NULL); |
|
2542 } |
|
2543 #endif /* LIBXML_VALID_ENABLED */ |
|
2544 OOM: |
|
2545 xmlFreeID(ret); |
|
2546 return(NULL); |
|
2547 } |
|
2548 // DONE: removed redundant check: // if (attr) |
|
2549 attr->atype = XML_ATTRIBUTE_ID; |
|
2550 return(ret); |
|
2551 } |
|
2552 |
|
2553 /** |
|
2554 * xmlFreeIDTable: |
|
2555 * @param table An id table |
|
2556 * |
|
2557 * Deallocate the memory used by an ID hash table. |
|
2558 */ |
|
2559 XMLPUBFUNEXPORT void |
|
2560 xmlFreeIDTable(xmlIDTablePtr table) { |
|
2561 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID); |
|
2562 } |
|
2563 |
|
2564 /** |
|
2565 * xmlIsID: |
|
2566 * @param doc the document |
|
2567 * @param elem the element carrying the attribute |
|
2568 * @param attr the attribute |
|
2569 * |
|
2570 * Determine whether an attribute is of type ID. In case we have DTD(s) |
|
2571 * then this is done if DTD loading has been requested. In the case |
|
2572 * of HTML documents parsed with the HTML parser, then ID detection is |
|
2573 * done systematically. |
|
2574 * |
|
2575 * Returns 0 or 1 depending on the lookup result |
|
2576 * |
|
2577 * OOM: possible --> set OOM flag when returns 0 |
|
2578 */ |
|
2579 XMLPUBFUNEXPORT int |
|
2580 xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) |
|
2581 { |
|
2582 LOAD_GS_SAFE_DOC(doc) |
|
2583 // DONE: Optimized: if elem is NULL it always returs 0 (in all branches) |
|
2584 if (!doc || !attr || !elem || |
|
2585 (!doc->intSubset && !doc->extSubset)) |
|
2586 { |
|
2587 return(0); |
|
2588 } |
|
2589 if (doc->type == XML_HTML_DOCUMENT_NODE) |
|
2590 { |
|
2591 if ((xmlStrEqual(BAD_CAST "id", attr->name) || |
|
2592 xmlStrEqual(BAD_CAST "name", attr->name)) |
|
2593 && |
|
2594 !xmlStrEqual(elem->name, BAD_CAST "input")) |
|
2595 { |
|
2596 return(1); |
|
2597 } |
|
2598 return(0); |
|
2599 } |
|
2600 else |
|
2601 { // we have XML document |
|
2602 xmlAttributePtr attrDecl; |
|
2603 |
|
2604 //DONE: 'elem' is check before; REMOVED: if (!elem) return(0); |
|
2605 if (elem->ns && elem->ns->prefix) |
|
2606 { |
|
2607 xmlChar fn[50]; |
|
2608 xmlChar* fullname; |
|
2609 |
|
2610 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); // may set OOM flag |
|
2611 if (!fullname) |
|
2612 return(0); |
|
2613 |
|
2614 attrDecl = xmlGetDtdAttrDesc( |
|
2615 doc->intSubset, |
|
2616 fullname, |
|
2617 attr->name); |
|
2618 //- |
|
2619 if(OOM_FLAG) |
|
2620 goto end; |
|
2621 //- |
|
2622 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
2623 { |
|
2624 attrDecl = xmlGetDtdAttrDesc( |
|
2625 doc->extSubset, |
|
2626 fullname, |
|
2627 attr->name); |
|
2628 //- |
|
2629 if(OOM_FLAG) |
|
2630 goto end; |
|
2631 //- |
|
2632 } |
|
2633 if ((fullname != fn) && (fullname != elem->name)) |
|
2634 xmlFree(fullname); |
|
2635 } else { |
|
2636 attrDecl = xmlGetDtdAttrDesc( |
|
2637 doc->intSubset, |
|
2638 elem->name, |
|
2639 attr->name); |
|
2640 //- |
|
2641 if(OOM_FLAG) |
|
2642 goto end; |
|
2643 //- |
|
2644 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
2645 { |
|
2646 attrDecl = xmlGetDtdAttrDesc( |
|
2647 doc->extSubset, |
|
2648 elem->name, |
|
2649 attr->name); |
|
2650 //- |
|
2651 if(OOM_FLAG) |
|
2652 goto end; |
|
2653 //- |
|
2654 } |
|
2655 } |
|
2656 |
|
2657 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID)) |
|
2658 return(1); |
|
2659 } |
|
2660 end: |
|
2661 return(0); |
|
2662 } |
|
2663 |
|
2664 /** |
|
2665 * xmlRemoveID: |
|
2666 * @param doc the document |
|
2667 * @param attr the attribute |
|
2668 * |
|
2669 * Remove the given attribute from the ID table maintained internally. |
|
2670 * |
|
2671 * Returns -1 if the lookup failed and 0 otherwise |
|
2672 */ |
|
2673 XMLPUBFUNEXPORT int |
|
2674 xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) |
|
2675 { |
|
2676 xmlIDTablePtr table; |
|
2677 xmlIDPtr id; |
|
2678 xmlChar* ID; |
|
2679 |
|
2680 if (!doc || !attr || !doc->ids) |
|
2681 return(-1); |
|
2682 table = (xmlIDTablePtr) doc->ids; |
|
2683 |
|
2684 ID = xmlNodeListGetString(doc, attr->children, 1); |
|
2685 if (!ID) |
|
2686 return(-1); |
|
2687 |
|
2688 id = (xmlIDPtr)xmlHashLookup(table, ID); |
|
2689 if (!id || id->attr != attr) { |
|
2690 xmlFree(ID); |
|
2691 return(-1); |
|
2692 } |
|
2693 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID); |
|
2694 xmlFree(ID); |
|
2695 return(0); |
|
2696 } |
|
2697 |
|
2698 /** |
|
2699 * xmlGetID: |
|
2700 * @param doc pointer to the document |
|
2701 * @param ID the ID value |
|
2702 * |
|
2703 * Search the attribute declaring the given ID |
|
2704 * |
|
2705 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID |
|
2706 */ |
|
2707 XMLPUBFUNEXPORT xmlAttrPtr |
|
2708 xmlGetID(xmlDocPtr doc, const xmlChar* ID) |
|
2709 { |
|
2710 xmlIDTablePtr table; |
|
2711 xmlIDPtr id; |
|
2712 |
|
2713 if (!doc || !doc->ids || !ID) |
|
2714 return(NULL); |
|
2715 |
|
2716 table = (xmlIDTablePtr) doc->ids; |
|
2717 |
|
2718 id = (xmlIDPtr)xmlHashLookup(table, ID); |
|
2719 if (id == NULL) |
|
2720 return(NULL); |
|
2721 if (!id->attr) { |
|
2722 /* |
|
2723 * We are operating on a stream, return a well known reference |
|
2724 * since the attribute node doesn't exist anymore |
|
2725 */ |
|
2726 return((xmlAttrPtr) doc); |
|
2727 } |
|
2728 return(id->attr); |
|
2729 } |
|
2730 |
|
2731 // forward declaration |
|
2732 int xmlAddIDsInternal(xmlDocPtr doc, xmlNodePtr cur, const xmlChar** ids); |
|
2733 |
|
2734 /** |
|
2735 * xmlAddIDs: |
|
2736 * @param root: the pointer to an XML node. |
|
2737 * @param ids: the pointer to a NULL terminated list of pairs {name, uri} |
|
2738 for looked up ID attributes. |
|
2739 * @return result code: |
|
2740 0 if OK; |
|
2741 -1 if duplication of ID happend; |
|
2742 -2 if OOM; |
|
2743 -3 if invalid arguments |
|
2744 * |
|
2745 * Recursively walks through all children of the root node and adds all attributes |
|
2746 * from the ids list to the root's document's IDs attributes hash. |
|
2747 * |
|
2748 * |
|
2749 * |
|
2750 * |
|
2751 * Prerequisites: TRUE == root && root->docs |
|
2752 * OOM: possible |
|
2753 */ |
|
2754 XMLPUBFUNEXPORT int |
|
2755 xmlAddIDs(xmlNodePtr root, const xmlChar** ids) |
|
2756 { |
|
2757 int ret; |
|
2758 LOAD_GS_SAFE_NODE(root) |
|
2759 |
|
2760 if(!root || !root->doc) |
|
2761 return -3; |
|
2762 |
|
2763 ret = xmlAddIDsInternal(root->doc, root, ids); |
|
2764 if(OOM_FLAG) |
|
2765 ret = -2; |
|
2766 |
|
2767 return ret; |
|
2768 } |
|
2769 |
|
2770 int |
|
2771 xmlAddIDsInternal(xmlDocPtr doc, xmlNodePtr cur, const xmlChar** ids) |
|
2772 { |
|
2773 int ret = 0; |
|
2774 xmlNodePtr children = NULL; |
|
2775 |
|
2776 if(cur && cur->type == XML_ELEMENT_NODE) |
|
2777 { |
|
2778 int i; |
|
2779 xmlAttrPtr attr; |
|
2780 const xmlChar* idAttrName; |
|
2781 //const xmlChar* idAttrUri; |
|
2782 |
|
2783 for(attr = cur->properties; attr; attr = attr->next) |
|
2784 { |
|
2785 for(i = 0; (idAttrName = ids[i]); i += 2) |
|
2786 { |
|
2787 if (xmlStrEqual(attr->name, idAttrName) && |
|
2788 ((attr->ns && xmlStrEqual(attr->ns->href,ids[i+1])) || (!attr->ns && !ids[i+1]))) |
|
2789 { |
|
2790 // Matching attribute found, get value (into 'name') |
|
2791 // and create ID info in the document |
|
2792 xmlChar* name = xmlNodeListGetString(doc, attr->children, 1); |
|
2793 if(name) |
|
2794 { |
|
2795 xmlAttrPtr tmp = xmlGetID(doc, name); |
|
2796 if(!tmp) |
|
2797 { // No such ID yet, try to add it |
|
2798 if(!xmlAddID(NULL, doc, name, attr)) |
|
2799 { |
|
2800 ret = -2; // OOM |
|
2801 } |
|
2802 } |
|
2803 else if(tmp != attr) |
|
2804 { |
|
2805 ret = -1; // already defined |
|
2806 } |
|
2807 xmlFree(name); |
|
2808 if(ret < 0) |
|
2809 return ret; |
|
2810 } |
|
2811 } |
|
2812 } |
|
2813 } |
|
2814 |
|
2815 children = cur->children; |
|
2816 } |
|
2817 else if(!cur) |
|
2818 { |
|
2819 children = doc->children; |
|
2820 } |
|
2821 |
|
2822 while(children) { |
|
2823 if(children->type == XML_ELEMENT_NODE) { |
|
2824 ret = xmlAddIDsInternal(doc, children, ids); |
|
2825 if(ret != 0) |
|
2826 return ret; |
|
2827 } |
|
2828 children = children->next; |
|
2829 } |
|
2830 return ret; |
|
2831 } |
|
2832 |
|
2833 |
|
2834 /************************************************************************ |
|
2835 * * |
|
2836 * Refs * |
|
2837 * * |
|
2838 ************************************************************************/ |
|
2839 typedef struct xmlRemoveMemo_t |
|
2840 { |
|
2841 xmlListPtr l; |
|
2842 xmlAttrPtr ap; |
|
2843 } xmlRemoveMemo; |
|
2844 |
|
2845 typedef xmlRemoveMemo *xmlRemoveMemoPtr; |
|
2846 |
|
2847 typedef struct xmlValidateMemo_t |
|
2848 { |
|
2849 xmlValidCtxtPtr ctxt; |
|
2850 const xmlChar *name; |
|
2851 } xmlValidateMemo; |
|
2852 |
|
2853 typedef xmlValidateMemo *xmlValidateMemoPtr; |
|
2854 |
|
2855 /** |
|
2856 * xmlCreateRefTable: |
|
2857 * |
|
2858 * create and initialize an empty ref hash table. |
|
2859 * |
|
2860 * Returns the xmlRefTablePtr just created or NULL in case |
|
2861 * of error. |
|
2862 */ |
|
2863 static xmlRefTablePtr |
|
2864 xmlCreateRefTable(void) { |
|
2865 return(xmlHashCreate(0)); |
|
2866 } |
|
2867 |
|
2868 /** |
|
2869 * xmlFreeRef: |
|
2870 * @param lk A list link |
|
2871 * |
|
2872 * Deallocate the memory used by a ref definition |
|
2873 */ |
|
2874 static void |
|
2875 xmlFreeRef(xmlLinkPtr lk) { |
|
2876 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk); |
|
2877 if (ref == NULL) return; |
|
2878 if (ref->value != NULL) |
|
2879 xmlFree((xmlChar *)ref->value); |
|
2880 if (ref->name != NULL) |
|
2881 xmlFree((xmlChar *)ref->name); |
|
2882 xmlFree(ref); |
|
2883 } |
|
2884 |
|
2885 /** |
|
2886 * xmlFreeRefList: |
|
2887 * @param list_ref A list of references. |
|
2888 * |
|
2889 * Deallocate the memory used by a list of references |
|
2890 */ |
|
2891 static void |
|
2892 xmlFreeRefList(xmlListPtr list_ref) { |
|
2893 if (list_ref == NULL) return; |
|
2894 xmlListDelete(list_ref); |
|
2895 } |
|
2896 |
|
2897 /** |
|
2898 * xmlWalkRemoveRef: |
|
2899 * @param data Contents of current link |
|
2900 * @param user Value supplied by the user |
|
2901 * |
|
2902 * Returns 0 to abort the walk or 1 to continue |
|
2903 */ |
|
2904 static int |
|
2905 xmlWalkRemoveRef(const void *data, const void *user) |
|
2906 { |
|
2907 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr; |
|
2908 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap; |
|
2909 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l; |
|
2910 |
|
2911 if (attr0 == attr1) { /* Matched: remove and terminate walk */ |
|
2912 xmlListRemoveFirst(ref_list, (void *)data); |
|
2913 return 0; |
|
2914 } |
|
2915 return 1; |
|
2916 } |
|
2917 |
|
2918 /** |
|
2919 * xmlDummyCompare |
|
2920 * @param data0 Value supplied by the user |
|
2921 * @param data1 Value supplied by the user |
|
2922 * |
|
2923 * Do nothing, return 0. Used to create unordered lists. |
|
2924 */ |
|
2925 static int |
|
2926 xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED, |
|
2927 const void *data1 ATTRIBUTE_UNUSED) |
|
2928 { |
|
2929 return (0); |
|
2930 } |
|
2931 |
|
2932 /** |
|
2933 * xmlAddRef: |
|
2934 * @param ctxt the validation context |
|
2935 * @param doc pointer to the document |
|
2936 * @param value the value name |
|
2937 * @param attr the attribute holding the Ref |
|
2938 * |
|
2939 * Register a new ref declaration |
|
2940 * |
|
2941 * Returns NULL if not, otherwise the new xmlRefPtr |
|
2942 */ |
|
2943 XMLPUBFUNEXPORT xmlRefPtr |
|
2944 xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, |
|
2945 xmlAttrPtr attr) { |
|
2946 xmlRefPtr ret; |
|
2947 xmlRefTablePtr table; |
|
2948 xmlListPtr ref_list; |
|
2949 |
|
2950 if (doc == NULL) { |
|
2951 return(NULL); |
|
2952 } |
|
2953 if (value == NULL) { |
|
2954 return(NULL); |
|
2955 } |
|
2956 if (attr == NULL) { |
|
2957 return(NULL); |
|
2958 } |
|
2959 |
|
2960 /* |
|
2961 * Create the Ref table if needed. |
|
2962 */ |
|
2963 table = (xmlRefTablePtr) doc->refs; |
|
2964 if (table == NULL) |
|
2965 doc->refs = table = xmlCreateRefTable(); |
|
2966 if (table == NULL) { |
|
2967 xmlVErrMemory(ctxt, |
|
2968 EMBED_ERRTXT("xmlAddRef: Table creation failed!\n")); |
|
2969 return(NULL); |
|
2970 } |
|
2971 |
|
2972 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef)); |
|
2973 if (ret == NULL) { |
|
2974 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
2975 return(NULL); |
|
2976 } |
|
2977 |
|
2978 /* |
|
2979 * fill the structure. |
|
2980 */ |
|
2981 ret->value = xmlStrdup(value); |
|
2982 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) |
|
2983 { |
|
2984 /* |
|
2985 * Operating in streaming mode, attr is gonna disapear |
|
2986 */ |
|
2987 ret->name = xmlStrdup(attr->name); |
|
2988 ret->attr = NULL; |
|
2989 } else { |
|
2990 ret->name = NULL; |
|
2991 ret->attr = attr; |
|
2992 } |
|
2993 |
|
2994 #ifdef LIBXML_ENABLE_NODE_LINEINFO |
|
2995 ret->lineno = xmlGetLineNo(attr->parent); |
|
2996 #endif |
|
2997 |
|
2998 /* To add a reference :- |
|
2999 * References are maintained as a list of references, |
|
3000 * Lookup the entry, if no entry create new nodelist |
|
3001 * Add the owning node to the NodeList |
|
3002 * Return the ref |
|
3003 */ |
|
3004 |
|
3005 if (NULL == (ref_list = (xmlListPtr)xmlHashLookup(table, value))) { |
|
3006 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) { |
|
3007 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
3008 EMBED_ERRTXT("xmlAddRef: Reference list creation failed!\n"), |
|
3009 NULL); |
|
3010 return(NULL); |
|
3011 } |
|
3012 if (xmlHashAddEntry(table, value, ref_list) < 0) { |
|
3013 xmlListDelete(ref_list); |
|
3014 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, |
|
3015 EMBED_ERRTXT("xmlAddRef: Reference list insertion failed!\n"), |
|
3016 NULL); |
|
3017 return(NULL); |
|
3018 } |
|
3019 } |
|
3020 /* xmlListInsert(ref_list, ret); */ |
|
3021 xmlListAppend(ref_list, ret); |
|
3022 return(ret); |
|
3023 } |
|
3024 |
|
3025 /** |
|
3026 * xmlFreeRefTable: |
|
3027 * @param table An ref table |
|
3028 * |
|
3029 * Deallocate the memory used by an Ref hash table. |
|
3030 */ |
|
3031 XMLPUBFUNEXPORT void |
|
3032 xmlFreeRefTable(xmlRefTablePtr table) { |
|
3033 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList); |
|
3034 } |
|
3035 |
|
3036 /** |
|
3037 * xmlIsRef: |
|
3038 * @param doc the document |
|
3039 * @param elem the element carrying the attribute |
|
3040 * @param attr the attribute |
|
3041 * |
|
3042 * Determine whether an attribute is of type Ref. In case we have DTD(s) |
|
3043 * then this is simple, otherwise we use an heuristic: name Ref (upper |
|
3044 * or lowercase). |
|
3045 * |
|
3046 * Returns 0 or 1 depending on the lookup result |
|
3047 */ |
|
3048 XMLPUBFUNEXPORT int |
|
3049 xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) { |
|
3050 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) { |
|
3051 return(0); |
|
3052 } else if (doc->type == XML_HTML_DOCUMENT_NODE) { |
|
3053 |
|
3054 return(0); |
|
3055 } else { |
|
3056 xmlAttributePtr attrDecl; |
|
3057 |
|
3058 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name); |
|
3059 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
3060 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, |
|
3061 elem->name, attr->name); |
|
3062 |
|
3063 if ((attrDecl != NULL) && |
|
3064 (attrDecl->atype == XML_ATTRIBUTE_IDREF || |
|
3065 attrDecl->atype == XML_ATTRIBUTE_IDREFS)) |
|
3066 return(1); |
|
3067 } |
|
3068 return(0); |
|
3069 } |
|
3070 |
|
3071 /** |
|
3072 * xmlRemoveRef: |
|
3073 * @param doc the document |
|
3074 * @param attr the attribute |
|
3075 * |
|
3076 * Remove the given attribute from the Ref table maintained internally. |
|
3077 * |
|
3078 * Returns -1 if the lookup failed and 0 otherwise |
|
3079 */ |
|
3080 XMLPUBFUNEXPORT int |
|
3081 xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) { |
|
3082 xmlListPtr ref_list; |
|
3083 xmlRefTablePtr table; |
|
3084 xmlChar *ID; |
|
3085 xmlRemoveMemo target; |
|
3086 |
|
3087 if (doc == NULL) return(-1); |
|
3088 if (attr == NULL) return(-1); |
|
3089 table = (xmlRefTablePtr) doc->refs; |
|
3090 if (table == NULL) |
|
3091 return(-1); |
|
3092 |
|
3093 if (attr == NULL) |
|
3094 return(-1); |
|
3095 ID = xmlNodeListGetString(doc, attr->children, 1); |
|
3096 if (ID == NULL) |
|
3097 return(-1); |
|
3098 ref_list = (xmlListPtr)xmlHashLookup(table, ID); |
|
3099 |
|
3100 if(ref_list == NULL) { |
|
3101 xmlFree(ID); |
|
3102 return (-1); |
|
3103 } |
|
3104 /* At this point, ref_list refers to a list of references which |
|
3105 * have the same key as the supplied attr. Our list of references |
|
3106 * is ordered by reference address and we don't have that information |
|
3107 * here to use when removing. We'll have to walk the list and |
|
3108 * check for a matching attribute, when we find one stop the walk |
|
3109 * and remove the entry. |
|
3110 * The list is ordered by reference, so that means we don't have the |
|
3111 * key. Passing the list and the reference to the walker means we |
|
3112 * will have enough data to be able to remove the entry. |
|
3113 */ |
|
3114 target.l = ref_list; |
|
3115 target.ap = attr; |
|
3116 |
|
3117 /* Remove the supplied attr from our list */ |
|
3118 xmlListWalk(ref_list, xmlWalkRemoveRef, &target); |
|
3119 |
|
3120 /*If the list is empty then remove the list entry in the hash */ |
|
3121 if (xmlListEmpty(ref_list)) |
|
3122 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) |
|
3123 xmlFreeRefList); |
|
3124 xmlFree(ID); |
|
3125 return(0); |
|
3126 } |
|
3127 |
|
3128 #ifndef XMLENGINE_EXCLUDE_UNUSED |
|
3129 /** |
|
3130 * xmlGetRefs: |
|
3131 * @param doc pointer to the document |
|
3132 * @param ID the ID value |
|
3133 * |
|
3134 * Find the set of references for the supplied ID. |
|
3135 * |
|
3136 * Returns NULL if not found, otherwise node set for the ID. |
|
3137 */ |
|
3138 xmlListPtr |
|
3139 xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) { |
|
3140 xmlRefTablePtr table; |
|
3141 |
|
3142 if (doc == NULL) { |
|
3143 return(NULL); |
|
3144 } |
|
3145 |
|
3146 if (ID == NULL) { |
|
3147 return(NULL); |
|
3148 } |
|
3149 |
|
3150 table = (xmlRefTablePtr) doc->refs; |
|
3151 if (table == NULL) |
|
3152 return(NULL); |
|
3153 |
|
3154 return (xmlHashLookup(table, ID)); |
|
3155 } |
|
3156 #endif /* ifndef XMLENGINE_EXCLUDE_UNUSED */ |
|
3157 |
|
3158 /************************************************************************ |
|
3159 * * |
|
3160 * Routines for validity checking * |
|
3161 * * |
|
3162 ************************************************************************/ |
|
3163 |
|
3164 /** |
|
3165 * xmlGetDtdElementDesc: |
|
3166 * @param dtd a pointer to the DtD to search |
|
3167 * @param name the element name |
|
3168 * |
|
3169 * Search the DTD for the description of this element |
|
3170 * |
|
3171 * returns the xmlElementPtr if found or NULL |
|
3172 */ |
|
3173 |
|
3174 XMLPUBFUNEXPORT xmlElementPtr |
|
3175 xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) { |
|
3176 xmlElementTablePtr table; |
|
3177 xmlElementPtr cur; |
|
3178 xmlChar *uqname = NULL, *prefix = NULL; |
|
3179 |
|
3180 if ((dtd == NULL) || (name == NULL)) return(NULL); |
|
3181 if (dtd->elements == NULL) |
|
3182 return(NULL); |
|
3183 table = (xmlElementTablePtr) dtd->elements; |
|
3184 |
|
3185 uqname = xmlSplitQName2(name, &prefix); |
|
3186 if (uqname != NULL) |
|
3187 name = uqname; |
|
3188 cur = (xmlElementPtr)xmlHashLookup2(table, name, prefix); |
|
3189 if (prefix != NULL) xmlFree(prefix); |
|
3190 if (uqname != NULL) xmlFree(uqname); |
|
3191 return(cur); |
|
3192 } |
|
3193 /** |
|
3194 * xmlGetDtdElementDesc2: |
|
3195 * @param dtd a pointer to the DtD to search |
|
3196 * @param name the element name |
|
3197 * @param create create an empty description if not found |
|
3198 * |
|
3199 * Search the DTD for the description of this element |
|
3200 * |
|
3201 * returns the xmlElementPtr if found or NULL |
|
3202 */ |
|
3203 |
|
3204 static xmlElementPtr |
|
3205 xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) { |
|
3206 xmlElementTablePtr table; |
|
3207 xmlElementPtr cur; |
|
3208 xmlChar *uqname = NULL, *prefix = NULL; |
|
3209 |
|
3210 if (dtd == NULL) return(NULL); |
|
3211 if (dtd->elements == NULL) { |
|
3212 if (!create) |
|
3213 return(NULL); |
|
3214 /* |
|
3215 * Create the Element table if needed. |
|
3216 */ |
|
3217 table = (xmlElementTablePtr) dtd->elements; |
|
3218 if (table == NULL) { |
|
3219 table = xmlCreateElementTable(); |
|
3220 dtd->elements = (void *) table; |
|
3221 } |
|
3222 if (table == NULL) { |
|
3223 xmlVErrMemory(NULL, EMBED_ERRTXT("element table allocation failed")); |
|
3224 return(NULL); |
|
3225 } |
|
3226 } |
|
3227 table = (xmlElementTablePtr) dtd->elements; |
|
3228 |
|
3229 uqname = xmlSplitQName2(name, &prefix); |
|
3230 if (uqname != NULL) |
|
3231 name = uqname; |
|
3232 cur = (xmlElementPtr)xmlHashLookup2(table, name, prefix); |
|
3233 if ((cur == NULL) && (create)) { |
|
3234 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); |
|
3235 if (cur == NULL) { |
|
3236 xmlVErrMemory(NULL, EMBED_ERRTXT("malloc failed")); |
|
3237 return(NULL); |
|
3238 } |
|
3239 memset(cur, 0, sizeof(xmlElement)); |
|
3240 cur->type = XML_ELEMENT_DECL; |
|
3241 |
|
3242 /* |
|
3243 * fill the structure. |
|
3244 */ |
|
3245 cur->name = xmlStrdup(name); |
|
3246 cur->prefix = xmlStrdup(prefix); |
|
3247 cur->etype = XML_ELEMENT_TYPE_UNDEFINED; |
|
3248 |
|
3249 xmlHashAddEntry2(table, name, prefix, cur); |
|
3250 } |
|
3251 if (prefix != NULL) xmlFree(prefix); |
|
3252 if (uqname != NULL) xmlFree(uqname); |
|
3253 return(cur); |
|
3254 } |
|
3255 |
|
3256 /** |
|
3257 * xmlGetDtdQElementDesc: |
|
3258 * @param dtd a pointer to the DtD to search |
|
3259 * @param name the element name |
|
3260 * @param prefix the element namespace prefix |
|
3261 * |
|
3262 * Search the DTD for the description of this element |
|
3263 * |
|
3264 * returns the xmlElementPtr if found or NULL |
|
3265 * |
|
3266 * OOM: never |
|
3267 */ |
|
3268 |
|
3269 XMLPUBFUNEXPORT xmlElementPtr |
|
3270 xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name, |
|
3271 const xmlChar *prefix) { |
|
3272 xmlElementTablePtr table; |
|
3273 |
|
3274 if (dtd == NULL) |
|
3275 return(NULL); |
|
3276 if (dtd->elements == NULL) |
|
3277 return(NULL); |
|
3278 |
|
3279 table = (xmlElementTablePtr) dtd->elements; |
|
3280 |
|
3281 return (xmlElementPtr)(xmlHashLookup2(table, name, prefix)); |
|
3282 } |
|
3283 |
|
3284 /** |
|
3285 * xmlGetDtdAttrDesc: |
|
3286 * @param dtd a pointer to the DtD to search |
|
3287 * @param elem the element name |
|
3288 * @param name the attribute name |
|
3289 * |
|
3290 * Search the DTD for the description of this attribute on |
|
3291 * this element. |
|
3292 * |
|
3293 * returns the xmlAttributePtr if found or NULL |
|
3294 * |
|
3295 * OOM: iif returns NULL and OOM flag is set |
|
3296 */ |
|
3297 |
|
3298 XMLPUBFUNEXPORT xmlAttributePtr |
|
3299 xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) |
|
3300 { |
|
3301 xmlAttributeTablePtr table; |
|
3302 xmlAttributePtr cur; |
|
3303 xmlChar* uqname; // DONE: removed unneeded initialization // = NULL; |
|
3304 xmlChar* prefix; // = NULL; // xmlSplitQName2 sets it NULL as the first thing |
|
3305 LOAD_GS_SAFE_DTD(dtd) |
|
3306 |
|
3307 if (!dtd || !dtd->attributes) |
|
3308 return(NULL); |
|
3309 |
|
3310 table = (xmlAttributeTablePtr) dtd->attributes; |
|
3311 // DONE: removed redundant check |
|
3312 //if (table == NULL) |
|
3313 // return(NULL); |
|
3314 |
|
3315 uqname = xmlSplitQName2(name, &prefix); |
|
3316 |
|
3317 if (uqname) { |
|
3318 cur = (xmlAttributePtr)xmlHashLookup3(table, uqname, prefix, elem); |
|
3319 if (prefix) xmlFree(prefix); |
|
3320 if (uqname) xmlFree(uqname); |
|
3321 } else { |
|
3322 // Check OOM |
|
3323 if(OOM_FLAG) |
|
3324 return NULL; |
|
3325 cur = (xmlAttributePtr)xmlHashLookup3(table, name, NULL, elem); |
|
3326 } |
|
3327 return(cur); |
|
3328 } |
|
3329 |
|
3330 /** |
|
3331 * xmlGetDtdQAttrDesc: |
|
3332 * @param dtd a pointer to the DtD to search |
|
3333 * @param elem the element name |
|
3334 * @param name the attribute name |
|
3335 * @param prefix the attribute namespace prefix |
|
3336 * |
|
3337 * Search the DTD for the description of this qualified attribute on |
|
3338 * this element. |
|
3339 * |
|
3340 * returns the xmlAttributePtr if found or NULL |
|
3341 * |
|
3342 * OOM: never |
|
3343 */ |
|
3344 |
|
3345 XMLPUBFUNEXPORT xmlAttributePtr |
|
3346 xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar* elem, const xmlChar* name, const xmlChar* prefix) |
|
3347 { |
|
3348 xmlAttributeTablePtr table; |
|
3349 |
|
3350 if (!dtd) |
|
3351 return(NULL); |
|
3352 table = (xmlAttributeTablePtr) dtd->attributes; |
|
3353 // For NULL table result is NULL |
|
3354 return (xmlAttributePtr)(xmlHashLookup3(table, name, prefix, elem)); |
|
3355 } |
|
3356 |
|
3357 /** |
|
3358 * xmlGetDtdNotationDesc: |
|
3359 * @param dtd a pointer to the DtD to search |
|
3360 * @param name the notation name |
|
3361 * |
|
3362 * Search the DTD for the description of this notation |
|
3363 * |
|
3364 * returns the xmlNotationPtr if found or NULL |
|
3365 */ |
|
3366 |
|
3367 XMLPUBFUNEXPORT xmlNotationPtr |
|
3368 xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) { |
|
3369 xmlNotationTablePtr table; |
|
3370 |
|
3371 if (dtd == NULL) return(NULL); |
|
3372 if (dtd->notations == NULL) return(NULL); |
|
3373 table = (xmlNotationTablePtr) dtd->notations; |
|
3374 |
|
3375 return (xmlNotationPtr)(xmlHashLookup(table, name)); |
|
3376 } |
|
3377 |
|
3378 #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) || defined (XMLENGINE_XMLSCHEMA_DATATYPES) |
|
3379 /** |
|
3380 * xmlValidateNotationUse: |
|
3381 * @param ctxt the validation context |
|
3382 * @param doc the document |
|
3383 * @param notationName the notation name to check |
|
3384 * |
|
3385 * Validate that the given name match a notation declaration. |
|
3386 * - [ VC: Notation Declared ] |
|
3387 * |
|
3388 * returns 1 if valid or 0 otherwise |
|
3389 */ |
|
3390 |
|
3391 XMLPUBFUNEXPORT int |
|
3392 xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
3393 const xmlChar *notationName) { |
|
3394 xmlNotationPtr notaDecl; |
|
3395 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1); |
|
3396 |
|
3397 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName); |
|
3398 if ((notaDecl == NULL) && (doc->extSubset != NULL)) |
|
3399 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName); |
|
3400 |
|
3401 if ((notaDecl == NULL) && (ctxt != NULL)) { |
|
3402 xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION, |
|
3403 EMBED_ERRTXT("NOTATION %s is not declared\n"), |
|
3404 notationName, NULL, NULL); |
|
3405 return(0); |
|
3406 } |
|
3407 return(1); |
|
3408 } |
|
3409 #endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */ |
|
3410 |
|
3411 /** |
|
3412 * xmlIsMixedElement: |
|
3413 * @param doc the document |
|
3414 * @param name the element name |
|
3415 * |
|
3416 * Search in the DtDs whether an element accept Mixed content (or ANY) |
|
3417 * basically if it is supposed to accept text childs |
|
3418 * |
|
3419 * returns 0 if no, 1 if yes, and -1 if no element description is available |
|
3420 */ |
|
3421 |
|
3422 XMLPUBFUNEXPORT int |
|
3423 xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) { |
|
3424 xmlElementPtr elemDecl; |
|
3425 |
|
3426 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1); |
|
3427 |
|
3428 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name); |
|
3429 if ((elemDecl == NULL) && (doc->extSubset != NULL)) |
|
3430 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name); |
|
3431 if (elemDecl == NULL) return(-1); |
|
3432 switch (elemDecl->etype) { |
|
3433 case XML_ELEMENT_TYPE_UNDEFINED: |
|
3434 return(-1); |
|
3435 case XML_ELEMENT_TYPE_ELEMENT: |
|
3436 return(0); |
|
3437 case XML_ELEMENT_TYPE_EMPTY: |
|
3438 /* |
|
3439 * return 1 for EMPTY since we want VC error to pop up |
|
3440 * on <empty> </empty> for example |
|
3441 */ |
|
3442 case XML_ELEMENT_TYPE_ANY: |
|
3443 case XML_ELEMENT_TYPE_MIXED: |
|
3444 return(1); |
|
3445 } |
|
3446 return(1); |
|
3447 } |
|
3448 |
|
3449 #ifdef LIBXML_VALID_ENABLED |
|
3450 /** |
|
3451 * xmlValidateNameValue: |
|
3452 * @param value an Name value |
|
3453 * |
|
3454 * Validate that the given value match Name production |
|
3455 * |
|
3456 * returns 1 if valid or 0 otherwise |
|
3457 */ |
|
3458 |
|
3459 int |
|
3460 xmlValidateNameValue(const xmlChar *value) { |
|
3461 const xmlChar *cur; |
|
3462 int val, len; |
|
3463 |
|
3464 if (value == NULL) return(0); |
|
3465 cur = value; |
|
3466 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3467 cur += len; |
|
3468 if (!IS_LETTER(val) && (val != '_') && |
|
3469 (val != ':')) { |
|
3470 return(0); |
|
3471 } |
|
3472 |
|
3473 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3474 cur += len; |
|
3475 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || |
|
3476 (val == '.') || (val == '-') || |
|
3477 (val == '_') || (val == ':') || |
|
3478 (IS_COMBINING(val)) || |
|
3479 (IS_EXTENDER(val))) { |
|
3480 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3481 cur += len; |
|
3482 } |
|
3483 |
|
3484 if (val != 0) return(0); |
|
3485 |
|
3486 return(1); |
|
3487 } |
|
3488 |
|
3489 /** |
|
3490 * xmlValidateNamesValue: |
|
3491 * @param value an Names value |
|
3492 * |
|
3493 * Validate that the given value match Names production |
|
3494 * |
|
3495 * returns 1 if valid or 0 otherwise |
|
3496 */ |
|
3497 |
|
3498 int |
|
3499 xmlValidateNamesValue(const xmlChar *value) { |
|
3500 const xmlChar *cur; |
|
3501 int val, len; |
|
3502 |
|
3503 if (value == NULL) return(0); |
|
3504 cur = value; |
|
3505 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3506 cur += len; |
|
3507 |
|
3508 if (!IS_LETTER(val) && (val != '_') && |
|
3509 (val != ':')) { |
|
3510 return(0); |
|
3511 } |
|
3512 |
|
3513 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3514 cur += len; |
|
3515 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || |
|
3516 (val == '.') || (val == '-') || |
|
3517 (val == '_') || (val == ':') || |
|
3518 (IS_COMBINING(val)) || |
|
3519 (IS_EXTENDER(val))) { |
|
3520 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3521 cur += len; |
|
3522 } |
|
3523 |
|
3524 while (IS_BLANK(val)) { |
|
3525 while (IS_BLANK(val)) { |
|
3526 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3527 cur += len; |
|
3528 } |
|
3529 |
|
3530 if (!IS_LETTER(val) && (val != '_') && |
|
3531 (val != ':')) { |
|
3532 return(0); |
|
3533 } |
|
3534 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3535 cur += len; |
|
3536 |
|
3537 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || |
|
3538 (val == '.') || (val == '-') || |
|
3539 (val == '_') || (val == ':') || |
|
3540 (IS_COMBINING(val)) || |
|
3541 (IS_EXTENDER(val))) { |
|
3542 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3543 cur += len; |
|
3544 } |
|
3545 } |
|
3546 |
|
3547 if (val != 0) return(0); |
|
3548 |
|
3549 return(1); |
|
3550 } |
|
3551 |
|
3552 /** |
|
3553 * xmlValidateNmtokenValue: |
|
3554 * @param value an Nmtoken value |
|
3555 * |
|
3556 * Validate that the given value match Nmtoken production |
|
3557 * |
|
3558 * [ VC: Name Token ] |
|
3559 * |
|
3560 * returns 1 if valid or 0 otherwise |
|
3561 */ |
|
3562 |
|
3563 int |
|
3564 xmlValidateNmtokenValue(const xmlChar *value) { |
|
3565 const xmlChar *cur; |
|
3566 int val, len; |
|
3567 |
|
3568 if (value == NULL) return(0); |
|
3569 cur = value; |
|
3570 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3571 cur += len; |
|
3572 |
|
3573 if (!IS_LETTER(val) && !IS_DIGIT(val) && |
|
3574 (val != '.') && (val != '-') && |
|
3575 (val != '_') && (val != ':') && |
|
3576 (!IS_COMBINING(val)) && |
|
3577 (!IS_EXTENDER(val))) |
|
3578 return(0); |
|
3579 |
|
3580 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || |
|
3581 (val == '.') || (val == '-') || |
|
3582 (val == '_') || (val == ':') || |
|
3583 (IS_COMBINING(val)) || |
|
3584 (IS_EXTENDER(val))) { |
|
3585 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3586 cur += len; |
|
3587 } |
|
3588 |
|
3589 if (val != 0) return(0); |
|
3590 |
|
3591 return(1); |
|
3592 } |
|
3593 |
|
3594 /** |
|
3595 * xmlValidateNmtokensValue: |
|
3596 * @param value an Nmtokens value |
|
3597 * |
|
3598 * Validate that the given value match Nmtokens production |
|
3599 * |
|
3600 * [ VC: Name Token ] |
|
3601 * |
|
3602 * returns 1 if valid or 0 otherwise |
|
3603 */ |
|
3604 |
|
3605 int |
|
3606 xmlValidateNmtokensValue(const xmlChar *value) { |
|
3607 const xmlChar *cur; |
|
3608 int val, len; |
|
3609 |
|
3610 if (value == NULL) return(0); |
|
3611 cur = value; |
|
3612 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3613 cur += len; |
|
3614 |
|
3615 while (IS_BLANK(val)) { |
|
3616 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3617 cur += len; |
|
3618 } |
|
3619 |
|
3620 if (!IS_LETTER(val) && !IS_DIGIT(val) && |
|
3621 (val != '.') && (val != '-') && |
|
3622 (val != '_') && (val != ':') && |
|
3623 (!IS_COMBINING(val)) && |
|
3624 (!IS_EXTENDER(val))) |
|
3625 return(0); |
|
3626 |
|
3627 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || |
|
3628 (val == '.') || (val == '-') || |
|
3629 (val == '_') || (val == ':') || |
|
3630 (IS_COMBINING(val)) || |
|
3631 (IS_EXTENDER(val))) { |
|
3632 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3633 cur += len; |
|
3634 } |
|
3635 |
|
3636 while (IS_BLANK(val)) { |
|
3637 while (IS_BLANK(val)) { |
|
3638 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3639 cur += len; |
|
3640 } |
|
3641 if (val == 0) return(1); |
|
3642 |
|
3643 if (!IS_LETTER(val) && !IS_DIGIT(val) && |
|
3644 (val != '.') && (val != '-') && |
|
3645 (val != '_') && (val != ':') && |
|
3646 (!IS_COMBINING(val)) && |
|
3647 (!IS_EXTENDER(val))) |
|
3648 return(0); |
|
3649 |
|
3650 while ((IS_LETTER(val)) || (IS_DIGIT(val)) || |
|
3651 (val == '.') || (val == '-') || |
|
3652 (val == '_') || (val == ':') || |
|
3653 (IS_COMBINING(val)) || |
|
3654 (IS_EXTENDER(val))) { |
|
3655 val = xmlStringCurrentChar(NULL, cur, &len); |
|
3656 cur += len; |
|
3657 } |
|
3658 } |
|
3659 |
|
3660 if (val != 0) return(0); |
|
3661 |
|
3662 return(1); |
|
3663 } |
|
3664 |
|
3665 /** |
|
3666 * xmlValidateNotationDecl: |
|
3667 * @param ctxt the validation context |
|
3668 * @param doc a document instance |
|
3669 * @param nota a notation definition |
|
3670 * |
|
3671 * Try to validate a single notation definition |
|
3672 * basically it does the following checks as described by the |
|
3673 * XML-1.0 recommendation: |
|
3674 * - it seems that no validity constraint exists on notation declarations |
|
3675 * But this function get called anyway ... |
|
3676 * |
|
3677 * returns 1 if valid or 0 otherwise |
|
3678 */ |
|
3679 |
|
3680 int |
|
3681 xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED, |
|
3682 xmlNotationPtr nota ATTRIBUTE_UNUSED) { |
|
3683 int ret = 1; |
|
3684 |
|
3685 return(ret); |
|
3686 } |
|
3687 |
|
3688 /** |
|
3689 * xmlValidateAttributeValue: |
|
3690 * @param type an attribute type |
|
3691 * @param value an attribute value |
|
3692 * |
|
3693 * Validate that the given attribute value match the proper production |
|
3694 * |
|
3695 * [ VC: ID ] |
|
3696 * Values of type ID must match the Name production.... |
|
3697 * |
|
3698 * [ VC: IDREF ] |
|
3699 * Values of type IDREF must match the Name production, and values |
|
3700 * of type IDREFS must match Names ... |
|
3701 * |
|
3702 * [ VC: Entity Name ] |
|
3703 * Values of type ENTITY must match the Name production, values |
|
3704 * of type ENTITIES must match Names ... |
|
3705 * |
|
3706 * [ VC: Name Token ] |
|
3707 * Values of type NMTOKEN must match the Nmtoken production; values |
|
3708 * of type NMTOKENS must match Nmtokens. |
|
3709 * |
|
3710 * returns 1 if valid or 0 otherwise |
|
3711 */ |
|
3712 |
|
3713 int |
|
3714 xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) { |
|
3715 switch (type) { |
|
3716 case XML_ATTRIBUTE_ENTITIES: |
|
3717 case XML_ATTRIBUTE_IDREFS: |
|
3718 return(xmlValidateNamesValue(value)); |
|
3719 case XML_ATTRIBUTE_ENTITY: |
|
3720 case XML_ATTRIBUTE_IDREF: |
|
3721 case XML_ATTRIBUTE_ID: |
|
3722 case XML_ATTRIBUTE_NOTATION: |
|
3723 return(xmlValidateNameValue(value)); |
|
3724 case XML_ATTRIBUTE_NMTOKENS: |
|
3725 case XML_ATTRIBUTE_ENUMERATION: |
|
3726 return(xmlValidateNmtokensValue(value)); |
|
3727 case XML_ATTRIBUTE_NMTOKEN: |
|
3728 return(xmlValidateNmtokenValue(value)); |
|
3729 case XML_ATTRIBUTE_CDATA: |
|
3730 break; |
|
3731 } |
|
3732 return(1); |
|
3733 } |
|
3734 |
|
3735 /** |
|
3736 * xmlValidateAttributeValue2: |
|
3737 * @param ctxt the validation context |
|
3738 * @param doc the document |
|
3739 * @param name the attribute name (used for error reporting only) |
|
3740 * @param type the attribute type |
|
3741 * @param value the attribute value |
|
3742 * |
|
3743 * Validate that the given attribute value match a given type. |
|
3744 * This typically cannot be done before having finished parsing |
|
3745 * the subsets. |
|
3746 * |
|
3747 * [ VC: IDREF ] |
|
3748 * Values of type IDREF must match one of the declared IDs |
|
3749 * Values of type IDREFS must match a sequence of the declared IDs |
|
3750 * each Name must match the value of an ID attribute on some element |
|
3751 * in the XML document; i.e. IDREF values must match the value of |
|
3752 * some ID attribute |
|
3753 * |
|
3754 * [ VC: Entity Name ] |
|
3755 * Values of type ENTITY must match one declared entity |
|
3756 * Values of type ENTITIES must match a sequence of declared entities |
|
3757 * |
|
3758 * [ VC: Notation Attributes ] |
|
3759 * all notation names in the declaration must be declared. |
|
3760 * |
|
3761 * returns 1 if valid or 0 otherwise |
|
3762 */ |
|
3763 |
|
3764 static int |
|
3765 xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
3766 const xmlChar *name, xmlAttributeType type, const xmlChar *value) { |
|
3767 int ret = 1; |
|
3768 switch (type) { |
|
3769 case XML_ATTRIBUTE_IDREFS: |
|
3770 case XML_ATTRIBUTE_IDREF: |
|
3771 case XML_ATTRIBUTE_ID: |
|
3772 case XML_ATTRIBUTE_NMTOKENS: |
|
3773 case XML_ATTRIBUTE_ENUMERATION: |
|
3774 case XML_ATTRIBUTE_NMTOKEN: |
|
3775 case XML_ATTRIBUTE_CDATA: |
|
3776 break; |
|
3777 case XML_ATTRIBUTE_ENTITY: { |
|
3778 xmlEntityPtr ent; |
|
3779 |
|
3780 ent = xmlGetDocEntity(doc, value); |
|
3781 /* yeah it's a bit messy... */ |
|
3782 if ((ent == NULL) && (doc->standalone == 1)) { |
|
3783 doc->standalone = 0; |
|
3784 ent = xmlGetDocEntity(doc, value); |
|
3785 } |
|
3786 if (ent == NULL) { |
|
3787 xmlErrValidNode(ctxt, (xmlNodePtr) doc, |
|
3788 XML_DTD_UNKNOWN_ENTITY, |
|
3789 EMBED_ERRTXT("ENTITY attribute %s reference an unknown entity \"%s\"\n"), |
|
3790 name, value, NULL); |
|
3791 ret = 0; |
|
3792 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { |
|
3793 xmlErrValidNode(ctxt, (xmlNodePtr) doc, |
|
3794 XML_DTD_ENTITY_TYPE, |
|
3795 EMBED_ERRTXT("ENTITY attribute %s reference an entity \"%s\" of wrong type\n"), |
|
3796 name, value, NULL); |
|
3797 ret = 0; |
|
3798 } |
|
3799 break; |
|
3800 } |
|
3801 case XML_ATTRIBUTE_ENTITIES: { |
|
3802 xmlChar *dup, *nam = NULL, *cur, save; |
|
3803 xmlEntityPtr ent; |
|
3804 |
|
3805 dup = xmlStrdup(value); |
|
3806 if (dup == NULL) |
|
3807 return(0); |
|
3808 cur = dup; |
|
3809 while (*cur != 0) { |
|
3810 nam = cur; |
|
3811 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; |
|
3812 save = *cur; |
|
3813 *cur = 0; |
|
3814 ent = xmlGetDocEntity(doc, nam); |
|
3815 if (ent == NULL) { |
|
3816 xmlErrValidNode(ctxt, (xmlNodePtr) doc, |
|
3817 XML_DTD_UNKNOWN_ENTITY, |
|
3818 EMBED_ERRTXT("ENTITIES attribute %s reference an unknown entity \"%s\"\n"), |
|
3819 name, nam, NULL); |
|
3820 ret = 0; |
|
3821 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { |
|
3822 xmlErrValidNode(ctxt, (xmlNodePtr) doc, |
|
3823 XML_DTD_ENTITY_TYPE, |
|
3824 EMBED_ERRTXT("ENTITIES attribute %s reference an entity \"%s\" of wrong type\n"), |
|
3825 name, nam, NULL); |
|
3826 ret = 0; |
|
3827 } |
|
3828 if (save == 0) |
|
3829 break; |
|
3830 *cur = save; |
|
3831 while (IS_BLANK_CH(*cur)) cur++; |
|
3832 } |
|
3833 xmlFree(dup); |
|
3834 break; |
|
3835 } |
|
3836 case XML_ATTRIBUTE_NOTATION: { |
|
3837 xmlNotationPtr nota; |
|
3838 |
|
3839 nota = xmlGetDtdNotationDesc(doc->intSubset, value); |
|
3840 if ((nota == NULL) && (doc->extSubset != NULL)) |
|
3841 nota = xmlGetDtdNotationDesc(doc->extSubset, value); |
|
3842 |
|
3843 if (nota == NULL) { |
|
3844 xmlErrValidNode(ctxt, (xmlNodePtr) doc, |
|
3845 XML_DTD_UNKNOWN_NOTATION, |
|
3846 EMBED_ERRTXT("NOTATION attribute %s reference an unknown notation \"%s\"\n"), |
|
3847 name, value, NULL); |
|
3848 ret = 0; |
|
3849 } |
|
3850 break; |
|
3851 } |
|
3852 } |
|
3853 return(ret); |
|
3854 } |
|
3855 |
|
3856 /** |
|
3857 * xmlValidCtxtNormalizeAttributeValue: |
|
3858 * @param ctxt the validation context |
|
3859 * @param doc the document |
|
3860 * @param elem the parent |
|
3861 * @param name the attribute name |
|
3862 * @param value the attribute value |
|
3863 * @param ctxt the validation context or NULL |
|
3864 * |
|
3865 * Does the validation related extra step of the normalization of attribute |
|
3866 * values: |
|
3867 * |
|
3868 * If the declared value is not CDATA, then the XML processor must further |
|
3869 * process the normalized attribute value by discarding any leading and |
|
3870 * trailing space (#x20) characters, and by replacing sequences of space |
|
3871 * (#x20) characters by single space (#x20) character. |
|
3872 * |
|
3873 * Also check VC: Standalone Document Declaration in P32, and update |
|
3874 * ctxt->valid accordingly |
|
3875 * |
|
3876 * returns a new normalized string if normalization is needed, NULL otherwise |
|
3877 * the caller must free the returned value. |
|
3878 */ |
|
3879 |
|
3880 xmlChar * |
|
3881 xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
3882 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) { |
|
3883 xmlChar *ret, *dst; |
|
3884 const xmlChar *src; |
|
3885 xmlAttributePtr attrDecl = NULL; |
|
3886 int extsubset = 0; |
|
3887 |
|
3888 if (doc == NULL) return(NULL); |
|
3889 if (elem == NULL) return(NULL); |
|
3890 if (name == NULL) return(NULL); |
|
3891 if (value == NULL) return(NULL); |
|
3892 |
|
3893 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { |
|
3894 xmlChar fn[50]; |
|
3895 xmlChar *fullname; |
|
3896 |
|
3897 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); |
|
3898 if (fullname == NULL) |
|
3899 return(0); |
|
3900 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name); |
|
3901 if ((attrDecl == NULL) && (doc->extSubset != NULL)) { |
|
3902 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name); |
|
3903 if (attrDecl != NULL) |
|
3904 extsubset = 1; |
|
3905 } |
|
3906 if ((fullname != fn) && (fullname != elem->name)) |
|
3907 xmlFree(fullname); |
|
3908 } |
|
3909 if ((attrDecl == NULL) && (doc->intSubset != NULL)) |
|
3910 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name); |
|
3911 if ((attrDecl == NULL) && (doc->extSubset != NULL)) { |
|
3912 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name); |
|
3913 if (attrDecl != NULL) |
|
3914 extsubset = 1; |
|
3915 } |
|
3916 |
|
3917 if (attrDecl == NULL) |
|
3918 return(NULL); |
|
3919 if (attrDecl->atype == XML_ATTRIBUTE_CDATA) |
|
3920 return(NULL); |
|
3921 |
|
3922 ret = xmlStrdup(value); |
|
3923 if (ret == NULL) |
|
3924 return(NULL); |
|
3925 src = value; |
|
3926 dst = ret; |
|
3927 while (*src == 0x20) src++; |
|
3928 while (*src != 0) { |
|
3929 if (*src == 0x20) { |
|
3930 while (*src == 0x20) src++; |
|
3931 if (*src != 0) |
|
3932 *dst++ = 0x20; |
|
3933 } else { |
|
3934 *dst++ = *src++; |
|
3935 } |
|
3936 } |
|
3937 *dst = 0; |
|
3938 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) { |
|
3939 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE, |
|
3940 EMBED_ERRTXT("standalone: %s on %s value had to be normalized based on external subset declaration\n"), |
|
3941 name, elem->name, NULL); |
|
3942 ctxt->valid = 0; |
|
3943 } |
|
3944 return(ret); |
|
3945 } |
|
3946 |
|
3947 /** |
|
3948 * xmlValidNormalizeAttributeValue: |
|
3949 * @param doc the document |
|
3950 * @param elem the parent |
|
3951 * @param name the attribute name |
|
3952 * @param value the attribute value |
|
3953 * |
|
3954 * Does the validation related extra step of the normalization of attribute |
|
3955 * values: |
|
3956 * |
|
3957 * If the declared value is not CDATA, then the XML processor must further |
|
3958 * process the normalized attribute value by discarding any leading and |
|
3959 * trailing space (#x20) characters, and by replacing sequences of space |
|
3960 * (#x20) characters by single space (#x20) character. |
|
3961 * |
|
3962 * Returns a new normalized string if normalization is needed, NULL otherwise |
|
3963 * the caller must free the returned value. |
|
3964 */ |
|
3965 |
|
3966 xmlChar * |
|
3967 xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem, |
|
3968 const xmlChar *name, const xmlChar *value) { |
|
3969 xmlChar *ret, *dst; |
|
3970 const xmlChar *src; |
|
3971 xmlAttributePtr attrDecl = NULL; |
|
3972 |
|
3973 if (doc == NULL) return(NULL); |
|
3974 if (elem == NULL) return(NULL); |
|
3975 if (name == NULL) return(NULL); |
|
3976 if (value == NULL) return(NULL); |
|
3977 |
|
3978 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { |
|
3979 xmlChar fn[50]; |
|
3980 xmlChar *fullname; |
|
3981 |
|
3982 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); |
|
3983 if (fullname == NULL) |
|
3984 return(0); |
|
3985 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name); |
|
3986 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
3987 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name); |
|
3988 if ((fullname != fn) && (fullname != elem->name)) |
|
3989 xmlFree(fullname); |
|
3990 } |
|
3991 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name); |
|
3992 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
3993 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name); |
|
3994 |
|
3995 if (attrDecl == NULL) |
|
3996 return(NULL); |
|
3997 if (attrDecl->atype == XML_ATTRIBUTE_CDATA) |
|
3998 return(NULL); |
|
3999 |
|
4000 ret = xmlStrdup(value); |
|
4001 if (ret == NULL) |
|
4002 return(NULL); |
|
4003 src = value; |
|
4004 dst = ret; |
|
4005 while (*src == 0x20) src++; |
|
4006 while (*src != 0) { |
|
4007 if (*src == 0x20) { |
|
4008 while (*src == 0x20) src++; |
|
4009 if (*src != 0) |
|
4010 *dst++ = 0x20; |
|
4011 } else { |
|
4012 *dst++ = *src++; |
|
4013 } |
|
4014 } |
|
4015 *dst = 0; |
|
4016 return(ret); |
|
4017 } |
|
4018 |
|
4019 static void |
|
4020 xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count, |
|
4021 const xmlChar* name ATTRIBUTE_UNUSED) { |
|
4022 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++; |
|
4023 } |
|
4024 |
|
4025 /** |
|
4026 * xmlValidateAttributeDecl: |
|
4027 * @param ctxt the validation context |
|
4028 * @param doc a document instance |
|
4029 * @param attr an attribute definition |
|
4030 * |
|
4031 * Try to validate a single attribute definition |
|
4032 * basically it does the following checks as described by the |
|
4033 * XML-1.0 recommendation: |
|
4034 * - [ VC: Attribute Default Legal ] |
|
4035 * - [ VC: Enumeration ] |
|
4036 * - [ VC: ID Attribute Default ] |
|
4037 * |
|
4038 * The ID/IDREF uniqueness and matching are done separately |
|
4039 * |
|
4040 * returns 1 if valid or 0 otherwise |
|
4041 */ |
|
4042 |
|
4043 int |
|
4044 xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
4045 xmlAttributePtr attr) { |
|
4046 int ret = 1; |
|
4047 int val; |
|
4048 CHECK_DTD; |
|
4049 if(attr == NULL) return(1); |
|
4050 |
|
4051 /* Attribute Default Legal */ |
|
4052 /* Enumeration */ |
|
4053 if (attr->defaultValue != NULL) { |
|
4054 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue); |
|
4055 if (val == 0) { |
|
4056 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT, |
|
4057 EMBED_ERRTXT("Syntax of default value for attribute %s of %s is not valid\n"), |
|
4058 attr->name, attr->elem, NULL); |
|
4059 } |
|
4060 ret &= val; |
|
4061 } |
|
4062 |
|
4063 /* ID Attribute Default */ |
|
4064 if ((attr->atype == XML_ATTRIBUTE_ID)&& |
|
4065 (attr->def != XML_ATTRIBUTE_IMPLIED) && |
|
4066 (attr->def != XML_ATTRIBUTE_REQUIRED)) { |
|
4067 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED, |
|
4068 EMBED_ERRTXT("ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n"), |
|
4069 attr->name, attr->elem, NULL); |
|
4070 ret = 0; |
|
4071 } |
|
4072 |
|
4073 /* One ID per Element Type */ |
|
4074 if (attr->atype == XML_ATTRIBUTE_ID) { |
|
4075 int nbId; |
|
4076 |
|
4077 /* the trick is that we parse DtD as their own internal subset */ |
|
4078 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset, |
|
4079 attr->elem); |
|
4080 if (elem != NULL) { |
|
4081 nbId = xmlScanIDAttributeDecl(NULL, elem); |
|
4082 } else { |
|
4083 xmlAttributeTablePtr table; |
|
4084 |
|
4085 /* |
|
4086 * The attribute may be declared in the internal subset and the |
|
4087 * element in the external subset. |
|
4088 */ |
|
4089 nbId = 0; |
|
4090 table = (xmlAttributeTablePtr) doc->intSubset->attributes; |
|
4091 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner) |
|
4092 xmlValidateAttributeIdCallback, &nbId); |
|
4093 } |
|
4094 if (nbId > 1) { |
|
4095 |
|
4096 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, |
|
4097 EMBED_ERRTXT("Element %s has %d ID attribute defined in the internal subset : %s\n"), |
|
4098 attr->elem, nbId, attr->name); |
|
4099 } else if (doc->extSubset != NULL) { |
|
4100 int extId = 0; |
|
4101 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem); |
|
4102 if (elem != NULL) { |
|
4103 extId = xmlScanIDAttributeDecl(NULL, elem); |
|
4104 } |
|
4105 if (extId > 1) { |
|
4106 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, |
|
4107 EMBED_ERRTXT("Element %s has %d ID attribute defined in the external subset : %s\n"), |
|
4108 attr->elem, extId, attr->name); |
|
4109 } else if (extId + nbId > 1) { |
|
4110 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, |
|
4111 EMBED_ERRTXT("Element %s has ID attributes defined in the internal and external subset : %s\n"), |
|
4112 attr->elem, attr->name, NULL); |
|
4113 } |
|
4114 } |
|
4115 } |
|
4116 |
|
4117 /* Validity Constraint: Enumeration */ |
|
4118 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) { |
|
4119 xmlEnumerationPtr tree = attr->tree; |
|
4120 while (tree != NULL) { |
|
4121 if (xmlStrEqual(tree->name, attr->defaultValue)) break; |
|
4122 tree = tree->next; |
|
4123 } |
|
4124 if (tree == NULL) { |
|
4125 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE, |
|
4126 EMBED_ERRTXT("Default value \"%s\" for attribute %s of %s is not among the enumerated set\n"), |
|
4127 attr->defaultValue, attr->name, attr->elem); |
|
4128 ret = 0; |
|
4129 } |
|
4130 } |
|
4131 |
|
4132 return(ret); |
|
4133 } |
|
4134 |
|
4135 /** |
|
4136 * xmlValidateElementDecl: |
|
4137 * @param ctxt the validation context |
|
4138 * @param doc a document instance |
|
4139 * @param elem an element definition |
|
4140 * |
|
4141 * Try to validate a single element definition |
|
4142 * basically it does the following checks as described by the |
|
4143 * XML-1.0 recommendation: |
|
4144 * - [ VC: One ID per Element Type ] |
|
4145 * - [ VC: No Duplicate Types ] |
|
4146 * - [ VC: Unique Element Type Declaration ] |
|
4147 * |
|
4148 * returns 1 if valid or 0 otherwise |
|
4149 */ |
|
4150 |
|
4151 int |
|
4152 xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
4153 xmlElementPtr elem) { |
|
4154 int ret = 1; |
|
4155 xmlElementPtr tst; |
|
4156 |
|
4157 CHECK_DTD; |
|
4158 |
|
4159 if (elem == NULL) return(1); |
|
4160 |
|
4161 #if 0 |
|
4162 #ifdef LIBXML_REGEXP_ENABLED |
|
4163 /* Build the regexp associated to the content model */ |
|
4164 ret = xmlValidBuildContentModel(ctxt, elem); |
|
4165 #endif |
|
4166 #endif |
|
4167 |
|
4168 /* No Duplicate Types */ |
|
4169 if (elem->etype == XML_ELEMENT_TYPE_MIXED) { |
|
4170 xmlElementContentPtr cur, next; |
|
4171 const xmlChar *name; |
|
4172 |
|
4173 cur = elem->content; |
|
4174 while (cur != NULL) { |
|
4175 if (cur->type != XML_ELEMENT_CONTENT_OR) break; |
|
4176 if (cur->c1 == NULL) break; |
|
4177 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) { |
|
4178 name = cur->c1->name; |
|
4179 next = cur->c2; |
|
4180 while (next != NULL) { |
|
4181 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) { |
|
4182 if ((xmlStrEqual(next->name, name)) && |
|
4183 (xmlStrEqual(next->prefix, cur->prefix))) { |
|
4184 if (cur->prefix == NULL) { |
|
4185 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, |
|
4186 EMBED_ERRTXT("Definition of %s has duplicate references of %s\n"), |
|
4187 elem->name, name, NULL); |
|
4188 } else { |
|
4189 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, |
|
4190 EMBED_ERRTXT("Definition of %s has duplicate references of %s:%s\n"), |
|
4191 elem->name, cur->prefix, name); |
|
4192 } |
|
4193 ret = 0; |
|
4194 } |
|
4195 break; |
|
4196 } |
|
4197 if (next->c1 == NULL) break; |
|
4198 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break; |
|
4199 if ((xmlStrEqual(next->c1->name, name)) && |
|
4200 (xmlStrEqual(next->c1->prefix, cur->prefix))) { |
|
4201 if (cur->prefix == NULL) { |
|
4202 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, |
|
4203 EMBED_ERRTXT("Definition of %s has duplicate references to %s\n"), |
|
4204 elem->name, name, NULL); |
|
4205 } else { |
|
4206 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, |
|
4207 EMBED_ERRTXT("Definition of %s has duplicate references to %s:%s\n"), |
|
4208 elem->name, cur->prefix, name); |
|
4209 } |
|
4210 ret = 0; |
|
4211 } |
|
4212 next = next->c2; |
|
4213 } |
|
4214 } |
|
4215 cur = cur->c2; |
|
4216 } |
|
4217 } |
|
4218 |
|
4219 /* VC: Unique Element Type Declaration */ |
|
4220 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name); |
|
4221 if ((tst != NULL ) && (tst != elem) && |
|
4222 ((tst->prefix == elem->prefix) || |
|
4223 (xmlStrEqual(tst->prefix, elem->prefix))) && |
|
4224 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { |
|
4225 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, |
|
4226 EMBED_ERRTXT("Redefinition of element %s\n"), |
|
4227 elem->name, NULL, NULL); |
|
4228 ret = 0; |
|
4229 } |
|
4230 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name); |
|
4231 if ((tst != NULL ) && (tst != elem) && |
|
4232 ((tst->prefix == elem->prefix) || |
|
4233 (xmlStrEqual(tst->prefix, elem->prefix))) && |
|
4234 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { |
|
4235 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, |
|
4236 EMBED_ERRTXT("Redefinition of element %s\n"), |
|
4237 elem->name, NULL, NULL); |
|
4238 ret = 0; |
|
4239 } |
|
4240 /* One ID per Element Type |
|
4241 * already done when registering the attribute |
|
4242 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) { |
|
4243 ret = 0; |
|
4244 } */ |
|
4245 return(ret); |
|
4246 } |
|
4247 |
|
4248 /** |
|
4249 * xmlValidateOneAttribute: |
|
4250 * @param ctxt the validation context |
|
4251 * @param doc a document instance |
|
4252 * @param elem an element instance |
|
4253 * @param attr an attribute instance |
|
4254 * @param value the attribute value (without entities processing) |
|
4255 * |
|
4256 * Try to validate a single attribute for an element |
|
4257 * basically it does the following checks as described by the |
|
4258 * XML-1.0 recommendation: |
|
4259 * - [ VC: Attribute Value Type ] |
|
4260 * - [ VC: Fixed Attribute Default ] |
|
4261 * - [ VC: Entity Name ] |
|
4262 * - [ VC: Name Token ] |
|
4263 * - [ VC: ID ] |
|
4264 * - [ VC: IDREF ] |
|
4265 * - [ VC: Entity Name ] |
|
4266 * - [ VC: Notation Attributes ] |
|
4267 * |
|
4268 * The ID/IDREF uniqueness and matching are done separately |
|
4269 * |
|
4270 * returns 1 if valid or 0 otherwise |
|
4271 */ |
|
4272 |
|
4273 int |
|
4274 xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
4275 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) |
|
4276 { |
|
4277 xmlAttributePtr attrDecl = NULL; |
|
4278 int val; |
|
4279 int ret = 1; |
|
4280 |
|
4281 CHECK_DTD; |
|
4282 if ((elem == NULL) || (elem->name == NULL)) return(0); |
|
4283 if ((attr == NULL) || (attr->name == NULL)) return(0); |
|
4284 |
|
4285 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { |
|
4286 xmlChar fn[50]; |
|
4287 xmlChar *fullname; |
|
4288 |
|
4289 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); |
|
4290 if (fullname == NULL) |
|
4291 return(0); |
|
4292 if (attr->ns != NULL) { |
|
4293 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, |
|
4294 attr->name, attr->ns->prefix); |
|
4295 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4296 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, |
|
4297 attr->name, attr->ns->prefix); |
|
4298 } else { |
|
4299 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name); |
|
4300 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4301 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, |
|
4302 fullname, attr->name); |
|
4303 } |
|
4304 if ((fullname != fn) && (fullname != elem->name)) |
|
4305 xmlFree(fullname); |
|
4306 } |
|
4307 if (attrDecl == NULL) { |
|
4308 if (attr->ns != NULL) { |
|
4309 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, |
|
4310 attr->name, attr->ns->prefix); |
|
4311 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4312 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, |
|
4313 attr->name, attr->ns->prefix); |
|
4314 } else { |
|
4315 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, |
|
4316 elem->name, attr->name); |
|
4317 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4318 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, |
|
4319 elem->name, attr->name); |
|
4320 } |
|
4321 } |
|
4322 |
|
4323 |
|
4324 /* Validity Constraint: Attribute Value Type */ |
|
4325 if (attrDecl == NULL) { |
|
4326 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, |
|
4327 EMBED_ERRTXT("No declaration for attribute %s of element %s\n"), |
|
4328 attr->name, elem->name, NULL); |
|
4329 return(0); |
|
4330 } |
|
4331 attr->atype = attrDecl->atype; |
|
4332 |
|
4333 val = xmlValidateAttributeValue(attrDecl->atype, value); |
|
4334 if (val == 0) { |
|
4335 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, |
|
4336 EMBED_ERRTXT("Syntax of value for attribute %s of %s is not valid\n"), |
|
4337 attr->name, elem->name, NULL); |
|
4338 ret = 0; |
|
4339 } |
|
4340 |
|
4341 /* Validity constraint: Fixed Attribute Default */ |
|
4342 if (attrDecl->def == XML_ATTRIBUTE_FIXED) { |
|
4343 if (!xmlStrEqual(value, attrDecl->defaultValue)) { |
|
4344 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, |
|
4345 EMBED_ERRTXT("Value for attribute %s of %s is different from default \"%s\"\n"), |
|
4346 attr->name, elem->name, attrDecl->defaultValue); |
|
4347 ret = 0; |
|
4348 } |
|
4349 } |
|
4350 |
|
4351 /* Validity Constraint: ID uniqueness */ |
|
4352 if (attrDecl->atype == XML_ATTRIBUTE_ID) { |
|
4353 if (xmlAddID(ctxt, doc, value, attr) == NULL) |
|
4354 ret = 0; |
|
4355 } |
|
4356 |
|
4357 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) || |
|
4358 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) { |
|
4359 if (xmlAddRef(ctxt, doc, value, attr) == NULL) |
|
4360 ret = 0; |
|
4361 } |
|
4362 |
|
4363 /* Validity Constraint: Notation Attributes */ |
|
4364 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) { |
|
4365 xmlEnumerationPtr tree = attrDecl->tree; |
|
4366 xmlNotationPtr nota; |
|
4367 |
|
4368 /* First check that the given NOTATION was declared */ |
|
4369 nota = xmlGetDtdNotationDesc(doc->intSubset, value); |
|
4370 if (nota == NULL) |
|
4371 nota = xmlGetDtdNotationDesc(doc->extSubset, value); |
|
4372 |
|
4373 if (nota == NULL) { |
|
4374 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, |
|
4375 EMBED_ERRTXT("Value \"%s\" for attribute %s of %s is not a declared Notation\n"), |
|
4376 value, attr->name, elem->name); |
|
4377 ret = 0; |
|
4378 } |
|
4379 |
|
4380 /* Second, verify that it's among the list */ |
|
4381 while (tree != NULL) { |
|
4382 if (xmlStrEqual(tree->name, value)) break; |
|
4383 tree = tree->next; |
|
4384 } |
|
4385 if (tree == NULL) { |
|
4386 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, |
|
4387 EMBED_ERRTXT("Value \"%s\" for attribute %s of %s is not among the enumerated notations\n"), |
|
4388 value, attr->name, elem->name); |
|
4389 ret = 0; |
|
4390 } |
|
4391 } |
|
4392 |
|
4393 /* Validity Constraint: Enumeration */ |
|
4394 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) { |
|
4395 xmlEnumerationPtr tree = attrDecl->tree; |
|
4396 while (tree != NULL) { |
|
4397 if (xmlStrEqual(tree->name, value)) break; |
|
4398 tree = tree->next; |
|
4399 } |
|
4400 if (tree == NULL) { |
|
4401 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, |
|
4402 EMBED_ERRTXT("Value \"%s\" for attribute %s of %s is not among the enumerated set\n"), |
|
4403 value, attr->name, elem->name); |
|
4404 ret = 0; |
|
4405 } |
|
4406 } |
|
4407 |
|
4408 /* Fixed Attribute Default */ |
|
4409 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) && |
|
4410 (!xmlStrEqual(attrDecl->defaultValue, value))) { |
|
4411 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, |
|
4412 EMBED_ERRTXT("Value for attribute %s of %s must be \"%s\"\n"), |
|
4413 attr->name, elem->name, attrDecl->defaultValue); |
|
4414 ret = 0; |
|
4415 } |
|
4416 |
|
4417 /* Extra check for the attribute value */ |
|
4418 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name, |
|
4419 attrDecl->atype, value); |
|
4420 |
|
4421 return(ret); |
|
4422 } |
|
4423 |
|
4424 /** |
|
4425 * xmlValidateOneNamespace: |
|
4426 * @param ctxt the validation context |
|
4427 * @param doc a document instance |
|
4428 * @param elem an element instance |
|
4429 * @param prefix the namespace prefix |
|
4430 * @param ns an namespace declaration instance |
|
4431 * @param value the attribute value (without entities processing) |
|
4432 * |
|
4433 * Try to validate a single namespace declaration for an element |
|
4434 * basically it does the following checks as described by the |
|
4435 * XML-1.0 recommendation: |
|
4436 * - [ VC: Attribute Value Type ] |
|
4437 * - [ VC: Fixed Attribute Default ] |
|
4438 * - [ VC: Entity Name ] |
|
4439 * - [ VC: Name Token ] |
|
4440 * - [ VC: ID ] |
|
4441 * - [ VC: IDREF ] |
|
4442 * - [ VC: Entity Name ] |
|
4443 * - [ VC: Notation Attributes ] |
|
4444 * |
|
4445 * The ID/IDREF uniqueness and matching are done separately |
|
4446 * |
|
4447 * returns 1 if valid or 0 otherwise |
|
4448 */ |
|
4449 |
|
4450 int |
|
4451 xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
4452 xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) { |
|
4453 /* xmlElementPtr elemDecl; */ |
|
4454 xmlAttributePtr attrDecl = NULL; |
|
4455 int val; |
|
4456 int ret = 1; |
|
4457 |
|
4458 CHECK_DTD; |
|
4459 if ((elem == NULL) || (elem->name == NULL)) return(0); |
|
4460 if ((ns == NULL) || (ns->href == NULL)) return(0); |
|
4461 |
|
4462 if (prefix != NULL) { |
|
4463 xmlChar fn[50]; |
|
4464 xmlChar *fullname; |
|
4465 |
|
4466 fullname = xmlBuildQName(elem->name, prefix, fn, 50); |
|
4467 if (fullname == NULL) { |
|
4468 xmlVErrMemory(ctxt, EMBED_ERRTXT("Validating namespace")); |
|
4469 return(0); |
|
4470 } |
|
4471 if (ns->prefix != NULL) { |
|
4472 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, |
|
4473 ns->prefix, BAD_CAST "xmlns"); |
|
4474 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4475 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, |
|
4476 ns->prefix, BAD_CAST "xmlns"); |
|
4477 } else { |
|
4478 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, |
|
4479 BAD_CAST "xmlns"); |
|
4480 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4481 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, |
|
4482 BAD_CAST "xmlns"); |
|
4483 } |
|
4484 if ((fullname != fn) && (fullname != elem->name)) |
|
4485 xmlFree(fullname); |
|
4486 } |
|
4487 if (attrDecl == NULL) { |
|
4488 if (ns->prefix != NULL) { |
|
4489 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, |
|
4490 ns->prefix, BAD_CAST "xmlns"); |
|
4491 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4492 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, |
|
4493 ns->prefix, BAD_CAST "xmlns"); |
|
4494 } else { |
|
4495 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, |
|
4496 elem->name, BAD_CAST "xmlns"); |
|
4497 if ((attrDecl == NULL) && (doc->extSubset != NULL)) |
|
4498 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, |
|
4499 elem->name, BAD_CAST "xmlns"); |
|
4500 } |
|
4501 } |
|
4502 |
|
4503 |
|
4504 /* Validity Constraint: Attribute Value Type */ |
|
4505 if (attrDecl == NULL) { |
|
4506 if (ns->prefix != NULL) { |
|
4507 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, |
|
4508 EMBED_ERRTXT("No declaration for attribute xmlns:%s of element %s\n"), |
|
4509 ns->prefix, elem->name, NULL); |
|
4510 } else { |
|
4511 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, |
|
4512 EMBED_ERRTXT("No declaration for attribute xmlns of element %s\n"), |
|
4513 elem->name, NULL, NULL); |
|
4514 } |
|
4515 return(0); |
|
4516 } |
|
4517 |
|
4518 val = xmlValidateAttributeValue(attrDecl->atype, value); |
|
4519 if (val == 0) { |
|
4520 if (ns->prefix != NULL) { |
|
4521 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT, |
|
4522 EMBED_ERRTXT("Syntax of value for attribute xmlns:%s of %s is not valid\n"), |
|
4523 ns->prefix, elem->name, NULL); |
|
4524 } else { |
|
4525 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT, |
|
4526 EMBED_ERRTXT("Syntax of value for attribute xmlns of %s is not valid\n"), |
|
4527 elem->name, NULL, NULL); |
|
4528 } |
|
4529 ret = 0; |
|
4530 } |
|
4531 |
|
4532 /* Validity constraint: Fixed Attribute Default */ |
|
4533 if (attrDecl->def == XML_ATTRIBUTE_FIXED) { |
|
4534 if (!xmlStrEqual(value, attrDecl->defaultValue)) { |
|
4535 if (ns->prefix != NULL) { |
|
4536 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, |
|
4537 EMBED_ERRTXT("Value for attribute xmlns:%s of %s is different from default \"%s\"\n"), |
|
4538 ns->prefix, elem->name, attrDecl->defaultValue); |
|
4539 } else { |
|
4540 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, |
|
4541 EMBED_ERRTXT("Value for attribute xmlns of %s is different from default \"%s\"\n"), |
|
4542 elem->name, attrDecl->defaultValue, NULL); |
|
4543 } |
|
4544 ret = 0; |
|
4545 } |
|
4546 } |
|
4547 |
|
4548 /* Validity Constraint: ID uniqueness */ |
|
4549 if (attrDecl->atype == XML_ATTRIBUTE_ID) { |
|
4550 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL) |
|
4551 ret = 0; |
|
4552 } |
|
4553 |
|
4554 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) || |
|
4555 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) { |
|
4556 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL) |
|
4557 ret = 0; |
|
4558 } |
|
4559 |
|
4560 /* Validity Constraint: Notation Attributes */ |
|
4561 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) { |
|
4562 xmlEnumerationPtr tree = attrDecl->tree; |
|
4563 xmlNotationPtr nota; |
|
4564 |
|
4565 /* First check that the given NOTATION was declared */ |
|
4566 nota = xmlGetDtdNotationDesc(doc->intSubset, value); |
|
4567 if (nota == NULL) |
|
4568 nota = xmlGetDtdNotationDesc(doc->extSubset, value); |
|
4569 |
|
4570 if (nota == NULL) { |
|
4571 if (ns->prefix != NULL) { |
|
4572 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, |
|
4573 EMBED_ERRTXT("Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n"), |
|
4574 value, ns->prefix, elem->name); |
|
4575 } else { |
|
4576 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, |
|
4577 EMBED_ERRTXT("Value \"%s\" for attribute xmlns of %s is not a declared Notation\n"), |
|
4578 value, elem->name, NULL); |
|
4579 } |
|
4580 ret = 0; |
|
4581 } |
|
4582 |
|
4583 /* Second, verify that it's among the list */ |
|
4584 while (tree != NULL) { |
|
4585 if (xmlStrEqual(tree->name, value)) break; |
|
4586 tree = tree->next; |
|
4587 } |
|
4588 if (tree == NULL) { |
|
4589 if (ns->prefix != NULL) { |
|
4590 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, |
|
4591 EMBED_ERRTXT("Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n"), |
|
4592 value, ns->prefix, elem->name); |
|
4593 } else { |
|
4594 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, |
|
4595 EMBED_ERRTXT("Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n"), |
|
4596 value, elem->name, NULL); |
|
4597 } |
|
4598 ret = 0; |
|
4599 } |
|
4600 } |
|
4601 |
|
4602 /* Validity Constraint: Enumeration */ |
|
4603 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) { |
|
4604 xmlEnumerationPtr tree = attrDecl->tree; |
|
4605 while (tree != NULL) { |
|
4606 if (xmlStrEqual(tree->name, value)) break; |
|
4607 tree = tree->next; |
|
4608 } |
|
4609 if (tree == NULL) { |
|
4610 if (ns->prefix != NULL) { |
|
4611 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, |
|
4612 EMBED_ERRTXT("Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n"), |
|
4613 value, ns->prefix, elem->name); |
|
4614 } else { |
|
4615 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, |
|
4616 EMBED_ERRTXT("Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n"), |
|
4617 value, elem->name, NULL); |
|
4618 } |
|
4619 ret = 0; |
|
4620 } |
|
4621 } |
|
4622 |
|
4623 /* Fixed Attribute Default */ |
|
4624 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) && |
|
4625 (!xmlStrEqual(attrDecl->defaultValue, value))) { |
|
4626 if (ns->prefix != NULL) { |
|
4627 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, |
|
4628 EMBED_ERRTXT("Value for attribute xmlns:%s of %s must be \"%s\"\n"), |
|
4629 ns->prefix, elem->name, attrDecl->defaultValue); |
|
4630 } else { |
|
4631 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, |
|
4632 EMBED_ERRTXT("Value for attribute xmlns of %s must be \"%s\"\n"), |
|
4633 elem->name, attrDecl->defaultValue, NULL); |
|
4634 } |
|
4635 ret = 0; |
|
4636 } |
|
4637 |
|
4638 /* Extra check for the attribute value */ |
|
4639 if (ns->prefix != NULL) { |
|
4640 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix, |
|
4641 attrDecl->atype, value); |
|
4642 } else { |
|
4643 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns", |
|
4644 attrDecl->atype, value); |
|
4645 } |
|
4646 |
|
4647 return(ret); |
|
4648 } |
|
4649 |
|
4650 #ifndef LIBXML_REGEXP_ENABLED |
|
4651 /** |
|
4652 * xmlValidateSkipIgnorable: |
|
4653 * @param ctxt the validation context |
|
4654 * @param child the child list |
|
4655 * |
|
4656 * Skip ignorable elements w.r.t. the validation process |
|
4657 * |
|
4658 * returns the first element to consider for validation of the content model |
|
4659 */ |
|
4660 |
|
4661 static xmlNodePtr |
|
4662 xmlValidateSkipIgnorable(xmlNodePtr child) { |
|
4663 while (child != NULL) { |
|
4664 switch (child->type) { |
|
4665 /* These things are ignored (skipped) during validation. */ |
|
4666 case XML_PI_NODE: |
|
4667 case XML_COMMENT_NODE: |
|
4668 case XML_XINCLUDE_START: |
|
4669 case XML_XINCLUDE_END: |
|
4670 child = child->next; |
|
4671 break; |
|
4672 case XML_TEXT_NODE: |
|
4673 if (xmlIsBlankNode(child)) |
|
4674 child = child->next; |
|
4675 else |
|
4676 return(child); |
|
4677 break; |
|
4678 /* keep current node */ |
|
4679 default: |
|
4680 return(child); |
|
4681 } |
|
4682 } |
|
4683 return(child); |
|
4684 } |
|
4685 |
|
4686 /** |
|
4687 * xmlValidateElementType: |
|
4688 * @param ctxt the validation context |
|
4689 * |
|
4690 * Try to validate the content model of an element internal function |
|
4691 * |
|
4692 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity |
|
4693 * reference is found and -3 if the validation succeeded but |
|
4694 * the content model is not determinist. |
|
4695 */ |
|
4696 |
|
4697 static int |
|
4698 xmlValidateElementType(xmlValidCtxtPtr ctxt) { |
|
4699 int ret = -1; |
|
4700 int determinist = 1; |
|
4701 |
|
4702 NODE = xmlValidateSkipIgnorable(NODE); |
|
4703 if ((NODE == NULL) && (CONT == NULL)) |
|
4704 return(1); |
|
4705 if ((NODE == NULL) && |
|
4706 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) || |
|
4707 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) { |
|
4708 return(1); |
|
4709 } |
|
4710 if (CONT == NULL) return(-1); |
|
4711 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE)) |
|
4712 return(-2); |
|
4713 |
|
4714 /* |
|
4715 * We arrive here when more states need to be examined |
|
4716 */ |
|
4717 cont: |
|
4718 |
|
4719 /* |
|
4720 * We just recovered from a rollback generated by a possible |
|
4721 * epsilon transition, go directly to the analysis phase |
|
4722 */ |
|
4723 if (STATE == ROLLBACK_PARENT) { |
|
4724 |
|
4725 DEBUG_VALID_MSG("restored parent branch"); |
|
4726 DEBUG_VALID_STATE(NODE, CONT) |
|
4727 ret = 1; |
|
4728 goto analyze; |
|
4729 } |
|
4730 |
|
4731 DEBUG_VALID_STATE(NODE, CONT) |
|
4732 /* |
|
4733 * we may have to save a backup state here. This is the equivalent |
|
4734 * of handling epsilon transition in NFAs. |
|
4735 */ |
|
4736 if ((CONT != NULL) && |
|
4737 ((CONT->parent == NULL) || |
|
4738 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) && |
|
4739 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) || |
|
4740 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) || |
|
4741 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) { |
|
4742 DEBUG_VALID_MSG("saving parent branch"); |
|
4743 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0) |
|
4744 return(0); |
|
4745 } |
|
4746 |
|
4747 |
|
4748 /* |
|
4749 * Check first if the content matches |
|
4750 */ |
|
4751 switch (CONT->type) { |
|
4752 case XML_ELEMENT_CONTENT_PCDATA: |
|
4753 if (NODE == NULL) { |
|
4754 DEBUG_VALID_MSG("pcdata failed no node"); |
|
4755 ret = 0; |
|
4756 break; |
|
4757 } |
|
4758 if (NODE->type == XML_TEXT_NODE) { |
|
4759 DEBUG_VALID_MSG("pcdata found, skip to next"); |
|
4760 /* |
|
4761 * go to next element in the content model |
|
4762 * skipping ignorable elems |
|
4763 */ |
|
4764 do { |
|
4765 NODE = NODE->next; |
|
4766 NODE = xmlValidateSkipIgnorable(NODE); |
|
4767 if ((NODE != NULL) && |
|
4768 (NODE->type == XML_ENTITY_REF_NODE)) |
|
4769 return(-2); |
|
4770 } while ((NODE != NULL) && |
|
4771 ((NODE->type != XML_ELEMENT_NODE) && |
|
4772 (NODE->type != XML_TEXT_NODE) && |
|
4773 (NODE->type != XML_CDATA_SECTION_NODE))); |
|
4774 ret = 1; |
|
4775 break; |
|
4776 } else { |
|
4777 DEBUG_VALID_MSG("pcdata failed"); |
|
4778 ret = 0; |
|
4779 break; |
|
4780 } |
|
4781 break; |
|
4782 case XML_ELEMENT_CONTENT_ELEMENT: |
|
4783 if (NODE == NULL) { |
|
4784 DEBUG_VALID_MSG("element failed no node"); |
|
4785 ret = 0; |
|
4786 break; |
|
4787 } |
|
4788 ret = ((NODE->type == XML_ELEMENT_NODE) && |
|
4789 (xmlStrEqual(NODE->name, CONT->name))); |
|
4790 if (ret == 1) { |
|
4791 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { |
|
4792 ret = (CONT->prefix == NULL); |
|
4793 } else if (CONT->prefix == NULL) { |
|
4794 ret = 0; |
|
4795 } else { |
|
4796 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix); |
|
4797 } |
|
4798 } |
|
4799 if (ret == 1) { |
|
4800 DEBUG_VALID_MSG("element found, skip to next"); |
|
4801 /* |
|
4802 * go to next element in the content model |
|
4803 * skipping ignorable elems |
|
4804 */ |
|
4805 do { |
|
4806 NODE = NODE->next; |
|
4807 NODE = xmlValidateSkipIgnorable(NODE); |
|
4808 if ((NODE != NULL) && |
|
4809 (NODE->type == XML_ENTITY_REF_NODE)) |
|
4810 return(-2); |
|
4811 } while ((NODE != NULL) && |
|
4812 ((NODE->type != XML_ELEMENT_NODE) && |
|
4813 (NODE->type != XML_TEXT_NODE) && |
|
4814 (NODE->type != XML_CDATA_SECTION_NODE))); |
|
4815 } else { |
|
4816 DEBUG_VALID_MSG("element failed"); |
|
4817 ret = 0; |
|
4818 break; |
|
4819 } |
|
4820 break; |
|
4821 case XML_ELEMENT_CONTENT_OR: |
|
4822 /* |
|
4823 * Small optimization. |
|
4824 */ |
|
4825 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) { |
|
4826 if ((NODE == NULL) || |
|
4827 (!xmlStrEqual(NODE->name, CONT->c1->name))) { |
|
4828 DEPTH++; |
|
4829 CONT = CONT->c2; |
|
4830 goto cont; |
|
4831 } |
|
4832 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { |
|
4833 ret = (CONT->c1->prefix == NULL); |
|
4834 } else if (CONT->c1->prefix == NULL) { |
|
4835 ret = 0; |
|
4836 } else { |
|
4837 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix); |
|
4838 } |
|
4839 if (ret == 0) { |
|
4840 DEPTH++; |
|
4841 CONT = CONT->c2; |
|
4842 goto cont; |
|
4843 } |
|
4844 } |
|
4845 |
|
4846 /* |
|
4847 * save the second branch 'or' branch |
|
4848 */ |
|
4849 DEBUG_VALID_MSG("saving 'or' branch"); |
|
4850 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1), |
|
4851 OCCURS, ROLLBACK_OR) < 0) |
|
4852 return(-1); |
|
4853 DEPTH++; |
|
4854 CONT = CONT->c1; |
|
4855 goto cont; |
|
4856 case XML_ELEMENT_CONTENT_SEQ: |
|
4857 /* |
|
4858 * Small optimization. |
|
4859 */ |
|
4860 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) && |
|
4861 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) || |
|
4862 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) { |
|
4863 if ((NODE == NULL) || |
|
4864 (!xmlStrEqual(NODE->name, CONT->c1->name))) { |
|
4865 DEPTH++; |
|
4866 CONT = CONT->c2; |
|
4867 goto cont; |
|
4868 } |
|
4869 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { |
|
4870 ret = (CONT->c1->prefix == NULL); |
|
4871 } else if (CONT->c1->prefix == NULL) { |
|
4872 ret = 0; |
|
4873 } else { |
|
4874 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix); |
|
4875 } |
|
4876 if (ret == 0) { |
|
4877 DEPTH++; |
|
4878 CONT = CONT->c2; |
|
4879 goto cont; |
|
4880 } |
|
4881 } |
|
4882 DEPTH++; |
|
4883 CONT = CONT->c1; |
|
4884 goto cont; |
|
4885 } |
|
4886 |
|
4887 /* |
|
4888 * At this point handle going up in the tree |
|
4889 */ |
|
4890 if (ret == -1) { |
|
4891 DEBUG_VALID_MSG("error found returning"); |
|
4892 return(ret); |
|
4893 } |
|
4894 analyze: |
|
4895 while (CONT != NULL) { |
|
4896 /* |
|
4897 * First do the analysis depending on the occurrence model at |
|
4898 * this level. |
|
4899 */ |
|
4900 if (ret == 0) { |
|
4901 switch (CONT->ocur) { |
|
4902 xmlNodePtr cur; |
|
4903 |
|
4904 case XML_ELEMENT_CONTENT_ONCE: |
|
4905 cur = ctxt->vstate->node; |
|
4906 DEBUG_VALID_MSG("Once branch failed, rollback"); |
|
4907 if (vstateVPop(ctxt) < 0 ) { |
|
4908 DEBUG_VALID_MSG("exhaustion, failed"); |
|
4909 return(0); |
|
4910 } |
|
4911 if (cur != ctxt->vstate->node) |
|
4912 determinist = -3; |
|
4913 goto cont; |
|
4914 case XML_ELEMENT_CONTENT_PLUS: |
|
4915 if (OCCURRENCE == 0) { |
|
4916 cur = ctxt->vstate->node; |
|
4917 DEBUG_VALID_MSG("Plus branch failed, rollback"); |
|
4918 if (vstateVPop(ctxt) < 0 ) { |
|
4919 DEBUG_VALID_MSG("exhaustion, failed"); |
|
4920 return(0); |
|
4921 } |
|
4922 if (cur != ctxt->vstate->node) |
|
4923 determinist = -3; |
|
4924 goto cont; |
|
4925 } |
|
4926 DEBUG_VALID_MSG("Plus branch found"); |
|
4927 ret = 1; |
|
4928 break; |
|
4929 case XML_ELEMENT_CONTENT_MULT: |
|
4930 #ifdef DEBUG_VALID_ALGO |
|
4931 if (OCCURRENCE == 0) { |
|
4932 DEBUG_VALID_MSG("Mult branch failed"); |
|
4933 } else { |
|
4934 DEBUG_VALID_MSG("Mult branch found"); |
|
4935 } |
|
4936 #endif |
|
4937 ret = 1; |
|
4938 break; |
|
4939 case XML_ELEMENT_CONTENT_OPT: |
|
4940 DEBUG_VALID_MSG("Option branch failed"); |
|
4941 ret = 1; |
|
4942 break; |
|
4943 } |
|
4944 } else { |
|
4945 switch (CONT->ocur) { |
|
4946 case XML_ELEMENT_CONTENT_OPT: |
|
4947 DEBUG_VALID_MSG("Option branch succeeded"); |
|
4948 ret = 1; |
|
4949 break; |
|
4950 case XML_ELEMENT_CONTENT_ONCE: |
|
4951 DEBUG_VALID_MSG("Once branch succeeded"); |
|
4952 ret = 1; |
|
4953 break; |
|
4954 case XML_ELEMENT_CONTENT_PLUS: |
|
4955 if (STATE == ROLLBACK_PARENT) { |
|
4956 DEBUG_VALID_MSG("Plus branch rollback"); |
|
4957 ret = 1; |
|
4958 break; |
|
4959 } |
|
4960 if (NODE == NULL) { |
|
4961 DEBUG_VALID_MSG("Plus branch exhausted"); |
|
4962 ret = 1; |
|
4963 break; |
|
4964 } |
|
4965 DEBUG_VALID_MSG("Plus branch succeeded, continuing"); |
|
4966 SET_OCCURRENCE; |
|
4967 goto cont; |
|
4968 case XML_ELEMENT_CONTENT_MULT: |
|
4969 if (STATE == ROLLBACK_PARENT) { |
|
4970 DEBUG_VALID_MSG("Mult branch rollback"); |
|
4971 ret = 1; |
|
4972 break; |
|
4973 } |
|
4974 if (NODE == NULL) { |
|
4975 DEBUG_VALID_MSG("Mult branch exhausted"); |
|
4976 ret = 1; |
|
4977 break; |
|
4978 } |
|
4979 DEBUG_VALID_MSG("Mult branch succeeded, continuing"); |
|
4980 /* SET_OCCURRENCE; */ |
|
4981 goto cont; |
|
4982 } |
|
4983 } |
|
4984 STATE = 0; |
|
4985 |
|
4986 /* |
|
4987 * Then act accordingly at the parent level |
|
4988 */ |
|
4989 RESET_OCCURRENCE; |
|
4990 if (CONT->parent == NULL) |
|
4991 break; |
|
4992 |
|
4993 switch (CONT->parent->type) { |
|
4994 case XML_ELEMENT_CONTENT_PCDATA: |
|
4995 DEBUG_VALID_MSG("Error: parent pcdata"); |
|
4996 return(-1); |
|
4997 case XML_ELEMENT_CONTENT_ELEMENT: |
|
4998 DEBUG_VALID_MSG("Error: parent element"); |
|
4999 return(-1); |
|
5000 case XML_ELEMENT_CONTENT_OR: |
|
5001 if (ret == 1) { |
|
5002 DEBUG_VALID_MSG("Or succeeded"); |
|
5003 CONT = CONT->parent; |
|
5004 DEPTH--; |
|
5005 } else { |
|
5006 DEBUG_VALID_MSG("Or failed"); |
|
5007 CONT = CONT->parent; |
|
5008 DEPTH--; |
|
5009 } |
|
5010 break; |
|
5011 case XML_ELEMENT_CONTENT_SEQ: |
|
5012 if (ret == 0) { |
|
5013 DEBUG_VALID_MSG("Sequence failed"); |
|
5014 CONT = CONT->parent; |
|
5015 DEPTH--; |
|
5016 } else if (CONT == CONT->parent->c1) { |
|
5017 DEBUG_VALID_MSG("Sequence testing 2nd branch"); |
|
5018 CONT = CONT->parent->c2; |
|
5019 goto cont; |
|
5020 } else { |
|
5021 DEBUG_VALID_MSG("Sequence succeeded"); |
|
5022 CONT = CONT->parent; |
|
5023 DEPTH--; |
|
5024 } |
|
5025 } |
|
5026 } |
|
5027 if (NODE != NULL) { |
|
5028 xmlNodePtr cur; |
|
5029 |
|
5030 cur = ctxt->vstate->node; |
|
5031 DEBUG_VALID_MSG("Failed, remaining input, rollback"); |
|
5032 if (vstateVPop(ctxt) < 0 ) { |
|
5033 DEBUG_VALID_MSG("exhaustion, failed"); |
|
5034 return(0); |
|
5035 } |
|
5036 if (cur != ctxt->vstate->node) |
|
5037 determinist = -3; |
|
5038 goto cont; |
|
5039 } |
|
5040 if (ret == 0) { |
|
5041 xmlNodePtr cur; |
|
5042 |
|
5043 cur = ctxt->vstate->node; |
|
5044 DEBUG_VALID_MSG("Failure, rollback"); |
|
5045 if (vstateVPop(ctxt) < 0 ) { |
|
5046 DEBUG_VALID_MSG("exhaustion, failed"); |
|
5047 return(0); |
|
5048 } |
|
5049 if (cur != ctxt->vstate->node) |
|
5050 determinist = -3; |
|
5051 goto cont; |
|
5052 } |
|
5053 return(determinist); |
|
5054 } |
|
5055 #endif |
|
5056 |
|
5057 /** |
|
5058 * xmlSnprintfElements: |
|
5059 * @param buf an output buffer |
|
5060 * @param size the size of the buffer |
|
5061 * @param content An element |
|
5062 * @param glob 1 if one must print the englobing parenthesis, 0 otherwise |
|
5063 * |
|
5064 * This will dump the list of elements to the buffer |
|
5065 * Intended just for the debug routine |
|
5066 */ |
|
5067 static void |
|
5068 xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) { |
|
5069 xmlNodePtr cur; |
|
5070 int len; |
|
5071 |
|
5072 if (node == NULL) return; |
|
5073 if (glob) strcat(buf, "("); |
|
5074 cur = node; |
|
5075 while (cur != NULL) { |
|
5076 len = strlen(buf); |
|
5077 if (size - len < 50) { |
|
5078 if ((size - len > 4) && (buf[len - 1] != '.')) |
|
5079 strcat(buf, " ..."); |
|
5080 return; |
|
5081 } |
|
5082 switch (cur->type) { |
|
5083 case XML_ELEMENT_NODE: |
|
5084 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { |
|
5085 if (size - len < xmlStrlen(cur->ns->prefix) + 10) { |
|
5086 if ((size - len > 4) && (buf[len - 1] != '.')) |
|
5087 strcat(buf, " ..."); |
|
5088 return; |
|
5089 } |
|
5090 strcat(buf, (char *) cur->ns->prefix); |
|
5091 strcat(buf, ":"); |
|
5092 } |
|
5093 if (size - len < xmlStrlen(cur->name) + 10) { |
|
5094 if ((size - len > 4) && (buf[len - 1] != '.')) |
|
5095 strcat(buf, " ..."); |
|
5096 return; |
|
5097 } |
|
5098 strcat(buf, (char *) cur->name); |
|
5099 if (cur->next != NULL) |
|
5100 strcat(buf, " "); |
|
5101 break; |
|
5102 case XML_TEXT_NODE: |
|
5103 if (xmlIsBlankNode(cur)) |
|
5104 break; |
|
5105 case XML_CDATA_SECTION_NODE: |
|
5106 case XML_ENTITY_REF_NODE: |
|
5107 strcat(buf, "CDATA"); |
|
5108 if (cur->next != NULL) |
|
5109 strcat(buf, " "); |
|
5110 break; |
|
5111 case XML_ATTRIBUTE_NODE: |
|
5112 case XML_DOCUMENT_NODE: |
|
5113 #ifdef LIBXML_DOCB_ENABLED |
|
5114 case XML_DOCB_DOCUMENT_NODE: |
|
5115 #endif |
|
5116 case XML_HTML_DOCUMENT_NODE: |
|
5117 case XML_DOCUMENT_TYPE_NODE: |
|
5118 case XML_DOCUMENT_FRAG_NODE: |
|
5119 case XML_NOTATION_NODE: |
|
5120 case XML_NAMESPACE_DECL: |
|
5121 strcat(buf, "???"); |
|
5122 if (cur->next != NULL) |
|
5123 strcat(buf, " "); |
|
5124 break; |
|
5125 case XML_ENTITY_NODE: |
|
5126 case XML_PI_NODE: |
|
5127 case XML_DTD_NODE: |
|
5128 case XML_COMMENT_NODE: |
|
5129 case XML_ELEMENT_DECL: |
|
5130 case XML_ATTRIBUTE_DECL: |
|
5131 case XML_ENTITY_DECL: |
|
5132 case XML_XINCLUDE_START: |
|
5133 case XML_XINCLUDE_END: |
|
5134 break; |
|
5135 } |
|
5136 cur = cur->next; |
|
5137 } |
|
5138 if (glob) strcat(buf, ")"); |
|
5139 } |
|
5140 |
|
5141 /** |
|
5142 * xmlValidateElementContent: |
|
5143 * @param ctxt the validation context |
|
5144 * @param child the child list |
|
5145 * @param elemDecl pointer to the element declaration |
|
5146 * @param warn emit the error message |
|
5147 * @param parent the parent element (for error reporting) |
|
5148 * |
|
5149 * Try to validate the content model of an element |
|
5150 * |
|
5151 * returns 1 if valid or 0 if not and -1 in case of error |
|
5152 */ |
|
5153 |
|
5154 static int |
|
5155 xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, |
|
5156 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) { |
|
5157 int ret = 1; |
|
5158 #ifndef LIBXML_REGEXP_ENABLED |
|
5159 xmlNodePtr repl = NULL, last = NULL, tmp; |
|
5160 #endif |
|
5161 xmlNodePtr cur; |
|
5162 xmlElementContentPtr cont; |
|
5163 const xmlChar *name; |
|
5164 |
|
5165 if (elemDecl == NULL) |
|
5166 return(-1); |
|
5167 cont = elemDecl->content; |
|
5168 name = elemDecl->name; |
|
5169 |
|
5170 #ifdef LIBXML_REGEXP_ENABLED |
|
5171 /* Build the regexp associated to the content model */ |
|
5172 if (elemDecl->contModel == NULL) |
|
5173 ret = xmlValidBuildContentModel(ctxt, elemDecl); |
|
5174 if (elemDecl->contModel == NULL) { |
|
5175 return(-1); |
|
5176 } else { |
|
5177 xmlRegExecCtxtPtr exec; |
|
5178 |
|
5179 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) { |
|
5180 return(-1); |
|
5181 } |
|
5182 ctxt->nodeMax = 0; |
|
5183 ctxt->nodeNr = 0; |
|
5184 ctxt->nodeTab = NULL; |
|
5185 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL); |
|
5186 if (exec != NULL) { |
|
5187 cur = child; |
|
5188 while (cur != NULL) { |
|
5189 switch (cur->type) { |
|
5190 case XML_ENTITY_REF_NODE: |
|
5191 /* |
|
5192 * Push the current node to be able to roll back |
|
5193 * and process within the entity |
|
5194 */ |
|
5195 if ((cur->children != NULL) && |
|
5196 (cur->children->children != NULL)) { |
|
5197 nodeVPush(ctxt, cur); |
|
5198 cur = cur->children->children; |
|
5199 continue; |
|
5200 } |
|
5201 break; |
|
5202 case XML_TEXT_NODE: |
|
5203 if (xmlIsBlankNode(cur)) |
|
5204 break; |
|
5205 ret = 0; |
|
5206 goto fail; |
|
5207 case XML_CDATA_SECTION_NODE: |
|
5208 |
|
5209 ret = 0; |
|
5210 goto fail; |
|
5211 case XML_ELEMENT_NODE: |
|
5212 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { |
|
5213 xmlChar fn[50]; |
|
5214 xmlChar *fullname; |
|
5215 |
|
5216 fullname = xmlBuildQName(cur->name, |
|
5217 cur->ns->prefix, fn, 50); |
|
5218 if (fullname == NULL) { |
|
5219 ret = -1; |
|
5220 goto fail; |
|
5221 } |
|
5222 ret = xmlRegExecPushString(exec, fullname, NULL); |
|
5223 if ((fullname != fn) && (fullname != cur->name)) |
|
5224 xmlFree(fullname); |
|
5225 } else { |
|
5226 ret = xmlRegExecPushString(exec, cur->name, NULL); |
|
5227 } |
|
5228 break; |
|
5229 default: |
|
5230 break; |
|
5231 } |
|
5232 /* |
|
5233 * Switch to next element |
|
5234 */ |
|
5235 cur = cur->next; |
|
5236 while (cur == NULL) { |
|
5237 cur = nodeVPop(ctxt); |
|
5238 if (cur == NULL) |
|
5239 break; |
|
5240 cur = cur->next; |
|
5241 } |
|
5242 } |
|
5243 ret = xmlRegExecPushString(exec, NULL, NULL); |
|
5244 fail: |
|
5245 xmlRegFreeExecCtxt(exec); |
|
5246 } |
|
5247 } |
|
5248 #else /* LIBXML_REGEXP_ENABLED */ |
|
5249 /* |
|
5250 * Allocate the stack |
|
5251 */ |
|
5252 ctxt->vstateMax = 8; |
|
5253 ctxt->vstateTab = (xmlValidState *) xmlMalloc( |
|
5254 ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); |
|
5255 if (ctxt->vstateTab == NULL) { |
|
5256 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
5257 return(-1); |
|
5258 } |
|
5259 /* |
|
5260 * The first entry in the stack is reserved to the current state |
|
5261 */ |
|
5262 ctxt->nodeMax = 0; |
|
5263 ctxt->nodeNr = 0; |
|
5264 ctxt->nodeTab = NULL; |
|
5265 ctxt->vstate = &ctxt->vstateTab[0]; |
|
5266 ctxt->vstateNr = 1; |
|
5267 CONT = cont; |
|
5268 NODE = child; |
|
5269 DEPTH = 0; |
|
5270 OCCURS = 0; |
|
5271 STATE = 0; |
|
5272 ret = xmlValidateElementType(ctxt); |
|
5273 if ((ret == -3) && (warn)) { |
|
5274 xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST, |
|
5275 EMBED_ERRTXT("Content model for Element %s is ambiguous\n"), |
|
5276 name, NULL, NULL); |
|
5277 } else if (ret == -2) { |
|
5278 /* |
|
5279 * An entities reference appeared at this level. |
|
5280 * Buid a minimal representation of this node content |
|
5281 * sufficient to run the validation process on it |
|
5282 */ |
|
5283 DEBUG_VALID_MSG("Found an entity reference, linearizing"); |
|
5284 cur = child; |
|
5285 while (cur != NULL) { |
|
5286 switch (cur->type) { |
|
5287 case XML_ENTITY_REF_NODE: |
|
5288 /* |
|
5289 * Push the current node to be able to roll back |
|
5290 * and process within the entity |
|
5291 */ |
|
5292 if ((cur->children != NULL) && |
|
5293 (cur->children->children != NULL)) { |
|
5294 nodeVPush(ctxt, cur); |
|
5295 cur = cur->children->children; |
|
5296 continue; |
|
5297 } |
|
5298 break; |
|
5299 case XML_TEXT_NODE: |
|
5300 if (xmlIsBlankNode(cur)) |
|
5301 break; |
|
5302 /* no break on purpose */ |
|
5303 case XML_CDATA_SECTION_NODE: |
|
5304 /* no break on purpose */ |
|
5305 case XML_ELEMENT_NODE: |
|
5306 /* |
|
5307 * Allocate a new node and minimally fills in |
|
5308 * what's required |
|
5309 */ |
|
5310 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); |
|
5311 if (tmp == NULL) { |
|
5312 xmlVErrMemory(ctxt, EMBED_ERRTXT("malloc failed")); |
|
5313 xmlFreeNodeList(repl); |
|
5314 ret = -1; |
|
5315 goto done; |
|
5316 } |
|
5317 tmp->type = cur->type; |
|
5318 tmp->name = cur->name; |
|
5319 tmp->ns = cur->ns; |
|
5320 tmp->next = NULL; |
|
5321 tmp->content = NULL; |
|
5322 if (repl == NULL) |
|
5323 repl = last = tmp; |
|
5324 else { |
|
5325 last->next = tmp; |
|
5326 last = tmp; |
|
5327 } |
|
5328 if (cur->type == XML_CDATA_SECTION_NODE) { |
|
5329 /* |
|
5330 * E59 spaces in CDATA does not match the |
|
5331 * nonterminal S |
|
5332 */ |
|
5333 tmp->content = xmlStrdup(BAD_CAST "CDATA"); |
|
5334 } |
|
5335 break; |
|
5336 default: |
|
5337 break; |
|
5338 } |
|
5339 /* |
|
5340 * Switch to next element |
|
5341 */ |
|
5342 cur = cur->next; |
|
5343 while (cur == NULL) { |
|
5344 cur = nodeVPop(ctxt); |
|
5345 if (cur == NULL) |
|
5346 break; |
|
5347 cur = cur->next; |
|
5348 } |
|
5349 } |
|
5350 |
|
5351 /* |
|
5352 * Relaunch the validation |
|
5353 */ |
|
5354 ctxt->vstate = &ctxt->vstateTab[0]; |
|
5355 ctxt->vstateNr = 1; |
|
5356 CONT = cont; |
|
5357 NODE = repl; |
|
5358 DEPTH = 0; |
|
5359 OCCURS = 0; |
|
5360 STATE = 0; |
|
5361 ret = xmlValidateElementType(ctxt); |
|
5362 } |
|
5363 #endif /* LIBXML_REGEXP_ENABLED */ |
|
5364 if ((warn) && ((ret != 1) && (ret != -3))) { |
|
5365 if ((ctxt != NULL) && (ctxt->warning != NULL)) { |
|
5366 #if 0 |
|
5367 char expr[5000]; |
|
5368 char list[5000]; |
|
5369 #endif |
|
5370 char *expr, *list; |
|
5371 expr = (char *)malloc(5000); |
|
5372 list = (char *)malloc(5000); |
|
5373 |
|
5374 expr[0] = 0; |
|
5375 xmlSnprintfElementContent(&expr[0], 5000, cont, 1); |
|
5376 list[0] = 0; |
|
5377 #ifndef LIBXML_REGEXP_ENABLED |
|
5378 if (repl != NULL) |
|
5379 xmlSnprintfElements(&list[0], 5000, repl, 1); |
|
5380 else |
|
5381 #endif /* LIBXML_REGEXP_ENABLED */ |
|
5382 xmlSnprintfElements(&list[0], 5000, child, 1); |
|
5383 |
|
5384 if (name != NULL) { |
|
5385 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, |
|
5386 EMBED_ERRTXT("Element %s content does not follow the DTD, expecting %s, got %s\n"), |
|
5387 name, BAD_CAST expr, BAD_CAST list); |
|
5388 } else { |
|
5389 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, |
|
5390 EMBED_ERRTXT("Element content does not follow the DTD, expecting %s, got %s\n"), |
|
5391 BAD_CAST expr, BAD_CAST list, NULL); |
|
5392 } |
|
5393 free(expr), free(list); |
|
5394 } else { |
|
5395 if (name != NULL) { |
|
5396 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, |
|
5397 EMBED_ERRTXT("Element %s content does not follow the DTD\n"), |
|
5398 name, NULL, NULL); |
|
5399 } else { |
|
5400 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, |
|
5401 EMBED_ERRTXT("Element content does not follow the DTD\n"), |
|
5402 NULL, NULL, NULL); |
|
5403 } |
|
5404 } |
|
5405 ret = 0; |
|
5406 } |
|
5407 if (ret == -3) |
|
5408 ret = 1; |
|
5409 |
|
5410 #ifndef LIBXML_REGEXP_ENABLED |
|
5411 done: |
|
5412 /* |
|
5413 * Deallocate the copy if done, and free up the validation stack |
|
5414 */ |
|
5415 while (repl != NULL) { |
|
5416 tmp = repl->next; |
|
5417 xmlFree(repl); |
|
5418 repl = tmp; |
|
5419 } |
|
5420 ctxt->vstateMax = 0; |
|
5421 if (ctxt->vstateTab != NULL) { |
|
5422 xmlFree(ctxt->vstateTab); |
|
5423 ctxt->vstateTab = NULL; |
|
5424 } |
|
5425 #endif |
|
5426 ctxt->nodeMax = 0; |
|
5427 ctxt->nodeNr = 0; |
|
5428 if (ctxt->nodeTab != NULL) { |
|
5429 xmlFree(ctxt->nodeTab); |
|
5430 ctxt->nodeTab = NULL; |
|
5431 } |
|
5432 return(ret); |
|
5433 |
|
5434 } |
|
5435 |
|
5436 /** |
|
5437 * xmlValidateCdataElement: |
|
5438 * @param ctxt the validation context |
|
5439 * @param doc a document instance |
|
5440 * @param elem an element instance |
|
5441 * |
|
5442 * Check that an element follows #CDATA |
|
5443 * |
|
5444 * returns 1 if valid or 0 otherwise |
|
5445 */ |
|
5446 static int |
|
5447 xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
5448 xmlNodePtr elem) { |
|
5449 int ret = 1; |
|
5450 xmlNodePtr cur, child; |
|
5451 |
|
5452 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL)) |
|
5453 return(0); |
|
5454 |
|
5455 child = elem->children; |
|
5456 |
|
5457 cur = child; |
|
5458 while (cur != NULL) { |
|
5459 switch (cur->type) { |
|
5460 case XML_ENTITY_REF_NODE: |
|
5461 /* |
|
5462 * Push the current node to be able to roll back |
|
5463 * and process within the entity |
|
5464 */ |
|
5465 if ((cur->children != NULL) && |
|
5466 (cur->children->children != NULL)) { |
|
5467 nodeVPush(ctxt, cur); |
|
5468 cur = cur->children->children; |
|
5469 continue; |
|
5470 } |
|
5471 break; |
|
5472 case XML_COMMENT_NODE: |
|
5473 case XML_PI_NODE: |
|
5474 case XML_TEXT_NODE: |
|
5475 case XML_CDATA_SECTION_NODE: |
|
5476 break; |
|
5477 default: |
|
5478 ret = 0; |
|
5479 goto done; |
|
5480 } |
|
5481 /* |
|
5482 * Switch to next element |
|
5483 */ |
|
5484 cur = cur->next; |
|
5485 while (cur == NULL) { |
|
5486 cur = nodeVPop(ctxt); |
|
5487 if (cur == NULL) |
|
5488 break; |
|
5489 cur = cur->next; |
|
5490 } |
|
5491 } |
|
5492 done: |
|
5493 ctxt->nodeMax = 0; |
|
5494 ctxt->nodeNr = 0; |
|
5495 if (ctxt->nodeTab != NULL) { |
|
5496 xmlFree(ctxt->nodeTab); |
|
5497 ctxt->nodeTab = NULL; |
|
5498 } |
|
5499 return(ret); |
|
5500 } |
|
5501 |
|
5502 /** |
|
5503 * xmlValidateCheckMixed: |
|
5504 * @param ctxt the validation context |
|
5505 * @param cont the mixed content model |
|
5506 * @param qname the qualified name as appearing in the serialization |
|
5507 * |
|
5508 * Check if the given node is part of the content model. |
|
5509 * |
|
5510 * Returns 1 if yes, 0 if no, -1 in case of error |
|
5511 */ |
|
5512 static int |
|
5513 xmlValidateCheckMixed(xmlValidCtxtPtr ctxt, |
|
5514 xmlElementContentPtr cont, const xmlChar *qname) { |
|
5515 const xmlChar *name; |
|
5516 int plen; |
|
5517 name = xmlSplitQName3(qname, &plen); |
|
5518 |
|
5519 if (name == NULL) { |
|
5520 while (cont != NULL) { |
|
5521 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { |
|
5522 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname))) |
|
5523 return(1); |
|
5524 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && |
|
5525 (cont->c1 != NULL) && |
|
5526 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ |
|
5527 if ((cont->c1->prefix == NULL) && |
|
5528 (xmlStrEqual(cont->c1->name, qname))) |
|
5529 return(1); |
|
5530 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || |
|
5531 (cont->c1 == NULL) || |
|
5532 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ |
|
5533 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT, |
|
5534 EMBED_ERRTXT("Internal: MIXED struct corrupted\n"), |
|
5535 NULL); |
|
5536 break; |
|
5537 } |
|
5538 cont = cont->c2; |
|
5539 } |
|
5540 } else { |
|
5541 while (cont != NULL) { |
|
5542 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { |
|
5543 if ((cont->prefix != NULL) && |
|
5544 (xmlStrncmp(cont->prefix, qname, plen) == 0) && |
|
5545 (xmlStrEqual(cont->name, name))) |
|
5546 return(1); |
|
5547 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && |
|
5548 (cont->c1 != NULL) && |
|
5549 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ |
|
5550 if ((cont->c1->prefix != NULL) && |
|
5551 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) && |
|
5552 (xmlStrEqual(cont->c1->name, name))) |
|
5553 return(1); |
|
5554 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || |
|
5555 (cont->c1 == NULL) || |
|
5556 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ |
|
5557 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT, |
|
5558 EMBED_ERRTXT("Internal: MIXED struct corrupted\n"), |
|
5559 NULL); |
|
5560 break; |
|
5561 } |
|
5562 cont = cont->c2; |
|
5563 } |
|
5564 } |
|
5565 return(0); |
|
5566 } |
|
5567 |
|
5568 /** |
|
5569 * xmlValidGetElemDecl: |
|
5570 * @param ctxt the validation context |
|
5571 * @param doc a document instance |
|
5572 * @param elem an element instance |
|
5573 * @param extsubset pointer, (out) indicate if the declaration was found |
|
5574 * in the external subset. |
|
5575 * |
|
5576 * Finds a declaration associated to an element in the document. |
|
5577 * |
|
5578 * returns the pointer to the declaration or NULL if not found. |
|
5579 */ |
|
5580 static xmlElementPtr |
|
5581 xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
5582 xmlNodePtr elem, int *extsubset) { |
|
5583 xmlElementPtr elemDecl = NULL; |
|
5584 const xmlChar *prefix = NULL; |
|
5585 |
|
5586 if ((elem == NULL) || (elem->name == NULL)) return(NULL); |
|
5587 if (extsubset != NULL) |
|
5588 *extsubset = 0; |
|
5589 |
|
5590 /* |
|
5591 * Fetch the declaration for the qualified name |
|
5592 */ |
|
5593 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) |
|
5594 prefix = elem->ns->prefix; |
|
5595 |
|
5596 if (prefix != NULL) { |
|
5597 elemDecl = xmlGetDtdQElementDesc(doc->intSubset, |
|
5598 elem->name, prefix); |
|
5599 if ((elemDecl == NULL) && (doc->extSubset != NULL)) { |
|
5600 elemDecl = xmlGetDtdQElementDesc(doc->extSubset, |
|
5601 elem->name, prefix); |
|
5602 if ((elemDecl != NULL) && (extsubset != NULL)) |
|
5603 *extsubset = 1; |
|
5604 } |
|
5605 } |
|
5606 |
|
5607 /* |
|
5608 * Fetch the declaration for the non qualified name |
|
5609 * This is "non-strict" validation should be done on the |
|
5610 * full QName but in that case being flexible makes sense. |
|
5611 */ |
|
5612 if (elemDecl == NULL) { |
|
5613 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name); |
|
5614 if ((elemDecl == NULL) && (doc->extSubset != NULL)) { |
|
5615 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name); |
|
5616 if ((elemDecl != NULL) && (extsubset != NULL)) |
|
5617 *extsubset = 1; |
|
5618 } |
|
5619 } |
|
5620 if (elemDecl == NULL) { |
|
5621 xmlErrValidNode(ctxt, elem, |
|
5622 XML_DTD_UNKNOWN_ELEM, |
|
5623 EMBED_ERRTXT("No declaration for element %s\n"), |
|
5624 elem->name, NULL, NULL); |
|
5625 } |
|
5626 return(elemDecl); |
|
5627 } |
|
5628 |
|
5629 #ifdef LIBXML_REGEXP_ENABLED |
|
5630 /** |
|
5631 * xmlValidatePushElement: |
|
5632 * @param ctxt the validation context |
|
5633 * @param doc a document instance |
|
5634 * @param elem an element instance |
|
5635 * @param qname the qualified name as appearing in the serialization |
|
5636 * |
|
5637 * Push a new element start on the validation stack. |
|
5638 * |
|
5639 * returns 1 if no validation problem was found or 0 otherwise |
|
5640 */ |
|
5641 int |
|
5642 xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
5643 xmlNodePtr elem, const xmlChar *qname) { |
|
5644 int ret = 1; |
|
5645 xmlElementPtr eDecl; |
|
5646 int extsubset = 0; |
|
5647 |
|
5648 /* printf("PushElem %s\n", qname); */ |
|
5649 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { |
|
5650 xmlValidStatePtr state = ctxt->vstate; |
|
5651 xmlElementPtr elemDecl; |
|
5652 |
|
5653 /* |
|
5654 * Check the new element agaisnt the content model of the new elem. |
|
5655 */ |
|
5656 if (state->elemDecl != NULL) { |
|
5657 elemDecl = state->elemDecl; |
|
5658 |
|
5659 switch(elemDecl->etype) { |
|
5660 case XML_ELEMENT_TYPE_UNDEFINED: |
|
5661 ret = 0; |
|
5662 break; |
|
5663 case XML_ELEMENT_TYPE_EMPTY: |
|
5664 xmlErrValidNode(ctxt, state->node, |
|
5665 XML_DTD_NOT_EMPTY, |
|
5666 EMBED_ERRTXT("Element %s was declared EMPTY this one has content\n"), |
|
5667 state->node->name, NULL, NULL); |
|
5668 ret = 0; |
|
5669 break; |
|
5670 case XML_ELEMENT_TYPE_ANY: |
|
5671 /* I don't think anything is required then */ |
|
5672 break; |
|
5673 case XML_ELEMENT_TYPE_MIXED: |
|
5674 /* simple case of declared as #PCDATA */ |
|
5675 if ((elemDecl->content != NULL) && |
|
5676 (elemDecl->content->type == |
|
5677 XML_ELEMENT_CONTENT_PCDATA)) { |
|
5678 xmlErrValidNode(ctxt, state->node, |
|
5679 XML_DTD_NOT_PCDATA, |
|
5680 EMBED_ERRTXT("Element %s was declared #PCDATA but contains non text nodes\n"), |
|
5681 state->node->name, NULL, NULL); |
|
5682 ret = 0; |
|
5683 } else { |
|
5684 ret = xmlValidateCheckMixed(ctxt, elemDecl->content, |
|
5685 qname); |
|
5686 if (ret != 1) { |
|
5687 xmlErrValidNode(ctxt, state->node, |
|
5688 XML_DTD_INVALID_CHILD, |
|
5689 EMBED_ERRTXT("Element %s is not declared in %s list of possible children\n"), |
|
5690 qname, state->node->name, NULL); |
|
5691 } |
|
5692 } |
|
5693 break; |
|
5694 case XML_ELEMENT_TYPE_ELEMENT: |
|
5695 /* |
|
5696 * |
|
5697 * VC: Standalone Document Declaration |
|
5698 * - element types with element content, if white space |
|
5699 * occurs directly within any instance of those types. |
|
5700 */ |
|
5701 if (state->exec != NULL) { |
|
5702 ret = xmlRegExecPushString(state->exec, qname, NULL); |
|
5703 if (ret < 0) { |
|
5704 xmlErrValidNode(ctxt, state->node, |
|
5705 XML_DTD_CONTENT_MODEL, |
|
5706 EMBED_ERRTXT("Element %s content does not follow the DTD, Misplaced %s\n"), |
|
5707 state->node->name, qname, NULL); |
|
5708 ret = 0; |
|
5709 } else { |
|
5710 ret = 1; |
|
5711 } |
|
5712 } |
|
5713 break; |
|
5714 } |
|
5715 } |
|
5716 } |
|
5717 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset); |
|
5718 vstateVPush(ctxt, eDecl, elem); |
|
5719 return(ret); |
|
5720 } |
|
5721 |
|
5722 /** |
|
5723 * xmlValidatePushCData: |
|
5724 * @param ctxt the validation context |
|
5725 * @param data some character data read |
|
5726 * @param len the lenght of the data |
|
5727 * |
|
5728 * check the CData parsed for validation in the current stack |
|
5729 * |
|
5730 * returns 1 if no validation problem was found or 0 otherwise |
|
5731 */ |
|
5732 int |
|
5733 xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) { |
|
5734 int ret = 1; |
|
5735 |
|
5736 /* printf("CDATA %s %d\n", data, len); */ |
|
5737 if (len <= 0) |
|
5738 return(ret); |
|
5739 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { |
|
5740 xmlValidStatePtr state = ctxt->vstate; |
|
5741 xmlElementPtr elemDecl; |
|
5742 |
|
5743 /* |
|
5744 * Check the new element agaisnt the content model of the new elem. |
|
5745 */ |
|
5746 if (state->elemDecl != NULL) { |
|
5747 elemDecl = state->elemDecl; |
|
5748 |
|
5749 switch(elemDecl->etype) { |
|
5750 case XML_ELEMENT_TYPE_UNDEFINED: |
|
5751 ret = 0; |
|
5752 break; |
|
5753 case XML_ELEMENT_TYPE_EMPTY: |
|
5754 xmlErrValidNode(ctxt, state->node, |
|
5755 XML_DTD_NOT_EMPTY, |
|
5756 EMBED_ERRTXT("Element %s was declared EMPTY this one has content\n"), |
|
5757 state->node->name, NULL, NULL); |
|
5758 ret = 0; |
|
5759 break; |
|
5760 case XML_ELEMENT_TYPE_ANY: |
|
5761 break; |
|
5762 case XML_ELEMENT_TYPE_MIXED: |
|
5763 break; |
|
5764 case XML_ELEMENT_TYPE_ELEMENT: |
|
5765 if (len > 0) { |
|
5766 int i; |
|
5767 |
|
5768 for (i = 0;i < len;i++) { |
|
5769 if (!IS_BLANK_CH(data[i])) { |
|
5770 xmlErrValidNode(ctxt, state->node, |
|
5771 XML_DTD_CONTENT_MODEL, |
|
5772 EMBED_ERRTXT("Element %s content does not follow the DTD, Text not allowed\n"), |
|
5773 state->node->name, NULL, NULL); |
|
5774 ret = 0; |
|
5775 goto done; |
|
5776 } |
|
5777 } |
|
5778 /* |
|
5779 * |
|
5780 * VC: Standalone Document Declaration |
|
5781 * element types with element content, if white space |
|
5782 * occurs directly within any instance of those types. |
|
5783 */ |
|
5784 } |
|
5785 break; |
|
5786 } |
|
5787 } |
|
5788 } |
|
5789 done: |
|
5790 return(ret); |
|
5791 } |
|
5792 |
|
5793 /** |
|
5794 * xmlValidatePopElement: |
|
5795 * @param ctxt the validation context |
|
5796 * @param doc a document instance |
|
5797 * @param elem an element instance |
|
5798 * @param qname the qualified name as appearing in the serialization |
|
5799 * |
|
5800 * Pop the element end from the validation stack. |
|
5801 * |
|
5802 * returns 1 if no validation problem was found or 0 otherwise |
|
5803 */ |
|
5804 int |
|
5805 xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, |
|
5806 xmlNodePtr elem ATTRIBUTE_UNUSED, |
|
5807 const xmlChar *qname ATTRIBUTE_UNUSED) { |
|
5808 int ret = 1; |
|
5809 |
|
5810 /* printf("PopElem %s\n", qname); */ |
|
5811 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { |
|
5812 xmlValidStatePtr state = ctxt->vstate; |
|
5813 xmlElementPtr elemDecl; |
|
5814 |
|
5815 /* |
|
5816 * Check the new element agaisnt the content model of the new elem. |
|
5817 */ |
|
5818 if (state->elemDecl != NULL) { |
|
5819 elemDecl = state->elemDecl; |
|
5820 |
|
5821 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) { |
|
5822 if (state->exec != NULL) { |
|
5823 ret = xmlRegExecPushString(state->exec, NULL, NULL); |
|
5824 if (ret == 0) { |
|
5825 xmlErrValidNode(ctxt, state->node, |
|
5826 XML_DTD_CONTENT_MODEL, |
|
5827 EMBED_ERRTXT("Element %s content does not follow the DTD, Expecting more child\n"), |
|
5828 state->node->name, NULL,NULL); |
|
5829 } else { |
|
5830 /* |
|
5831 * previous validation errors should not generate |
|
5832 * a new one here |
|
5833 */ |
|
5834 ret = 1; |
|
5835 } |
|
5836 } |
|
5837 } |
|
5838 } |
|
5839 vstateVPop(ctxt); |
|
5840 } |
|
5841 return(ret); |
|
5842 } |
|
5843 #endif /* LIBXML_REGEXP_ENABLED */ |
|
5844 |
|
5845 /** |
|
5846 * xmlValidateOneElement: |
|
5847 * @param ctxt the validation context |
|
5848 * @param doc a document instance |
|
5849 * @param elem an element instance |
|
5850 * |
|
5851 * Try to validate a single element and it's attributes, |
|
5852 * basically it does the following checks as described by the |
|
5853 * XML-1.0 recommendation: |
|
5854 * - [ VC: Element Valid ] |
|
5855 * - [ VC: Required Attribute ] |
|
5856 * Then call xmlValidateOneAttribute() for each attribute present. |
|
5857 * |
|
5858 * The ID/IDREF checkings are done separately |
|
5859 * |
|
5860 * returns 1 if valid or 0 otherwise |
|
5861 */ |
|
5862 |
|
5863 int |
|
5864 xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, |
|
5865 xmlNodePtr elem) { |
|
5866 xmlElementPtr elemDecl = NULL; |
|
5867 xmlElementContentPtr cont; |
|
5868 xmlAttributePtr attr; |
|
5869 xmlNodePtr child; |
|
5870 int ret = 1, tmp; |
|
5871 const xmlChar *name; |
|
5872 int extsubset = 0; |
|
5873 |
|
5874 CHECK_DTD; |
|
5875 |
|
5876 if (elem == NULL) return(0); |
|
5877 switch (elem->type) { |
|
5878 case XML_ATTRIBUTE_NODE: |
|
5879 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5880 EMBED_ERRTXT("Attribute element not expected\n"), NULL, NULL ,NULL); |
|
5881 return(0); |
|
5882 case XML_TEXT_NODE: |
|
5883 if (elem->children != NULL) { |
|
5884 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5885 EMBED_ERRTXT("Text element has children !\n"), |
|
5886 NULL,NULL,NULL); |
|
5887 return(0); |
|
5888 } |
|
5889 if (elem->properties != NULL) { |
|
5890 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5891 EMBED_ERRTXT("Text element has attribute !\n"), |
|
5892 NULL,NULL,NULL); |
|
5893 return(0); |
|
5894 } |
|
5895 if (elem->ns != NULL) { |
|
5896 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5897 EMBED_ERRTXT("Text element has namespace !\n"), |
|
5898 NULL,NULL,NULL); |
|
5899 return(0); |
|
5900 } |
|
5901 if (elem->nsDef != NULL) { |
|
5902 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5903 EMBED_ERRTXT("Text element has namespace !\n"), |
|
5904 NULL,NULL,NULL); |
|
5905 return(0); |
|
5906 } |
|
5907 if (elem->content == NULL) { |
|
5908 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5909 EMBED_ERRTXT("Text element has no content !\n"), |
|
5910 NULL,NULL,NULL); |
|
5911 return(0); |
|
5912 } |
|
5913 return(1); |
|
5914 case XML_XINCLUDE_START: |
|
5915 case XML_XINCLUDE_END: |
|
5916 return(1); |
|
5917 case XML_CDATA_SECTION_NODE: |
|
5918 case XML_ENTITY_REF_NODE: |
|
5919 case XML_PI_NODE: |
|
5920 case XML_COMMENT_NODE: |
|
5921 return(1); |
|
5922 case XML_ENTITY_NODE: |
|
5923 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5924 EMBED_ERRTXT("Entity element not expected\n"), NULL, NULL ,NULL); |
|
5925 return(0); |
|
5926 case XML_NOTATION_NODE: |
|
5927 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5928 EMBED_ERRTXT("Notation element not expected\n"), NULL, NULL ,NULL); |
|
5929 return(0); |
|
5930 case XML_DOCUMENT_NODE: |
|
5931 case XML_DOCUMENT_TYPE_NODE: |
|
5932 case XML_DOCUMENT_FRAG_NODE: |
|
5933 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5934 EMBED_ERRTXT("Document element not expected\n"), NULL, NULL ,NULL); |
|
5935 return(0); |
|
5936 |
|
5937 case XML_HTML_DOCUMENT_NODE: |
|
5938 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5939 EMBED_ERRTXT("HTML Document not expected\n"), NULL, NULL ,NULL); |
|
5940 return(0); |
|
5941 case XML_ELEMENT_NODE: |
|
5942 break; |
|
5943 default: |
|
5944 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, |
|
5945 EMBED_ERRTXT("unknown element type\n"), NULL, NULL ,NULL); |
|
5946 return(0); |
|
5947 } |
|
5948 |
|
5949 /* |
|
5950 * Fetch the declaration |
|
5951 */ |
|
5952 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset); |
|
5953 if (elemDecl == NULL) |
|
5954 return(0); |
|
5955 |
|
5956 /* |
|
5957 * If vstateNr is not zero that means continuous validation is |
|
5958 * activated, do not try to check the content model at that level. |
|
5959 */ |
|
5960 if (ctxt->vstateNr == 0) { |
|
5961 /* Check that the element content matches the definition */ |
|
5962 switch (elemDecl->etype) { |
|
5963 case XML_ELEMENT_TYPE_UNDEFINED: |
|
5964 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM, |
|
5965 EMBED_ERRTXT("No declaration for element %s\n"), |
|
5966 elem->name, NULL, NULL); |
|
5967 return(0); |
|
5968 case XML_ELEMENT_TYPE_EMPTY: |
|
5969 if (elem->children != NULL) { |
|
5970 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY, |
|
5971 EMBED_ERRTXT("Element %s was declared EMPTY this one has content\n"), |
|
5972 elem->name, NULL, NULL); |
|
5973 ret = 0; |
|
5974 } |
|
5975 break; |
|
5976 case XML_ELEMENT_TYPE_ANY: |
|
5977 /* I don't think anything is required then */ |
|
5978 break; |
|
5979 case XML_ELEMENT_TYPE_MIXED: |
|
5980 |
|
5981 /* simple case of declared as #PCDATA */ |
|
5982 if ((elemDecl->content != NULL) && |
|
5983 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) { |
|
5984 ret = xmlValidateOneCdataElement(ctxt, doc, elem); |
|
5985 if (!ret) { |
|
5986 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA, |
|
5987 EMBED_ERRTXT("Element %s was declared #PCDATA but contains non text nodes\n"), |
|
5988 elem->name, NULL, NULL); |
|
5989 } |
|
5990 break; |
|
5991 } |
|
5992 child = elem->children; |
|
5993 /* Hum, this start to get messy */ |
|
5994 while (child != NULL) { |
|
5995 if (child->type == XML_ELEMENT_NODE) { |
|
5996 name = child->name; |
|
5997 if ((child->ns != NULL) && (child->ns->prefix != NULL)) { |
|
5998 xmlChar fn[50]; |
|
5999 xmlChar *fullname; |
|
6000 |
|
6001 fullname = xmlBuildQName(child->name, child->ns->prefix, |
|
6002 fn, 50); |
|
6003 if (fullname == NULL) |
|
6004 return(0); |
|
6005 cont = elemDecl->content; |
|
6006 while (cont != NULL) { |
|
6007 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { |
|
6008 if (xmlStrEqual(cont->name, fullname)) |
|
6009 break; |
|
6010 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && |
|
6011 (cont->c1 != NULL) && |
|
6012 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ |
|
6013 if (xmlStrEqual(cont->c1->name, fullname)) |
|
6014 break; |
|
6015 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || |
|
6016 (cont->c1 == NULL) || |
|
6017 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ |
|
6018 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT, |
|
6019 EMBED_ERRTXT("Internal: MIXED struct corrupted\n"), |
|
6020 NULL); |
|
6021 break; |
|
6022 } |
|
6023 cont = cont->c2; |
|
6024 } |
|
6025 if ((fullname != fn) && (fullname != child->name)) |
|
6026 xmlFree(fullname); |
|
6027 if (cont != NULL) |
|
6028 goto child_ok; |
|
6029 } |
|
6030 cont = elemDecl->content; |
|
6031 while (cont != NULL) { |
|
6032 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { |
|
6033 if (xmlStrEqual(cont->name, name)) break; |
|
6034 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && |
|
6035 (cont->c1 != NULL) && |
|
6036 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) { |
|
6037 if (xmlStrEqual(cont->c1->name, name)) break; |
|
6038 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || |
|
6039 (cont->c1 == NULL) || |
|
6040 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) { |
|
6041 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT, |
|
6042 EMBED_ERRTXT("Internal: MIXED struct corrupted\n"), |
|
6043 NULL); |
|
6044 break; |
|
6045 } |
|
6046 cont = cont->c2; |
|
6047 } |
|
6048 if (cont == NULL) { |
|
6049 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD, |
|
6050 EMBED_ERRTXT("Element %s is not declared in %s list of possible children\n"), |
|
6051 name, elem->name, NULL); |
|
6052 ret = 0; |
|
6053 } |
|
6054 } |
|
6055 child_ok: |
|
6056 child = child->next; |
|
6057 } |
|
6058 break; |
|
6059 case XML_ELEMENT_TYPE_ELEMENT: |
|
6060 if ((doc->standalone == 1) && (extsubset == 1)) { |
|
6061 /* |
|
6062 * VC: Standalone Document Declaration |
|
6063 * - element types with element content, if white space |
|
6064 * occurs directly within any instance of those types. |
|
6065 */ |
|
6066 child = elem->children; |
|
6067 while (child != NULL) { |
|
6068 if (child->type == XML_TEXT_NODE) { |
|
6069 const xmlChar *content = child->content; |
|
6070 |
|
6071 while (IS_BLANK_CH(*content)) |
|
6072 content++; |
|
6073 if (*content == 0) { |
|
6074 xmlErrValidNode(ctxt, elem, |
|
6075 XML_DTD_STANDALONE_WHITE_SPACE, |
|
6076 EMBED_ERRTXT("standalone: %s declared in the external subset contains white spaces nodes\n"), |
|
6077 elem->name, NULL, NULL); |
|
6078 ret = 0; |
|
6079 break; |
|
6080 } |
|
6081 } |
|
6082 child =child->next; |
|
6083 } |
|
6084 } |
|
6085 child = elem->children; |
|
6086 cont = elemDecl->content; |
|
6087 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem); |
|
6088 if (tmp <= 0) |
|
6089 ret = tmp; |
|
6090 break; |
|
6091 } |
|
6092 } /* not continuous */ |
|
6093 |
|
6094 /* [ VC: Required Attribute ] */ |
|
6095 attr = elemDecl->attributes; |
|
6096 while (attr != NULL) { |
|
6097 if (attr->def == XML_ATTRIBUTE_REQUIRED) { |
|
6098 int qualified = -1; |
|
6099 |
|
6100 if ((attr->prefix == NULL) && |
|
6101 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) { |
|
6102 xmlNsPtr ns; |
|
6103 |
|
6104 ns = elem->nsDef; |
|
6105 while (ns != NULL) { |
|
6106 if (ns->prefix == NULL) |
|
6107 goto found; |
|
6108 ns = ns->next; |
|
6109 } |
|
6110 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) { |
|
6111 xmlNsPtr ns; |
|
6112 |
|
6113 ns = elem->nsDef; |
|
6114 while (ns != NULL) { |
|
6115 if (xmlStrEqual(attr->name, ns->prefix)) |
|
6116 goto found; |
|
6117 ns = ns->next; |
|
6118 } |
|
6119 } else { |
|
6120 xmlAttrPtr attrib; |
|
6121 |
|
6122 attrib = elem->properties; |
|
6123 while (attrib != NULL) { |
|
6124 if (xmlStrEqual(attrib->name, attr->name)) { |
|
6125 if (attr->prefix != NULL) { |
|
6126 xmlNsPtr nameSpace = attrib->ns; |
|
6127 |
|
6128 if (nameSpace == NULL) |
|
6129 nameSpace = elem->ns; |
|
6130 /* |
|
6131 * qualified names handling is problematic, having a |
|
6132 * different prefix should be possible but DTDs don't |
|
6133 * allow to define the URI instead of the prefix :-( |
|
6134 */ |
|
6135 if (nameSpace == NULL) { |
|
6136 if (qualified < 0) |
|
6137 qualified = 0; |
|
6138 } else if (!xmlStrEqual(nameSpace->prefix, |
|
6139 attr->prefix)) { |
|
6140 if (qualified < 1) |
|
6141 qualified = 1; |
|
6142 } else |
|
6143 goto found; |
|
6144 } else { |
|
6145 /* |
|
6146 * We should allow applications to define namespaces |
|
6147 * for their application even if the DTD doesn't |
|
6148 * carry one, otherwise, basically we would always |
|
6149 * break. |
|
6150 */ |
|
6151 goto found; |
|
6152 } |
|
6153 } |
|
6154 attrib = attrib->next; |
|
6155 } |
|
6156 } |
|
6157 if (qualified == -1) { |
|
6158 if (attr->prefix == NULL) { |
|
6159 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE, |
|
6160 EMBED_ERRTXT("Element %s does not carry attribute %s\n"), |
|
6161 elem->name, attr->name, NULL); |
|
6162 ret = 0; |
|
6163 } else { |
|
6164 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE, |
|
6165 EMBED_ERRTXT("Element %s does not carry attribute %s:%s\n"), |
|
6166 elem->name, attr->prefix,attr->name); |
|
6167 ret = 0; |
|
6168 } |
|
6169 } else if (qualified == 0) { |
|
6170 xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX, |
|
6171 EMBED_ERRTXT("Element %s required attribute %s:%s has no prefix\n"), |
|
6172 elem->name, attr->prefix, attr->name); |
|
6173 } else if (qualified == 1) { |
|
6174 xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX, |
|
6175 EMBED_ERRTXT("Element %s required attribute %s:%s has different prefix\n"), |
|
6176 elem->name, attr->prefix, attr->name); |
|
6177 } |
|
6178 } else if (attr->def == XML_ATTRIBUTE_FIXED) { |
|
6179 /* |
|
6180 * Special tests checking #FIXED namespace declarations |
|
6181 * have the right value since this is not done as an |
|
6182 * attribute checking |
|
6183 */ |
|
6184 if ((attr->prefix == NULL) && |
|
6185 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) { |
|
6186 xmlNsPtr ns; |
|
6187 |
|
6188 ns = elem->nsDef; |
|
6189 while (ns != NULL) { |
|
6190 if (ns->prefix == NULL) { |
|
6191 if (!xmlStrEqual(attr->defaultValue, ns->href)) { |
|
6192 xmlErrValidNode(ctxt, elem, |
|
6193 XML_DTD_ELEM_DEFAULT_NAMESPACE, |
|
6194 EMBED_ERRTXT("Element %s namespace name for default namespace does not match the DTD\n"), |
|
6195 elem->name, NULL, NULL); |
|
6196 ret = 0; |
|
6197 } |
|
6198 goto found; |
|
6199 } |
|
6200 ns = ns->next; |
|
6201 } |
|
6202 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) { |
|
6203 xmlNsPtr ns; |
|
6204 |
|
6205 ns = elem->nsDef; |
|
6206 while (ns != NULL) { |
|
6207 if (xmlStrEqual(attr->name, ns->prefix)) { |
|
6208 if (!xmlStrEqual(attr->defaultValue, ns->href)) { |
|
6209 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, |
|
6210 EMBED_ERRTXT("Element %s namespace name for %s does not match the DTD\n"), |
|
6211 elem->name, ns->prefix, NULL); |
|
6212 ret = 0; |
|
6213 } |
|
6214 goto found; |
|
6215 } |
|
6216 ns = ns->next; |
|
6217 } |
|
6218 } |
|
6219 } |
|
6220 found: |
|
6221 attr = attr->nexth; |
|
6222 } |
|
6223 return(ret); |
|
6224 } |
|
6225 |
|
6226 /** |
|
6227 * xmlValidateRoot: |
|
6228 * @param ctxt the validation context |
|
6229 * @param doc a document instance |
|
6230 * |
|
6231 * Try to validate a the root element |
|
6232 * basically it does the following check as described by the |
|
6233 * XML-1.0 recommendation: |
|
6234 * - [ VC: Root Element Type ] |
|
6235 * it doesn't try to recurse or apply other check to the element |
|
6236 * |
|
6237 * returns 1 if valid or 0 otherwise |
|
6238 */ |
|
6239 |
|
6240 int |
|
6241 xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { |
|
6242 xmlNodePtr root; |
|
6243 int ret; |
|
6244 |
|
6245 if (doc == NULL) return(0); |
|
6246 |
|
6247 root = xmlDocGetRootElement(doc); |
|
6248 if ((root == NULL) || (root->name == NULL)) { |
|
6249 xmlErrValid(ctxt, XML_DTD_NO_ROOT, |
|
6250 EMBED_ERRTXT("no root element\n"), NULL); |
|
6251 return(0); |
|
6252 } |
|
6253 |
|
6254 /* |
|
6255 * When doing post validation against a separate DTD, those may |
|
6256 * no internal subset has been generated |
|
6257 */ |
|
6258 if ((doc->intSubset != NULL) && |
|
6259 (doc->intSubset->name != NULL)) { |
|
6260 /* |
|
6261 * Check first the document root against the NQName |
|
6262 */ |
|
6263 if (!xmlStrEqual(doc->intSubset->name, root->name)) { |
|
6264 if ((root->ns != NULL) && (root->ns->prefix != NULL)) { |
|
6265 xmlChar fn[50]; |
|
6266 xmlChar *fullname; |
|
6267 |
|
6268 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50); |
|
6269 if (fullname == NULL) { |
|
6270 xmlVErrMemory(ctxt, NULL); |
|
6271 return(0); |
|
6272 } |
|
6273 ret = xmlStrEqual(doc->intSubset->name, fullname); |
|
6274 if ((fullname != fn) && (fullname != root->name)) |
|
6275 xmlFree(fullname); |
|
6276 if (ret == 1) |
|
6277 goto name_ok; |
|
6278 } |
|
6279 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) && |
|
6280 (xmlStrEqual(root->name, BAD_CAST "html"))) |
|
6281 goto name_ok; |
|
6282 xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME, |
|
6283 EMBED_ERRTXT("root and DTD name do not match '%s' and '%s'\n"), |
|
6284 root->name, doc->intSubset->name, NULL); |
|
6285 return(0); |
|
6286 } |
|
6287 } |
|
6288 name_ok: |
|
6289 return(1); |
|
6290 } |
|
6291 |
|
6292 |
|
6293 /** |
|
6294 * xmlValidateElement: |
|
6295 * @param ctxt the validation context |
|
6296 * @param doc a document instance |
|
6297 * @param elem an element instance |
|
6298 * |
|
6299 * Try to validate the subtree under an element |
|
6300 * |
|
6301 * returns 1 if valid or 0 otherwise |
|
6302 */ |
|
6303 |
|
6304 int |
|
6305 xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) { |
|
6306 xmlNodePtr child; |
|
6307 xmlAttrPtr attr; |
|
6308 xmlNsPtr ns; |
|
6309 const xmlChar *value; |
|
6310 int ret = 1; |
|
6311 |
|
6312 if (elem == NULL) return(0); |
|
6313 |
|
6314 /* |
|
6315 * XInclude elements were added after parsing in the infoset, |
|
6316 * they don't really mean anything validation wise. |
|
6317 */ |
|
6318 if ((elem->type == XML_XINCLUDE_START) || |
|
6319 (elem->type == XML_XINCLUDE_END)) |
|
6320 return(1); |
|
6321 |
|
6322 CHECK_DTD; |
|
6323 |
|
6324 /* |
|
6325 * Entities references have to be handled separately |
|
6326 */ |
|
6327 if (elem->type == XML_ENTITY_REF_NODE) { |
|
6328 return(1); |
|
6329 } |
|
6330 |
|
6331 ret &= xmlValidateOneElement(ctxt, doc, elem); |
|
6332 attr = elem->properties; |
|
6333 while (attr != NULL) { |
|
6334 value = xmlNodeListGetString(doc, attr->children, 0); |
|
6335 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value); |
|
6336 if (value != NULL) |
|
6337 xmlFree((char *)value); |
|
6338 attr= attr->next; |
|
6339 } |
|
6340 ns = elem->nsDef; |
|
6341 while (ns != NULL) { |
|
6342 if (elem->ns == NULL) |
|
6343 ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL, |
|
6344 ns, ns->href); |
|
6345 else |
|
6346 ret &= xmlValidateOneNamespace(ctxt, doc, elem, elem->ns->prefix, |
|
6347 ns, ns->href); |
|
6348 ns = ns->next; |
|
6349 } |
|
6350 child = elem->children; |
|
6351 while (child != NULL) { |
|
6352 ret &= xmlValidateElement(ctxt, doc, child); |
|
6353 child = child->next; |
|
6354 } |
|
6355 |
|
6356 return(ret); |
|
6357 } |
|
6358 |
|
6359 /** |
|
6360 * xmlValidateRef: |
|
6361 * @param ref A reference to be validated |
|
6362 * @param ctxt Validation context |
|
6363 * @param name Name of ID we are searching for |
|
6364 * |
|
6365 */ |
|
6366 static void |
|
6367 xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt, |
|
6368 const xmlChar *name) { |
|
6369 xmlAttrPtr id; |
|
6370 xmlAttrPtr attr; |
|
6371 |
|
6372 if (ref == NULL) |
|
6373 return; |
|
6374 if ((ref->attr == NULL) && (ref->name == NULL)) |
|
6375 return; |
|
6376 attr = ref->attr; |
|
6377 if (attr == NULL) { |
|
6378 xmlChar *dup, *str = NULL, *cur, save; |
|
6379 |
|
6380 dup = xmlStrdup(name); |
|
6381 if (dup == NULL) { |
|
6382 ctxt->valid = 0; |
|
6383 return; |
|
6384 } |
|
6385 cur = dup; |
|
6386 while (*cur != 0) { |
|
6387 str = cur; |
|
6388 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; |
|
6389 save = *cur; |
|
6390 *cur = 0; |
|
6391 id = xmlGetID(ctxt->doc, str); |
|
6392 if (id == NULL) { |
|
6393 xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID, |
|
6394 EMBED_ERRTXT("attribute %s line %d references an unknown ID \"%s\"\n"), |
|
6395 ref->name, |
|
6396 #ifdef LIBXML_ENABLE_NODE_LINEINFO |
|
6397 ref->lineno, |
|
6398 #else |
|
6399 0, // line numbers are not stored |
|
6400 #endif |
|
6401 str); |
|
6402 ctxt->valid = 0; |
|
6403 } |
|
6404 if (save == 0) |
|
6405 break; |
|
6406 *cur = save; |
|
6407 while (IS_BLANK_CH(*cur)) cur++; |
|
6408 } |
|
6409 xmlFree(dup); |
|
6410 } else if (attr->atype == XML_ATTRIBUTE_IDREF) { |
|
6411 id = xmlGetID(ctxt->doc, name); |
|
6412 if (id == NULL) { |
|
6413 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID, |
|
6414 EMBED_ERRTXT("IDREF attribute %s references an unknown ID \"%s\"\n"), |
|
6415 attr->name, name, NULL); |
|
6416 ctxt->valid = 0; |
|
6417 } |
|
6418 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) { |
|
6419 xmlChar *dup, *str = NULL, *cur, save; |
|
6420 |
|
6421 dup = xmlStrdup(name); |
|
6422 if (dup == NULL) { |
|
6423 xmlVErrMemory(ctxt, EMBED_ERRTXT("IDREFS split")); |
|
6424 ctxt->valid = 0; |
|
6425 return; |
|
6426 } |
|
6427 cur = dup; |
|
6428 while (*cur != 0) { |
|
6429 str = cur; |
|
6430 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; |
|
6431 save = *cur; |
|
6432 *cur = 0; |
|
6433 id = xmlGetID(ctxt->doc, str); |
|
6434 if (id == NULL) { |
|
6435 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID, |
|
6436 EMBED_ERRTXT("IDREFS attribute %s references an unknown ID \"%s\"\n"), |
|
6437 attr->name, str, NULL); |
|
6438 ctxt->valid = 0; |
|
6439 } |
|
6440 if (save == 0) |
|
6441 break; |
|
6442 *cur = save; |
|
6443 while (IS_BLANK_CH(*cur)) cur++; |
|
6444 } |
|
6445 xmlFree(dup); |
|
6446 } |
|
6447 } |
|
6448 |
|
6449 /** |
|
6450 * xmlWalkValidateList: |
|
6451 * @param data Contents of current link |
|
6452 * @param user Value supplied by the user |
|
6453 * |
|
6454 * Returns 0 to abort the walk or 1 to continue |
|
6455 */ |
|
6456 static int |
|
6457 xmlWalkValidateList(const void *data, const void *user) |
|
6458 { |
|
6459 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user; |
|
6460 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name); |
|
6461 return 1; |
|
6462 } |
|
6463 |
|
6464 /** |
|
6465 * xmlValidateCheckRefCallback: |
|
6466 * @param ref_list List of references |
|
6467 * @param ctxt Validation context |
|
6468 * @param name Name of ID we are searching for |
|
6469 * |
|
6470 */ |
|
6471 static void |
|
6472 xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt, |
|
6473 const xmlChar *name) { |
|
6474 xmlValidateMemo memo; |
|
6475 |
|
6476 if (ref_list == NULL) |
|
6477 return; |
|
6478 memo.ctxt = ctxt; |
|
6479 memo.name = name; |
|
6480 |
|
6481 xmlListWalk(ref_list, xmlWalkValidateList, &memo); |
|
6482 |
|
6483 } |
|
6484 |
|
6485 /** |
|
6486 * xmlValidateDocumentFinal: |
|
6487 * @param ctxt the validation context |
|
6488 * @param doc a document instance |
|
6489 * |
|
6490 * Does the final step for the document validation once all the |
|
6491 * incremental validation steps have been completed |
|
6492 * |
|
6493 * basically it does the following checks described by the XML Rec |
|
6494 * |
|
6495 * Check all the IDREF/IDREFS attributes definition for validity |
|
6496 * |
|
6497 * returns 1 if valid or 0 otherwise |
|
6498 */ |
|
6499 |
|
6500 int |
|
6501 xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { |
|
6502 xmlRefTablePtr table; |
|
6503 |
|
6504 if (doc == NULL) { |
|
6505 xmlErrValid(ctxt, XML_DTD_NO_DOC, |
|
6506 EMBED_ERRTXT("xmlValidateDocumentFinal: doc == NULL\n"), NULL); |
|
6507 return(0); |
|
6508 } |
|
6509 |
|
6510 /* |
|
6511 * Check all the NOTATION/NOTATIONS attributes |
|
6512 */ |
|
6513 /* |
|
6514 * Check all the ENTITY/ENTITIES attributes definition for validity |
|
6515 */ |
|
6516 /* |
|
6517 * Check all the IDREF/IDREFS attributes definition for validity |
|
6518 */ |
|
6519 table = (xmlRefTablePtr) doc->refs; |
|
6520 ctxt->doc = doc; |
|
6521 ctxt->valid = 1; |
|
6522 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt); |
|
6523 return(ctxt->valid); |
|
6524 } |
|
6525 |
|
6526 /** |
|
6527 * xmlValidateDtd: |
|
6528 * @param ctxt the validation context |
|
6529 * @param doc a document instance |
|
6530 * @param dtd a dtd instance |
|
6531 * |
|
6532 * Try to validate the document against the dtd instance |
|
6533 * |
|
6534 * basically it does check all the definitions in the DtD. |
|
6535 * |
|
6536 * returns 1 if valid or 0 otherwise |
|
6537 */ |
|
6538 |
|
6539 int |
|
6540 xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) { |
|
6541 int ret; |
|
6542 xmlDtdPtr oldExt; |
|
6543 xmlNodePtr root; |
|
6544 |
|
6545 if (dtd == NULL) return(0); |
|
6546 if (doc == NULL) return(0); |
|
6547 oldExt = doc->extSubset; |
|
6548 doc->extSubset = dtd; |
|
6549 ret = xmlValidateRoot(ctxt, doc); |
|
6550 if (ret == 0) { |
|
6551 doc->extSubset = oldExt; |
|
6552 return(ret); |
|
6553 } |
|
6554 if (doc->ids != NULL) { |
|
6555 xmlFreeIDTable(doc->ids); |
|
6556 doc->ids = NULL; |
|
6557 } |
|
6558 if (doc->refs != NULL) { |
|
6559 xmlFreeRefTable(doc->refs); |
|
6560 doc->refs = NULL; |
|
6561 } |
|
6562 root = xmlDocGetRootElement(doc); |
|
6563 ret = xmlValidateElement(ctxt, doc, root); |
|
6564 ret &= xmlValidateDocumentFinal(ctxt, doc); |
|
6565 doc->extSubset = oldExt; |
|
6566 return(ret); |
|
6567 } |
|
6568 |
|
6569 static void |
|
6570 xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt, |
|
6571 const xmlChar *name ATTRIBUTE_UNUSED) { |
|
6572 if (cur == NULL) |
|
6573 return; |
|
6574 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { |
|
6575 xmlChar *notation = cur->content; |
|
6576 |
|
6577 if (notation != NULL) { |
|
6578 int ret; |
|
6579 |
|
6580 ret = xmlValidateNotationUse(ctxt, cur->doc, notation); |
|
6581 if (ret != 1) { |
|
6582 ctxt->valid = 0; |
|
6583 } |
|
6584 } |
|
6585 } |
|
6586 } |
|
6587 |
|
6588 static void |
|
6589 xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt, |
|
6590 const xmlChar *name ATTRIBUTE_UNUSED) { |
|
6591 int ret; |
|
6592 xmlDocPtr doc; |
|
6593 xmlElementPtr elem = NULL; |
|
6594 |
|
6595 if (cur == NULL) |
|
6596 return; |
|
6597 switch (cur->atype) { |
|
6598 case XML_ATTRIBUTE_CDATA: |
|
6599 case XML_ATTRIBUTE_ID: |
|
6600 case XML_ATTRIBUTE_IDREF : |
|
6601 case XML_ATTRIBUTE_IDREFS: |
|
6602 case XML_ATTRIBUTE_NMTOKEN: |
|
6603 case XML_ATTRIBUTE_NMTOKENS: |
|
6604 case XML_ATTRIBUTE_ENUMERATION: |
|
6605 break; |
|
6606 case XML_ATTRIBUTE_ENTITY: |
|
6607 case XML_ATTRIBUTE_ENTITIES: |
|
6608 case XML_ATTRIBUTE_NOTATION: |
|
6609 if (cur->defaultValue != NULL) { |
|
6610 |
|
6611 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name, |
|
6612 cur->atype, cur->defaultValue); |
|
6613 if ((ret == 0) && (ctxt->valid == 1)) |
|
6614 ctxt->valid = 0; |
|
6615 } |
|
6616 if (cur->tree != NULL) { |
|
6617 xmlEnumerationPtr tree = cur->tree; |
|
6618 while (tree != NULL) { |
|
6619 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, |
|
6620 cur->name, cur->atype, tree->name); |
|
6621 if ((ret == 0) && (ctxt->valid == 1)) |
|
6622 ctxt->valid = 0; |
|
6623 tree = tree->next; |
|
6624 } |
|
6625 } |
|
6626 } |
|
6627 if (cur->atype == XML_ATTRIBUTE_NOTATION) { |
|
6628 doc = cur->doc; |
|
6629 if (cur->elem == NULL) { |
|
6630 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, |
|
6631 EMBED_ERRTXT("xmlValidateAttributeCallback(%s): internal error\n"), |
|
6632 (const char *) cur->name); |
|
6633 return; |
|
6634 } |
|
6635 |
|
6636 if (doc != NULL) |
|
6637 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem); |
|
6638 if ((elem == NULL) && (doc != NULL)) |
|
6639 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem); |
|
6640 if ((elem == NULL) && (cur->parent != NULL) && |
|
6641 (cur->parent->type == XML_DTD_NODE)) |
|
6642 elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem); |
|
6643 if (elem == NULL) { |
|
6644 xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM, |
|
6645 EMBED_ERRTXT("attribute %s: could not find decl for element %s\n"), |
|
6646 cur->name, cur->elem, NULL); |
|
6647 return; |
|
6648 } |
|
6649 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) { |
|
6650 xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION, |
|
6651 EMBED_ERRTXT("NOTATION attribute %s declared for EMPTY element %s\n"), |
|
6652 cur->name, cur->elem, NULL); |
|
6653 ctxt->valid = 0; |
|
6654 } |
|
6655 } |
|
6656 } |
|
6657 |
|
6658 /** |
|
6659 * xmlValidateDtdFinal: |
|
6660 * @param ctxt the validation context |
|
6661 * @param doc a document instance |
|
6662 * |
|
6663 * Does the final step for the dtds validation once all the |
|
6664 * subsets have been parsed |
|
6665 * |
|
6666 * basically it does the following checks described by the XML Rec |
|
6667 * - check that ENTITY and ENTITIES type attributes default or |
|
6668 * possible values matches one of the defined entities. |
|
6669 * - check that NOTATION type attributes default or |
|
6670 * possible values matches one of the defined notations. |
|
6671 * |
|
6672 * returns 1 if valid or 0 if invalid and -1 if not well-formed |
|
6673 */ |
|
6674 |
|
6675 int |
|
6676 xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { |
|
6677 xmlDtdPtr dtd; |
|
6678 xmlAttributeTablePtr table; |
|
6679 xmlEntitiesTablePtr entities; |
|
6680 |
|
6681 if (doc == NULL) return(0); |
|
6682 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) |
|
6683 return(0); |
|
6684 ctxt->doc = doc; |
|
6685 ctxt->valid = 1; |
|
6686 dtd = doc->intSubset; |
|
6687 if ((dtd != NULL) && (dtd->attributes != NULL)) { |
|
6688 table = (xmlAttributeTablePtr) dtd->attributes; |
|
6689 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt); |
|
6690 } |
|
6691 if ((dtd != NULL) && (dtd->entities != NULL)) { |
|
6692 entities = (xmlEntitiesTablePtr) dtd->entities; |
|
6693 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback, |
|
6694 ctxt); |
|
6695 } |
|
6696 dtd = doc->extSubset; |
|
6697 if ((dtd != NULL) && (dtd->attributes != NULL)) { |
|
6698 table = (xmlAttributeTablePtr) dtd->attributes; |
|
6699 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt); |
|
6700 } |
|
6701 if ((dtd != NULL) && (dtd->entities != NULL)) { |
|
6702 entities = (xmlEntitiesTablePtr) dtd->entities; |
|
6703 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback, |
|
6704 ctxt); |
|
6705 } |
|
6706 return(ctxt->valid); |
|
6707 } |
|
6708 |
|
6709 /** |
|
6710 * xmlValidateDocument: |
|
6711 * @param ctxt the validation context |
|
6712 * @param doc a document instance |
|
6713 * |
|
6714 * Try to validate the document instance |
|
6715 * |
|
6716 * basically it does the all the checks described by the XML Rec |
|
6717 * i.e. validates the internal and external subset (if present) |
|
6718 * and validate the document tree. |
|
6719 * |
|
6720 * returns 1 if valid or 0 otherwise |
|
6721 */ |
|
6722 |
|
6723 int |
|
6724 xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { |
|
6725 int ret; |
|
6726 xmlNodePtr root; |
|
6727 |
|
6728 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) { |
|
6729 xmlErrValid(ctxt, XML_DTD_NO_DTD, |
|
6730 EMBED_ERRTXT("no DTD found!\n"), NULL); |
|
6731 return(0); |
|
6732 } |
|
6733 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) || |
|
6734 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) { |
|
6735 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID, |
|
6736 doc->intSubset->SystemID); |
|
6737 if (doc->extSubset == NULL) { |
|
6738 if (doc->intSubset->SystemID != NULL) { |
|
6739 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, |
|
6740 EMBED_ERRTXT("Could not load the external subset \"%s\"\n"), |
|
6741 (const char *) doc->intSubset->SystemID); |
|
6742 } else { |
|
6743 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, |
|
6744 EMBED_ERRTXT("Could not load the external subset \"%s\"\n"), |
|
6745 (const char *) doc->intSubset->ExternalID); |
|
6746 } |
|
6747 return(0); |
|
6748 } |
|
6749 } |
|
6750 |
|
6751 if (doc->ids != NULL) { |
|
6752 xmlFreeIDTable(doc->ids); |
|
6753 doc->ids = NULL; |
|
6754 } |
|
6755 if (doc->refs != NULL) { |
|
6756 xmlFreeRefTable(doc->refs); |
|
6757 doc->refs = NULL; |
|
6758 } |
|
6759 ret = xmlValidateDtdFinal(ctxt, doc); |
|
6760 if (!xmlValidateRoot(ctxt, doc)) return(0); |
|
6761 |
|
6762 root = xmlDocGetRootElement(doc); |
|
6763 ret &= xmlValidateElement(ctxt, doc, root); |
|
6764 ret &= xmlValidateDocumentFinal(ctxt, doc); |
|
6765 return(ret); |
|
6766 } |
|
6767 |
|
6768 /************************************************************************ |
|
6769 * * |
|
6770 * Routines for dynamic validation editing * |
|
6771 * * |
|
6772 ************************************************************************/ |
|
6773 |
|
6774 /** |
|
6775 * xmlValidGetPotentialChildren: |
|
6776 * @param ctree an element content tree |
|
6777 * @param list an array to store the list of child names |
|
6778 * @param len a pointer to the number of element in the list |
|
6779 * @param max the size of the array |
|
6780 * |
|
6781 * Build/extend a list of potential children allowed by the content tree |
|
6782 * |
|
6783 * returns the number of element in the list, or -1 in case of error. |
|
6784 */ |
|
6785 |
|
6786 int |
|
6787 xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list, |
|
6788 int *len, int max) { |
|
6789 int i; |
|
6790 |
|
6791 if ((ctree == NULL) || (list == NULL) || (len == NULL)) |
|
6792 return(-1); |
|
6793 if (*len >= max) return(*len); |
|
6794 |
|
6795 switch (ctree->type) { |
|
6796 case XML_ELEMENT_CONTENT_PCDATA: |
|
6797 for (i = 0; i < *len;i++) |
|
6798 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len); |
|
6799 list[(*len)++] = BAD_CAST "#PCDATA"; |
|
6800 break; |
|
6801 case XML_ELEMENT_CONTENT_ELEMENT: |
|
6802 for (i = 0; i < *len;i++) |
|
6803 if (xmlStrEqual(ctree->name, list[i])) return(*len); |
|
6804 list[(*len)++] = ctree->name; |
|
6805 break; |
|
6806 case XML_ELEMENT_CONTENT_SEQ: |
|
6807 xmlValidGetPotentialChildren(ctree->c1, list, len, max); |
|
6808 xmlValidGetPotentialChildren(ctree->c2, list, len, max); |
|
6809 break; |
|
6810 case XML_ELEMENT_CONTENT_OR: |
|
6811 xmlValidGetPotentialChildren(ctree->c1, list, len, max); |
|
6812 xmlValidGetPotentialChildren(ctree->c2, list, len, max); |
|
6813 break; |
|
6814 } |
|
6815 |
|
6816 return(*len); |
|
6817 } |
|
6818 |
|
6819 /** |
|
6820 * xmlValidGetValidElements: |
|
6821 * @param prev an element to insert after |
|
6822 * @param next an element to insert next |
|
6823 * @param names an array to store the list of child names |
|
6824 * @param max the size of the array |
|
6825 * |
|
6826 * This function returns the list of authorized children to insert |
|
6827 * within an existing tree while respecting the validity constraints |
|
6828 * forced by the Dtd. The insertion point is defined using prev and |
|
6829 * next in the following ways: |
|
6830 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ... |
|
6831 * to insert next 'node': xmlValidGetValidElements(node, node->next, ... |
|
6832 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ... |
|
6833 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs, |
|
6834 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ... |
|
6835 * |
|
6836 * pointers to the element names are inserted at the beginning of the array |
|
6837 * and do not need to be freed. |
|
6838 * |
|
6839 * returns the number of element in the list, or -1 in case of error. If |
|
6840 * the function returns the value max the caller is invited to grow the |
|
6841 * receiving array and retry. |
|
6842 */ |
|
6843 |
|
6844 int |
|
6845 xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names, |
|
6846 int max) { |
|
6847 xmlValidCtxt vctxt; |
|
6848 int nb_valid_elements = 0; |
|
6849 const xmlChar *elements[256]; |
|
6850 int nb_elements = 0, i; |
|
6851 const xmlChar *name; |
|
6852 |
|
6853 xmlNode *ref_node; |
|
6854 xmlNode *parent; |
|
6855 xmlNode *test_node; |
|
6856 |
|
6857 xmlNode *prev_next; |
|
6858 xmlNode *next_prev; |
|
6859 xmlNode *parent_childs; |
|
6860 xmlNode *parent_last; |
|
6861 |
|
6862 xmlElement *element_desc; |
|
6863 |
|
6864 memset(&vctxt, 0, sizeof (xmlValidCtxt)); |
|
6865 |
|
6866 if (prev == NULL && next == NULL) |
|
6867 return(-1); |
|
6868 |
|
6869 if (names == NULL) return(-1); |
|
6870 if (max <= 0) return(-1); |
|
6871 |
|
6872 nb_valid_elements = 0; |
|
6873 ref_node = prev ? prev : next; |
|
6874 parent = ref_node->parent; |
|
6875 |
|
6876 /* |
|
6877 * Retrieves the parent element declaration |
|
6878 */ |
|
6879 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset, |
|
6880 parent->name); |
|
6881 if ((element_desc == NULL) && (parent->doc->extSubset != NULL)) |
|
6882 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset, |
|
6883 parent->name); |
|
6884 if (element_desc == NULL) return(-1); |
|
6885 |
|
6886 /* |
|
6887 * Do a backup of the current tree structure |
|
6888 */ |
|
6889 prev_next = prev ? prev->next : NULL; |
|
6890 next_prev = next ? next->prev : NULL; |
|
6891 parent_childs = parent->children; |
|
6892 parent_last = parent->last; |
|
6893 |
|
6894 /* |
|
6895 * Creates a dummy node and insert it into the tree |
|
6896 */ |
|
6897 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>"); |
|
6898 test_node->doc = ref_node->doc; |
|
6899 test_node->parent = parent; |
|
6900 test_node->prev = prev; |
|
6901 test_node->next = next; |
|
6902 name = test_node->name; |
|
6903 |
|
6904 if (prev) prev->next = test_node; |
|
6905 else parent->children = test_node; |
|
6906 |
|
6907 if (next) next->prev = test_node; |
|
6908 else parent->last = test_node; |
|
6909 |
|
6910 /* |
|
6911 * Insert each potential child node and check if the parent is |
|
6912 * still valid |
|
6913 */ |
|
6914 nb_elements = xmlValidGetPotentialChildren(element_desc->content, |
|
6915 elements, &nb_elements, 256); |
|
6916 |
|
6917 for (i = 0;i < nb_elements;i++) { |
|
6918 test_node->name = elements[i]; |
|
6919 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) { |
|
6920 int j; |
|
6921 |
|
6922 for (j = 0; j < nb_valid_elements;j++) |
|
6923 if (xmlStrEqual(elements[i], names[j])) break; |
|
6924 names[nb_valid_elements++] = elements[i]; |
|
6925 if (nb_valid_elements >= max) break; |
|
6926 } |
|
6927 } |
|
6928 |
|
6929 /* |
|
6930 * Restore the tree structure |
|
6931 */ |
|
6932 if (prev) prev->next = prev_next; |
|
6933 if (next) next->prev = next_prev; |
|
6934 parent->children = parent_childs; |
|
6935 parent->last = parent_last; |
|
6936 |
|
6937 /* |
|
6938 * Free up the dummy node |
|
6939 */ |
|
6940 test_node->name = name; |
|
6941 xmlFreeNode(test_node); |
|
6942 |
|
6943 return(nb_valid_elements); |
|
6944 } |
|
6945 #endif /* LIBXML_VALID_ENABLED */ |
|
6946 |