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