OLD | NEW |
| (Empty) |
1 /* | |
2 * variables.c: Implementation of the variable storage and lookup | |
3 * | |
4 * Reference: | |
5 * http://www.w3.org/TR/1999/REC-xslt-19991116 | |
6 * | |
7 * See Copyright for the status of this software. | |
8 * | |
9 * daniel@veillard.com | |
10 */ | |
11 | |
12 #define IN_LIBXSLT | |
13 #include "libxslt.h" | |
14 | |
15 #include <string.h> | |
16 | |
17 #include <libxml/xmlmemory.h> | |
18 #include <libxml/tree.h> | |
19 #include <libxml/valid.h> | |
20 #include <libxml/hash.h> | |
21 #include <libxml/xmlerror.h> | |
22 #include <libxml/xpath.h> | |
23 #include <libxml/xpathInternals.h> | |
24 #include <libxml/parserInternals.h> | |
25 #include <libxml/dict.h> | |
26 #include "xslt.h" | |
27 #include "xsltInternals.h" | |
28 #include "xsltutils.h" | |
29 #include "variables.h" | |
30 #include "transform.h" | |
31 #include "imports.h" | |
32 #include "preproc.h" | |
33 #include "keys.h" | |
34 | |
35 #ifdef WITH_XSLT_DEBUG | |
36 #define WITH_XSLT_DEBUG_VARIABLE | |
37 #endif | |
38 | |
39 #ifdef XSLT_REFACTORED | |
40 const xmlChar *xsltDocFragFake = (const xmlChar *) " fake node libxslt"; | |
41 #endif | |
42 | |
43 static const xmlChar *xsltComputingGlobalVarMarker = | |
44 (const xmlChar *) " var/param being computed"; | |
45 | |
46 #define XSLT_VAR_GLOBAL (1<<0) | |
47 #define XSLT_VAR_IN_SELECT (1<<1) | |
48 #define XSLT_TCTXT_VARIABLE(c) ((xsltStackElemPtr) (c)->contextVariable) | |
49 | |
50 /************************************************************************ | |
51 * * | |
52 * Result Value Tree (Result Tree Fragment) interfaces * | |
53 * * | |
54 ************************************************************************/ | |
55 /** | |
56 * xsltCreateRVT: | |
57 * @ctxt: an XSLT transformation context | |
58 * | |
59 * Creates a Result Value Tree | |
60 * (the XSLT 1.0 term for this is "Result Tree Fragment") | |
61 * | |
62 * Returns the result value tree or NULL in case of API or internal errors. | |
63 */ | |
64 xmlDocPtr | |
65 xsltCreateRVT(xsltTransformContextPtr ctxt) | |
66 { | |
67 xmlDocPtr container; | |
68 | |
69 /* | |
70 * Question: Why is this function public? | |
71 * Answer: It is called by the EXSLT module. | |
72 */ | |
73 if (ctxt == NULL) | |
74 return(NULL); | |
75 | |
76 /* | |
77 * Reuse a RTF from the cache if available. | |
78 */ | |
79 if (ctxt->cache->RVT) { | |
80 container = ctxt->cache->RVT; | |
81 ctxt->cache->RVT = (xmlDocPtr) container->next; | |
82 /* clear the internal pointers */ | |
83 container->next = NULL; | |
84 container->prev = NULL; | |
85 if (ctxt->cache->nbRVT > 0) | |
86 ctxt->cache->nbRVT--; | |
87 #ifdef XSLT_DEBUG_PROFILE_CACHE | |
88 ctxt->cache->dbgReusedRVTs++; | |
89 #endif | |
90 return(container); | |
91 } | |
92 | |
93 container = xmlNewDoc(NULL); | |
94 if (container == NULL) | |
95 return(NULL); | |
96 container->dict = ctxt->dict; | |
97 xmlDictReference(container->dict); | |
98 XSLT_MARK_RES_TREE_FRAG(container); | |
99 container->doc = container; | |
100 container->parent = NULL; | |
101 return(container); | |
102 } | |
103 | |
104 /** | |
105 * xsltRegisterTmpRVT: | |
106 * @ctxt: an XSLT transformation context | |
107 * @RVT: a result value tree (Result Tree Fragment) | |
108 * | |
109 * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment) | |
110 * in the garbage collector. | |
111 * The fragment will be freed at the exit of the currently | |
112 * instantiated xsl:template. | |
113 * Obsolete; this function might produce massive memory overhead, | |
114 * since the fragment is only freed when the current xsl:template | |
115 * exits. Use xsltRegisterLocalRVT() instead. | |
116 * | |
117 * Returns 0 in case of success and -1 in case of API or internal errors. | |
118 */ | |
119 int | |
120 xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) | |
121 { | |
122 if ((ctxt == NULL) || (RVT == NULL)) | |
123 return(-1); | |
124 | |
125 RVT->prev = NULL; | |
126 RVT->psvi = XSLT_RVT_VARIABLE; | |
127 | |
128 /* | |
129 * We'll restrict the lifetime of user-created fragments | |
130 * insinde an xsl:variable and xsl:param to the lifetime of the | |
131 * var/param itself. | |
132 */ | |
133 if (ctxt->contextVariable != NULL) { | |
134 RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; | |
135 XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; | |
136 return(0); | |
137 } | |
138 | |
139 RVT->next = (xmlNodePtr) ctxt->tmpRVT; | |
140 if (ctxt->tmpRVT != NULL) | |
141 ctxt->tmpRVT->prev = (xmlNodePtr) RVT; | |
142 ctxt->tmpRVT = RVT; | |
143 return(0); | |
144 } | |
145 | |
146 /** | |
147 * xsltRegisterLocalRVT: | |
148 * @ctxt: an XSLT transformation context | |
149 * @RVT: a result value tree (Result Tree Fragment; xmlDocPtr) | |
150 * | |
151 * Registers a result value tree (XSLT 1.0 term: Result Tree Fragment) | |
152 * in the RVT garbage collector. | |
153 * The fragment will be freed when the instruction which created the | |
154 * fragment exits. | |
155 * | |
156 * Returns 0 in case of success and -1 in case of API or internal errors. | |
157 */ | |
158 int | |
159 xsltRegisterLocalRVT(xsltTransformContextPtr ctxt, | |
160 xmlDocPtr RVT) | |
161 { | |
162 if ((ctxt == NULL) || (RVT == NULL)) | |
163 return(-1); | |
164 | |
165 RVT->prev = NULL; | |
166 | |
167 /* | |
168 * When evaluating "select" expressions of xsl:variable | |
169 * and xsl:param, we need to bind newly created tree fragments | |
170 * to the variable itself; otherwise the fragment will be | |
171 * freed before we leave the scope of a var. | |
172 */ | |
173 if ((ctxt->contextVariable != NULL) && | |
174 (XSLT_TCTXT_VARIABLE(ctxt)->flags & XSLT_VAR_IN_SELECT)) | |
175 { | |
176 RVT->psvi = XSLT_RVT_VARIABLE; | |
177 RVT->next = (xmlNodePtr) XSLT_TCTXT_VARIABLE(ctxt)->fragment; | |
178 XSLT_TCTXT_VARIABLE(ctxt)->fragment = RVT; | |
179 return(0); | |
180 } | |
181 /* | |
182 * Store the fragment in the scope of the current instruction. | |
183 * If not reference by a returning instruction (like EXSLT's function), | |
184 * then this fragment will be freed, when the instruction exits. | |
185 */ | |
186 RVT->psvi = XSLT_RVT_LOCAL; | |
187 RVT->next = (xmlNodePtr) ctxt->localRVT; | |
188 if (ctxt->localRVT != NULL) | |
189 ctxt->localRVT->prev = (xmlNodePtr) RVT; | |
190 ctxt->localRVT = RVT; | |
191 return(0); | |
192 } | |
193 | |
194 /** | |
195 * xsltExtensionInstructionResultFinalize: | |
196 * @ctxt: an XSLT transformation context | |
197 * | |
198 * Finalizes the data (e.g. result tree fragments) created | |
199 * within a value-returning process (e.g. EXSLT's function). | |
200 * Tree fragments marked as being returned by a function are | |
201 * set to normal state, which means that the fragment garbage | |
202 * collector will free them after the function-calling process exits. | |
203 * | |
204 * Returns 0 in case of success and -1 in case of API or internal errors. | |
205 * | |
206 * This function is unsupported in newer releases of libxslt. | |
207 */ | |
208 int | |
209 xsltExtensionInstructionResultFinalize(xsltTransformContextPtr ctxt) | |
210 { | |
211 xmlGenericError(xmlGenericErrorContext, | |
212 "xsltExtensionInstructionResultFinalize is unsupported " | |
213 "in this release of libxslt.\n"); | |
214 return(-1); | |
215 } | |
216 | |
217 /** | |
218 * xsltExtensionInstructionResultRegister: | |
219 * @ctxt: an XSLT transformation context | |
220 * @obj: an XPath object to be inspected for result tree fragments | |
221 * | |
222 * Marks the result of a value-returning extension instruction | |
223 * in order to avoid it being garbage collected before the | |
224 * extension instruction exits. | |
225 * Note that one still has to additionally register any newly created | |
226 * tree fragments (via xsltCreateRVT()) with xsltRegisterLocalRVT(). | |
227 * | |
228 * Returns 0 in case of success and -1 in case of error. | |
229 * | |
230 * It isn't necessary to call this function in newer releases of | |
231 * libxslt. | |
232 */ | |
233 int | |
234 xsltExtensionInstructionResultRegister(xsltTransformContextPtr ctxt, | |
235 xmlXPathObjectPtr obj) | |
236 { | |
237 return(0); | |
238 } | |
239 | |
240 /** | |
241 * xsltFlagRVTs: | |
242 * @ctxt: an XSLT transformation context | |
243 * @obj: an XPath object to be inspected for result tree fragments | |
244 * @val: the flag value | |
245 * | |
246 * Updates ownership information of RVTs in @obj according to @val. | |
247 * | |
248 * @val = XSLT_RVT_FUNC_RESULT for the result of an extension function, so its | |
249 * RVTs won't be destroyed after leaving the returning scope. | |
250 * @val = XSLT_RVT_LOCAL for the result of an extension function to reset | |
251 * the state of its RVTs after it was returned to a new scope. | |
252 * @val = XSLT_RVT_GLOBAL for parts of global variables. | |
253 * | |
254 * Returns 0 in case of success and -1 in case of error. | |
255 */ | |
256 int | |
257 xsltFlagRVTs(xsltTransformContextPtr ctxt, xmlXPathObjectPtr obj, void *val) { | |
258 int i; | |
259 xmlNodePtr cur; | |
260 xmlDocPtr doc; | |
261 | |
262 if ((ctxt == NULL) || (obj == NULL)) | |
263 return(-1); | |
264 | |
265 /* | |
266 * OPTIMIZE TODO: If no local variables/params and no local tree | |
267 * fragments were created, then we don't need to analyse the XPath | |
268 * objects for tree fragments. | |
269 */ | |
270 | |
271 if ((obj->type != XPATH_NODESET) && (obj->type != XPATH_XSLT_TREE)) | |
272 return(0); | |
273 if ((obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) | |
274 return(0); | |
275 | |
276 for (i = 0; i < obj->nodesetval->nodeNr; i++) { | |
277 cur = obj->nodesetval->nodeTab[i]; | |
278 if (cur->type == XML_NAMESPACE_DECL) { | |
279 /* | |
280 * The XPath module sets the owner element of a ns-node on | |
281 * the ns->next field. | |
282 */ | |
283 if ((((xmlNsPtr) cur)->next != NULL) && | |
284 (((xmlNsPtr) cur)->next->type == XML_ELEMENT_NODE)) | |
285 { | |
286 cur = (xmlNodePtr) ((xmlNsPtr) cur)->next; | |
287 doc = cur->doc; | |
288 } else { | |
289 xsltTransformError(ctxt, NULL, ctxt->inst, | |
290 "Internal error in xsltFlagRVTs(): " | |
291 "Cannot retrieve the doc of a namespace node.\n"); | |
292 return(-1); | |
293 } | |
294 } else { | |
295 doc = cur->doc; | |
296 } | |
297 if (doc == NULL) { | |
298 xsltTransformError(ctxt, NULL, ctxt->inst, | |
299 "Internal error in xsltFlagRVTs(): " | |
300 "Cannot retrieve the doc of a node.\n"); | |
301 return(-1); | |
302 } | |
303 if (doc->name && (doc->name[0] == ' ') && | |
304 doc->psvi != XSLT_RVT_GLOBAL) { | |
305 /* | |
306 * This is a result tree fragment. | |
307 * We store ownership information in the @psvi field. | |
308 * TODO: How do we know if this is a doc acquired via the | |
309 * document() function? | |
310 */ | |
311 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
312 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDeb
ugContext, | |
313 "Flagging RVT %p: %p -> %p\n", doc, doc->psvi, val)); | |
314 #endif | |
315 | |
316 if (val == XSLT_RVT_LOCAL) { | |
317 if (doc->psvi != XSLT_RVT_FUNC_RESULT) { | |
318 xmlGenericError(xmlGenericErrorContext, | |
319 "xsltFlagRVTs: Invalid transition %p => LOCAL\n", | |
320 doc->psvi); | |
321 return(-1); | |
322 } | |
323 | |
324 xsltRegisterLocalRVT(ctxt, doc); | |
325 } else if (val == XSLT_RVT_GLOBAL) { | |
326 if (doc->psvi != XSLT_RVT_LOCAL) { | |
327 xmlGenericError(xmlGenericErrorContext, | |
328 "xsltFlagRVTs: Invalid transition %p => GLOBAL\n", | |
329 doc->psvi); | |
330 doc->psvi = XSLT_RVT_GLOBAL; | |
331 return(-1); | |
332 } | |
333 | |
334 /* Will be registered as persistant in xsltReleaseLocalRVTs. */ | |
335 doc->psvi = XSLT_RVT_GLOBAL; | |
336 } else if (val == XSLT_RVT_FUNC_RESULT) { | |
337 doc->psvi = val; | |
338 } | |
339 } | |
340 } | |
341 | |
342 return(0); | |
343 } | |
344 | |
345 /** | |
346 * xsltReleaseRVT: | |
347 * @ctxt: an XSLT transformation context | |
348 * @RVT: a result value tree (Result Tree Fragment) | |
349 * | |
350 * Either frees the RVT (which is an xmlDoc) or stores | |
351 * it in the context's cache for later reuse. | |
352 */ | |
353 void | |
354 xsltReleaseRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) | |
355 { | |
356 if (RVT == NULL) | |
357 return; | |
358 | |
359 if (ctxt && (ctxt->cache->nbRVT < 40)) { | |
360 /* | |
361 * Store the Result Tree Fragment. | |
362 * Free the document info. | |
363 */ | |
364 if (RVT->_private != NULL) { | |
365 xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); | |
366 xmlFree(RVT->_private); | |
367 RVT->_private = NULL; | |
368 } | |
369 /* | |
370 * Clear the document tree. | |
371 * REVISIT TODO: Do we expect ID/IDREF tables to be existent? | |
372 */ | |
373 if (RVT->children != NULL) { | |
374 xmlFreeNodeList(RVT->children); | |
375 RVT->children = NULL; | |
376 RVT->last = NULL; | |
377 } | |
378 if (RVT->ids != NULL) { | |
379 xmlFreeIDTable((xmlIDTablePtr) RVT->ids); | |
380 RVT->ids = NULL; | |
381 } | |
382 if (RVT->refs != NULL) { | |
383 xmlFreeRefTable((xmlRefTablePtr) RVT->refs); | |
384 RVT->refs = NULL; | |
385 } | |
386 | |
387 /* | |
388 * Reset the ownership information. | |
389 */ | |
390 RVT->psvi = NULL; | |
391 | |
392 RVT->next = (xmlNodePtr) ctxt->cache->RVT; | |
393 ctxt->cache->RVT = RVT; | |
394 | |
395 ctxt->cache->nbRVT++; | |
396 | |
397 #ifdef XSLT_DEBUG_PROFILE_CACHE | |
398 ctxt->cache->dbgCachedRVTs++; | |
399 #endif | |
400 return; | |
401 } | |
402 /* | |
403 * Free it. | |
404 */ | |
405 if (RVT->_private != NULL) { | |
406 xsltFreeDocumentKeys((xsltDocumentPtr) RVT->_private); | |
407 xmlFree(RVT->_private); | |
408 } | |
409 xmlFreeDoc(RVT); | |
410 } | |
411 | |
412 /** | |
413 * xsltRegisterPersistRVT: | |
414 * @ctxt: an XSLT transformation context | |
415 * @RVT: a result value tree (Result Tree Fragment) | |
416 * | |
417 * Register the result value tree (XSLT 1.0 term: Result Tree Fragment) | |
418 * in the fragment garbage collector. | |
419 * The fragment will be freed when the transformation context is | |
420 * freed. | |
421 * | |
422 * Returns 0 in case of success and -1 in case of error. | |
423 */ | |
424 int | |
425 xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT) | |
426 { | |
427 if ((ctxt == NULL) || (RVT == NULL)) return(-1); | |
428 | |
429 RVT->psvi = XSLT_RVT_GLOBAL; | |
430 RVT->prev = NULL; | |
431 RVT->next = (xmlNodePtr) ctxt->persistRVT; | |
432 if (ctxt->persistRVT != NULL) | |
433 ctxt->persistRVT->prev = (xmlNodePtr) RVT; | |
434 ctxt->persistRVT = RVT; | |
435 return(0); | |
436 } | |
437 | |
438 /** | |
439 * xsltFreeRVTs: | |
440 * @ctxt: an XSLT transformation context | |
441 * | |
442 * Frees all registered result value trees (Result Tree Fragments) | |
443 * of the transformation. Internal function; should not be called | |
444 * by user-code. | |
445 */ | |
446 void | |
447 xsltFreeRVTs(xsltTransformContextPtr ctxt) | |
448 { | |
449 xmlDocPtr cur, next; | |
450 | |
451 if (ctxt == NULL) | |
452 return; | |
453 /* | |
454 * Local fragments. | |
455 */ | |
456 cur = ctxt->localRVT; | |
457 while (cur != NULL) { | |
458 next = (xmlDocPtr) cur->next; | |
459 if (cur->_private != NULL) { | |
460 xsltFreeDocumentKeys(cur->_private); | |
461 xmlFree(cur->_private); | |
462 } | |
463 xmlFreeDoc(cur); | |
464 cur = next; | |
465 } | |
466 ctxt->localRVT = NULL; | |
467 /* | |
468 * User-created per-template fragments. | |
469 */ | |
470 cur = ctxt->tmpRVT; | |
471 while (cur != NULL) { | |
472 next = (xmlDocPtr) cur->next; | |
473 if (cur->_private != NULL) { | |
474 xsltFreeDocumentKeys(cur->_private); | |
475 xmlFree(cur->_private); | |
476 } | |
477 xmlFreeDoc(cur); | |
478 cur = next; | |
479 } | |
480 ctxt->tmpRVT = NULL; | |
481 /* | |
482 * Global fragments. | |
483 */ | |
484 cur = ctxt->persistRVT; | |
485 while (cur != NULL) { | |
486 next = (xmlDocPtr) cur->next; | |
487 if (cur->_private != NULL) { | |
488 xsltFreeDocumentKeys(cur->_private); | |
489 xmlFree(cur->_private); | |
490 } | |
491 xmlFreeDoc(cur); | |
492 cur = next; | |
493 } | |
494 ctxt->persistRVT = NULL; | |
495 } | |
496 | |
497 /************************************************************************ | |
498 * * | |
499 * Module interfaces * | |
500 * * | |
501 ************************************************************************/ | |
502 | |
503 /** | |
504 * xsltNewStackElem: | |
505 * | |
506 * Create a new XSLT ParserContext | |
507 * | |
508 * Returns the newly allocated xsltParserStackElem or NULL in case of error | |
509 */ | |
510 static xsltStackElemPtr | |
511 xsltNewStackElem(xsltTransformContextPtr ctxt) | |
512 { | |
513 xsltStackElemPtr ret; | |
514 /* | |
515 * Reuse a stack item from the cache if available. | |
516 */ | |
517 if (ctxt && ctxt->cache->stackItems) { | |
518 ret = ctxt->cache->stackItems; | |
519 ctxt->cache->stackItems = ret->next; | |
520 ret->next = NULL; | |
521 ctxt->cache->nbStackItems--; | |
522 #ifdef XSLT_DEBUG_PROFILE_CACHE | |
523 ctxt->cache->dbgReusedVars++; | |
524 #endif | |
525 return(ret); | |
526 } | |
527 ret = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); | |
528 if (ret == NULL) { | |
529 xsltTransformError(NULL, NULL, NULL, | |
530 "xsltNewStackElem : malloc failed\n"); | |
531 return(NULL); | |
532 } | |
533 memset(ret, 0, sizeof(xsltStackElem)); | |
534 ret->context = ctxt; | |
535 return(ret); | |
536 } | |
537 | |
538 /** | |
539 * xsltCopyStackElem: | |
540 * @elem: an XSLT stack element | |
541 * | |
542 * Makes a copy of the stack element | |
543 * | |
544 * Returns the copy of NULL | |
545 */ | |
546 static xsltStackElemPtr | |
547 xsltCopyStackElem(xsltStackElemPtr elem) { | |
548 xsltStackElemPtr cur; | |
549 | |
550 cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem)); | |
551 if (cur == NULL) { | |
552 xsltTransformError(NULL, NULL, NULL, | |
553 "xsltCopyStackElem : malloc failed\n"); | |
554 return(NULL); | |
555 } | |
556 memset(cur, 0, sizeof(xsltStackElem)); | |
557 cur->context = elem->context; | |
558 cur->name = elem->name; | |
559 cur->nameURI = elem->nameURI; | |
560 cur->select = elem->select; | |
561 cur->tree = elem->tree; | |
562 cur->comp = elem->comp; | |
563 return(cur); | |
564 } | |
565 | |
566 /** | |
567 * xsltFreeStackElem: | |
568 * @elem: an XSLT stack element | |
569 * | |
570 * Free up the memory allocated by @elem | |
571 */ | |
572 static void | |
573 xsltFreeStackElem(xsltStackElemPtr elem) { | |
574 if (elem == NULL) | |
575 return; | |
576 if (elem->value != NULL) | |
577 xmlXPathFreeObject(elem->value); | |
578 /* | |
579 * Release the list of temporary Result Tree Fragments. | |
580 */ | |
581 if (elem->context) { | |
582 xmlDocPtr cur; | |
583 | |
584 while (elem->fragment != NULL) { | |
585 cur = elem->fragment; | |
586 elem->fragment = (xmlDocPtr) cur->next; | |
587 | |
588 if (cur->psvi == XSLT_RVT_VARIABLE) { | |
589 xsltReleaseRVT((xsltTransformContextPtr) elem->context, | |
590 cur); | |
591 } else if (cur->psvi != XSLT_RVT_FUNC_RESULT) { | |
592 xmlGenericError(xmlGenericErrorContext, | |
593 "xsltFreeStackElem: Unexpected RVT flag %p\n", | |
594 cur->psvi); | |
595 } | |
596 } | |
597 } | |
598 /* | |
599 * Cache or free the variable structure. | |
600 */ | |
601 if (elem->context && (elem->context->cache->nbStackItems < 50)) { | |
602 /* | |
603 * Store the item in the cache. | |
604 */ | |
605 xsltTransformContextPtr ctxt = elem->context; | |
606 memset(elem, 0, sizeof(xsltStackElem)); | |
607 elem->context = ctxt; | |
608 elem->next = ctxt->cache->stackItems; | |
609 ctxt->cache->stackItems = elem; | |
610 ctxt->cache->nbStackItems++; | |
611 #ifdef XSLT_DEBUG_PROFILE_CACHE | |
612 ctxt->cache->dbgCachedVars++; | |
613 #endif | |
614 return; | |
615 } | |
616 xmlFree(elem); | |
617 } | |
618 | |
619 /** | |
620 * xsltFreeStackElemList: | |
621 * @elem: an XSLT stack element | |
622 * | |
623 * Free up the memory allocated by @elem | |
624 */ | |
625 void | |
626 xsltFreeStackElemList(xsltStackElemPtr elem) { | |
627 xsltStackElemPtr next; | |
628 | |
629 while (elem != NULL) { | |
630 next = elem->next; | |
631 xsltFreeStackElem(elem); | |
632 elem = next; | |
633 } | |
634 } | |
635 | |
636 /** | |
637 * xsltStackLookup: | |
638 * @ctxt: an XSLT transformation context | |
639 * @name: the local part of the name | |
640 * @nameURI: the URI part of the name | |
641 * | |
642 * Locate an element in the stack based on its name. | |
643 */ | |
644 #if 0 /* TODO: Those seem to have been used for debugging. */ | |
645 static int stack_addr = 0; | |
646 static int stack_cmp = 0; | |
647 #endif | |
648 | |
649 static xsltStackElemPtr | |
650 xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name, | |
651 const xmlChar *nameURI) { | |
652 int i; | |
653 xsltStackElemPtr cur; | |
654 | |
655 if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0)) | |
656 return(NULL); | |
657 | |
658 /* | |
659 * Do the lookup from the top of the stack, but | |
660 * don't use params being computed in a call-param | |
661 * First lookup expects the variable name and URI to | |
662 * come from the disctionnary and hence pointer comparison. | |
663 */ | |
664 for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { | |
665 cur = ctxt->varsTab[i-1]; | |
666 while (cur != NULL) { | |
667 if ((cur->name == name) && (cur->nameURI == nameURI)) { | |
668 #if 0 | |
669 stack_addr++; | |
670 #endif | |
671 return(cur); | |
672 } | |
673 cur = cur->next; | |
674 } | |
675 } | |
676 | |
677 /* | |
678 * Redo the lookup with interned string compares | |
679 * to avoid string compares. | |
680 */ | |
681 name = xmlDictLookup(ctxt->dict, name, -1); | |
682 if (nameURI != NULL) | |
683 nameURI = xmlDictLookup(ctxt->dict, nameURI, -1); | |
684 | |
685 for (i = ctxt->varsNr; i > ctxt->varsBase; i--) { | |
686 cur = ctxt->varsTab[i-1]; | |
687 while (cur != NULL) { | |
688 if ((cur->name == name) && (cur->nameURI == nameURI)) { | |
689 #if 0 | |
690 stack_cmp++; | |
691 #endif | |
692 return(cur); | |
693 } | |
694 cur = cur->next; | |
695 } | |
696 } | |
697 | |
698 return(NULL); | |
699 } | |
700 | |
701 #ifdef XSLT_REFACTORED | |
702 #else | |
703 | |
704 /** | |
705 * xsltCheckStackElem: | |
706 * @ctxt: xn XSLT transformation context | |
707 * @name: the variable name | |
708 * @nameURI: the variable namespace URI | |
709 * | |
710 * Checks whether a variable or param is already defined. | |
711 * | |
712 * URGENT TODO: Checks for redefinition of vars/params should be | |
713 * done only at compilation time. | |
714 * | |
715 * Returns 1 if variable is present, 2 if param is present, 3 if this | |
716 * is an inherited param, 0 if not found, -1 in case of failure. | |
717 */ | |
718 static int | |
719 xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name, | |
720 const xmlChar *nameURI) { | |
721 xsltStackElemPtr cur; | |
722 | |
723 if ((ctxt == NULL) || (name == NULL)) | |
724 return(-1); | |
725 | |
726 cur = xsltStackLookup(ctxt, name, nameURI); | |
727 if (cur == NULL) | |
728 return(0); | |
729 if (cur->comp != NULL) { | |
730 if (cur->comp->type == XSLT_FUNC_WITHPARAM) | |
731 return(3); | |
732 else if (cur->comp->type == XSLT_FUNC_PARAM) | |
733 return(2); | |
734 } | |
735 | |
736 return(1); | |
737 } | |
738 | |
739 #endif /* XSLT_REFACTORED */ | |
740 | |
741 /** | |
742 * xsltAddStackElem: | |
743 * @ctxt: xn XSLT transformation context | |
744 * @elem: a stack element | |
745 * | |
746 * Push an element (or list) onto the stack. | |
747 * In case of a list, each member will be pushed into | |
748 * a seperate slot; i.e. there's always 1 stack entry for | |
749 * 1 stack element. | |
750 * | |
751 * Returns 0 in case of success, -1 in case of failure. | |
752 */ | |
753 static int | |
754 xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) | |
755 { | |
756 if ((ctxt == NULL) || (elem == NULL)) | |
757 return(-1); | |
758 | |
759 do { | |
760 if (ctxt->varsMax == 0) { | |
761 ctxt->varsMax = 10; | |
762 ctxt->varsTab = | |
763 (xsltStackElemPtr *) xmlMalloc(ctxt->varsMax * | |
764 sizeof(ctxt->varsTab[0])); | |
765 if (ctxt->varsTab == NULL) { | |
766 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n"); | |
767 return (-1); | |
768 } | |
769 } | |
770 if (ctxt->varsNr >= ctxt->varsMax) { | |
771 ctxt->varsMax *= 2; | |
772 ctxt->varsTab = | |
773 (xsltStackElemPtr *) xmlRealloc(ctxt->varsTab, | |
774 ctxt->varsMax * | |
775 sizeof(ctxt->varsTab[0])); | |
776 if (ctxt->varsTab == NULL) { | |
777 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); | |
778 return (-1); | |
779 } | |
780 } | |
781 ctxt->varsTab[ctxt->varsNr++] = elem; | |
782 ctxt->vars = elem; | |
783 | |
784 elem = elem->next; | |
785 } while (elem != NULL); | |
786 | |
787 return(0); | |
788 } | |
789 | |
790 /** | |
791 * xsltAddStackElemList: | |
792 * @ctxt: xn XSLT transformation context | |
793 * @elems: a stack element list | |
794 * | |
795 * Push an element list onto the stack. | |
796 * | |
797 * Returns 0 in case of success, -1 in case of failure. | |
798 */ | |
799 int | |
800 xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) | |
801 { | |
802 return(xsltAddStackElem(ctxt, elems)); | |
803 } | |
804 | |
805 /************************************************************************ | |
806 * * | |
807 * Module interfaces * | |
808 * * | |
809 ************************************************************************/ | |
810 | |
811 /** | |
812 * xsltEvalVariable: | |
813 * @ctxt: the XSLT transformation context | |
814 * @variable: the variable or parameter item | |
815 * @comp: the compiled XSLT instruction | |
816 * | |
817 * Evaluate a variable value. | |
818 * | |
819 * Returns the XPath Object value or NULL in case of error | |
820 */ | |
821 static xmlXPathObjectPtr | |
822 xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr variable, | |
823 xsltStylePreCompPtr castedComp) | |
824 { | |
825 #ifdef XSLT_REFACTORED | |
826 xsltStyleItemVariablePtr comp = | |
827 (xsltStyleItemVariablePtr) castedComp; | |
828 #else | |
829 xsltStylePreCompPtr comp = castedComp; | |
830 #endif | |
831 xmlXPathObjectPtr result = NULL; | |
832 xmlNodePtr oldInst; | |
833 | |
834 if ((ctxt == NULL) || (variable == NULL)) | |
835 return(NULL); | |
836 | |
837 /* | |
838 * A variable or parameter are evaluated on demand; thus the | |
839 * context (of XSLT and XPath) need to be temporarily adjusted and | |
840 * restored on exit. | |
841 */ | |
842 oldInst = ctxt->inst; | |
843 | |
844 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
845 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
846 "Evaluating variable '%s'\n", variable->name)); | |
847 #endif | |
848 if (variable->select != NULL) { | |
849 xmlXPathCompExprPtr xpExpr = NULL; | |
850 xmlDocPtr oldXPDoc; | |
851 xmlNodePtr oldXPContextNode; | |
852 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; | |
853 xmlNsPtr *oldXPNamespaces; | |
854 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; | |
855 xsltStackElemPtr oldVar = ctxt->contextVariable; | |
856 | |
857 if ((comp != NULL) && (comp->comp != NULL)) { | |
858 xpExpr = comp->comp; | |
859 } else { | |
860 xpExpr = xmlXPathCompile(variable->select); | |
861 } | |
862 if (xpExpr == NULL) | |
863 return(NULL); | |
864 /* | |
865 * Save context states. | |
866 */ | |
867 oldXPDoc = xpctxt->doc; | |
868 oldXPContextNode = xpctxt->node; | |
869 oldXPProximityPosition = xpctxt->proximityPosition; | |
870 oldXPContextSize = xpctxt->contextSize; | |
871 oldXPNamespaces = xpctxt->namespaces; | |
872 oldXPNsNr = xpctxt->nsNr; | |
873 | |
874 xpctxt->node = ctxt->node; | |
875 /* | |
876 * OPTIMIZE TODO: Lame try to set the context doc. | |
877 * Get rid of this somehow in xpath.c. | |
878 */ | |
879 if ((ctxt->node->type != XML_NAMESPACE_DECL) && | |
880 ctxt->node->doc) | |
881 xpctxt->doc = ctxt->node->doc; | |
882 /* | |
883 * BUG TODO: The proximity position and the context size will | |
884 * potentially be wrong. | |
885 * Example: | |
886 * <xsl:template select="foo"> | |
887 * <xsl:variable name="pos" select="position()"/> | |
888 * <xsl:for-each select="bar"> | |
889 * <xsl:value-of select="$pos"/> | |
890 * </xsl:for-each> | |
891 * </xsl:template> | |
892 * Here the proximity position and context size are changed | |
893 * to the context of <xsl:for-each select="bar">, but | |
894 * the variable needs to be evaluated in the context of | |
895 * <xsl:template select="foo">. | |
896 */ | |
897 if (comp != NULL) { | |
898 | |
899 #ifdef XSLT_REFACTORED | |
900 if (comp->inScopeNs != NULL) { | |
901 xpctxt->namespaces = comp->inScopeNs->list; | |
902 xpctxt->nsNr = comp->inScopeNs->xpathNumber; | |
903 } else { | |
904 xpctxt->namespaces = NULL; | |
905 xpctxt->nsNr = 0; | |
906 } | |
907 #else | |
908 xpctxt->namespaces = comp->nsList; | |
909 xpctxt->nsNr = comp->nsNr; | |
910 #endif | |
911 } else { | |
912 xpctxt->namespaces = NULL; | |
913 xpctxt->nsNr = 0; | |
914 } | |
915 | |
916 /* | |
917 * We need to mark that we are "selecting" a var's value; | |
918 * if any tree fragments are created inside the expression, | |
919 * then those need to be stored inside the variable; otherwise | |
920 * we'll eventually free still referenced fragments, before | |
921 * we leave the scope of the variable. | |
922 */ | |
923 ctxt->contextVariable = variable; | |
924 variable->flags |= XSLT_VAR_IN_SELECT; | |
925 | |
926 result = xmlXPathCompiledEval(xpExpr, xpctxt); | |
927 | |
928 variable->flags ^= XSLT_VAR_IN_SELECT; | |
929 /* | |
930 * Restore Context states. | |
931 */ | |
932 ctxt->contextVariable = oldVar; | |
933 | |
934 xpctxt->doc = oldXPDoc; | |
935 xpctxt->node = oldXPContextNode; | |
936 xpctxt->contextSize = oldXPContextSize; | |
937 xpctxt->proximityPosition = oldXPProximityPosition; | |
938 xpctxt->namespaces = oldXPNamespaces; | |
939 xpctxt->nsNr = oldXPNsNr; | |
940 | |
941 if ((comp == NULL) || (comp->comp == NULL)) | |
942 xmlXPathFreeCompExpr(xpExpr); | |
943 if (result == NULL) { | |
944 xsltTransformError(ctxt, NULL, | |
945 (comp != NULL) ? comp->inst : NULL, | |
946 "Failed to evaluate the expression of variable '%s'.\n", | |
947 variable->name); | |
948 ctxt->state = XSLT_STATE_STOPPED; | |
949 | |
950 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
951 #ifdef LIBXML_DEBUG_ENABLED | |
952 } else { | |
953 if ((xsltGenericDebugContext == stdout) || | |
954 (xsltGenericDebugContext == stderr)) | |
955 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, | |
956 result, 0); | |
957 #endif | |
958 #endif | |
959 } | |
960 } else { | |
961 if (variable->tree == NULL) { | |
962 result = xmlXPathNewCString(""); | |
963 } else { | |
964 if (variable->tree) { | |
965 xmlDocPtr container; | |
966 xmlNodePtr oldInsert; | |
967 xmlDocPtr oldOutput; | |
968 xsltStackElemPtr oldVar = ctxt->contextVariable; | |
969 | |
970 /* | |
971 * Generate a result tree fragment. | |
972 */ | |
973 container = xsltCreateRVT(ctxt); | |
974 if (container == NULL) | |
975 goto error; | |
976 /* | |
977 * NOTE: Local Result Tree Fragments of params/variables | |
978 * are not registered globally anymore; the life-time | |
979 * is not directly dependant of the param/variable itself. | |
980 * | |
981 * OLD: xsltRegisterTmpRVT(ctxt, container); | |
982 */ | |
983 /* | |
984 * Attach the Result Tree Fragment to the variable; | |
985 * when the variable is freed, it will also free | |
986 * the Result Tree Fragment. | |
987 */ | |
988 variable->fragment = container; | |
989 container->psvi = XSLT_RVT_VARIABLE; | |
990 | |
991 oldOutput = ctxt->output; | |
992 oldInsert = ctxt->insert; | |
993 | |
994 ctxt->output = container; | |
995 ctxt->insert = (xmlNodePtr) container; | |
996 ctxt->contextVariable = variable; | |
997 /* | |
998 * Process the sequence constructor (variable->tree). | |
999 * The resulting tree will be held by @container. | |
1000 */ | |
1001 xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree, | |
1002 NULL, NULL); | |
1003 | |
1004 ctxt->contextVariable = oldVar; | |
1005 ctxt->insert = oldInsert; | |
1006 ctxt->output = oldOutput; | |
1007 | |
1008 result = xmlXPathNewValueTree((xmlNodePtr) container); | |
1009 } | |
1010 if (result == NULL) { | |
1011 result = xmlXPathNewCString(""); | |
1012 } else { | |
1013 /* | |
1014 * Freeing is not handled there anymore. | |
1015 * QUESTION TODO: What does the above comment mean? | |
1016 */ | |
1017 result->boolval = 0; | |
1018 } | |
1019 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1020 #ifdef LIBXML_DEBUG_ENABLED | |
1021 | |
1022 if ((xsltGenericDebugContext == stdout) || | |
1023 (xsltGenericDebugContext == stderr)) | |
1024 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, | |
1025 result, 0); | |
1026 #endif | |
1027 #endif | |
1028 } | |
1029 } | |
1030 | |
1031 error: | |
1032 ctxt->inst = oldInst; | |
1033 return(result); | |
1034 } | |
1035 | |
1036 /** | |
1037 * xsltEvalGlobalVariable: | |
1038 * @elem: the variable or parameter | |
1039 * @ctxt: the XSLT transformation context | |
1040 * | |
1041 * Evaluates a the value of a global xsl:variable or | |
1042 * xsl:param declaration. | |
1043 * | |
1044 * Returns the XPath Object value or NULL in case of error | |
1045 */ | |
1046 static xmlXPathObjectPtr | |
1047 xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) | |
1048 { | |
1049 xmlXPathObjectPtr result = NULL; | |
1050 xmlNodePtr oldInst; | |
1051 const xmlChar* oldVarName; | |
1052 | |
1053 #ifdef XSLT_REFACTORED | |
1054 xsltStyleBasicItemVariablePtr comp; | |
1055 #else | |
1056 xsltStylePreCompPtr comp; | |
1057 #endif | |
1058 | |
1059 if ((ctxt == NULL) || (elem == NULL)) | |
1060 return(NULL); | |
1061 if (elem->computed) | |
1062 return(elem->value); | |
1063 | |
1064 | |
1065 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1066 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
1067 "Evaluating global variable %s\n", elem->name)); | |
1068 #endif | |
1069 | |
1070 #ifdef WITH_DEBUGGER | |
1071 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) && | |
1072 elem->comp && elem->comp->inst) | |
1073 xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt); | |
1074 #endif | |
1075 | |
1076 oldInst = ctxt->inst; | |
1077 #ifdef XSLT_REFACTORED | |
1078 comp = (xsltStyleBasicItemVariablePtr) elem->comp; | |
1079 #else | |
1080 comp = elem->comp; | |
1081 #endif | |
1082 oldVarName = elem->name; | |
1083 elem->name = xsltComputingGlobalVarMarker; | |
1084 /* | |
1085 * OPTIMIZE TODO: We should consider instantiating global vars/params | |
1086 * on-demand. The vars/params don't need to be evaluated if never | |
1087 * called; and in the case of global params, if values for such params | |
1088 * are provided by the user. | |
1089 */ | |
1090 if (elem->select != NULL) { | |
1091 xmlXPathCompExprPtr xpExpr = NULL; | |
1092 xmlDocPtr oldXPDoc; | |
1093 xmlNodePtr oldXPContextNode; | |
1094 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; | |
1095 xmlNsPtr *oldXPNamespaces; | |
1096 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; | |
1097 | |
1098 if ((comp != NULL) && (comp->comp != NULL)) { | |
1099 xpExpr = comp->comp; | |
1100 } else { | |
1101 xpExpr = xmlXPathCompile(elem->select); | |
1102 } | |
1103 if (xpExpr == NULL) | |
1104 goto error; | |
1105 | |
1106 | |
1107 if (comp != NULL) | |
1108 ctxt->inst = comp->inst; | |
1109 else | |
1110 ctxt->inst = NULL; | |
1111 /* | |
1112 * SPEC XSLT 1.0: | |
1113 * "At top-level, the expression or template specifying the | |
1114 * variable value is evaluated with the same context as that used | |
1115 * to process the root node of the source document: the current | |
1116 * node is the root node of the source document and the current | |
1117 * node list is a list containing just the root node of the source | |
1118 * document." | |
1119 */ | |
1120 /* | |
1121 * Save context states. | |
1122 */ | |
1123 oldXPDoc = xpctxt->doc; | |
1124 oldXPContextNode = xpctxt->node; | |
1125 oldXPProximityPosition = xpctxt->proximityPosition; | |
1126 oldXPContextSize = xpctxt->contextSize; | |
1127 oldXPNamespaces = xpctxt->namespaces; | |
1128 oldXPNsNr = xpctxt->nsNr; | |
1129 | |
1130 xpctxt->node = ctxt->initialContextNode; | |
1131 xpctxt->doc = ctxt->initialContextDoc; | |
1132 xpctxt->contextSize = 1; | |
1133 xpctxt->proximityPosition = 1; | |
1134 | |
1135 if (comp != NULL) { | |
1136 | |
1137 #ifdef XSLT_REFACTORED | |
1138 if (comp->inScopeNs != NULL) { | |
1139 xpctxt->namespaces = comp->inScopeNs->list; | |
1140 xpctxt->nsNr = comp->inScopeNs->xpathNumber; | |
1141 } else { | |
1142 xpctxt->namespaces = NULL; | |
1143 xpctxt->nsNr = 0; | |
1144 } | |
1145 #else | |
1146 xpctxt->namespaces = comp->nsList; | |
1147 xpctxt->nsNr = comp->nsNr; | |
1148 #endif | |
1149 } else { | |
1150 xpctxt->namespaces = NULL; | |
1151 xpctxt->nsNr = 0; | |
1152 } | |
1153 | |
1154 result = xmlXPathCompiledEval(xpExpr, xpctxt); | |
1155 | |
1156 /* | |
1157 * Restore Context states. | |
1158 */ | |
1159 xpctxt->doc = oldXPDoc; | |
1160 xpctxt->node = oldXPContextNode; | |
1161 xpctxt->contextSize = oldXPContextSize; | |
1162 xpctxt->proximityPosition = oldXPProximityPosition; | |
1163 xpctxt->namespaces = oldXPNamespaces; | |
1164 xpctxt->nsNr = oldXPNsNr; | |
1165 | |
1166 if ((comp == NULL) || (comp->comp == NULL)) | |
1167 xmlXPathFreeCompExpr(xpExpr); | |
1168 if (result == NULL) { | |
1169 if (comp == NULL) | |
1170 xsltTransformError(ctxt, NULL, NULL, | |
1171 "Evaluating global variable %s failed\n", elem->name); | |
1172 else | |
1173 xsltTransformError(ctxt, NULL, comp->inst, | |
1174 "Evaluating global variable %s failed\n", elem->name); | |
1175 ctxt->state = XSLT_STATE_STOPPED; | |
1176 goto error; | |
1177 } | |
1178 | |
1179 /* | |
1180 * Mark all RVTs that are referenced from result as part | |
1181 * of this variable so they won't be freed too early. | |
1182 */ | |
1183 xsltFlagRVTs(ctxt, result, XSLT_RVT_GLOBAL); | |
1184 | |
1185 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1186 #ifdef LIBXML_DEBUG_ENABLED | |
1187 if ((xsltGenericDebugContext == stdout) || | |
1188 (xsltGenericDebugContext == stderr)) | |
1189 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, | |
1190 result, 0); | |
1191 #endif | |
1192 #endif | |
1193 } else { | |
1194 if (elem->tree == NULL) { | |
1195 result = xmlXPathNewCString(""); | |
1196 } else { | |
1197 xmlDocPtr container; | |
1198 xmlNodePtr oldInsert; | |
1199 xmlDocPtr oldOutput, oldXPDoc; | |
1200 /* | |
1201 * Generate a result tree fragment. | |
1202 */ | |
1203 container = xsltCreateRVT(ctxt); | |
1204 if (container == NULL) | |
1205 goto error; | |
1206 /* | |
1207 * Let the lifetime of the tree fragment be handled by | |
1208 * the Libxslt's garbage collector. | |
1209 */ | |
1210 xsltRegisterPersistRVT(ctxt, container); | |
1211 | |
1212 oldOutput = ctxt->output; | |
1213 oldInsert = ctxt->insert; | |
1214 | |
1215 oldXPDoc = ctxt->xpathCtxt->doc; | |
1216 | |
1217 ctxt->output = container; | |
1218 ctxt->insert = (xmlNodePtr) container; | |
1219 | |
1220 ctxt->xpathCtxt->doc = ctxt->initialContextDoc; | |
1221 /* | |
1222 * Process the sequence constructor. | |
1223 */ | |
1224 xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL); | |
1225 | |
1226 ctxt->xpathCtxt->doc = oldXPDoc; | |
1227 | |
1228 ctxt->insert = oldInsert; | |
1229 ctxt->output = oldOutput; | |
1230 | |
1231 result = xmlXPathNewValueTree((xmlNodePtr) container); | |
1232 if (result == NULL) { | |
1233 result = xmlXPathNewCString(""); | |
1234 } else { | |
1235 result->boolval = 0; /* Freeing is not handled there anymore */ | |
1236 } | |
1237 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1238 #ifdef LIBXML_DEBUG_ENABLED | |
1239 if ((xsltGenericDebugContext == stdout) || | |
1240 (xsltGenericDebugContext == stderr)) | |
1241 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, | |
1242 result, 0); | |
1243 #endif | |
1244 #endif | |
1245 } | |
1246 } | |
1247 | |
1248 error: | |
1249 elem->name = oldVarName; | |
1250 ctxt->inst = oldInst; | |
1251 if (result != NULL) { | |
1252 elem->value = result; | |
1253 elem->computed = 1; | |
1254 } | |
1255 return(result); | |
1256 } | |
1257 | |
1258 /** | |
1259 * xsltEvalGlobalVariables: | |
1260 * @ctxt: the XSLT transformation context | |
1261 * | |
1262 * Evaluates all global variables and parameters of a stylesheet. | |
1263 * For internal use only. This is called at start of a transformation. | |
1264 * | |
1265 * Returns 0 in case of success, -1 in case of error | |
1266 */ | |
1267 int | |
1268 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) { | |
1269 xsltStackElemPtr elem; | |
1270 xsltStylesheetPtr style; | |
1271 | |
1272 if ((ctxt == NULL) || (ctxt->document == NULL)) | |
1273 return(-1); | |
1274 | |
1275 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1276 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
1277 "Registering global variables\n")); | |
1278 #endif | |
1279 /* | |
1280 * Walk the list from the stylesheets and populate the hash table | |
1281 */ | |
1282 style = ctxt->style; | |
1283 while (style != NULL) { | |
1284 elem = style->variables; | |
1285 | |
1286 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1287 if ((style->doc != NULL) && (style->doc->URL != NULL)) { | |
1288 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDeb
ugContext, | |
1289 "Registering global variables from %s\n", | |
1290 style->doc->URL)); | |
1291 } | |
1292 #endif | |
1293 | |
1294 while (elem != NULL) { | |
1295 xsltStackElemPtr def; | |
1296 | |
1297 /* | |
1298 * Global variables are stored in the variables pool. | |
1299 */ | |
1300 def = (xsltStackElemPtr) | |
1301 xmlHashLookup2(ctxt->globalVars, | |
1302 elem->name, elem->nameURI); | |
1303 if (def == NULL) { | |
1304 | |
1305 def = xsltCopyStackElem(elem); | |
1306 xmlHashAddEntry2(ctxt->globalVars, | |
1307 elem->name, elem->nameURI, def); | |
1308 } else if ((elem->comp != NULL) && | |
1309 (elem->comp->type == XSLT_FUNC_VARIABLE)) { | |
1310 /* | |
1311 * Redefinition of variables from a different stylesheet | |
1312 * should not generate a message. | |
1313 */ | |
1314 if ((elem->comp->inst != NULL) && | |
1315 (def->comp != NULL) && (def->comp->inst != NULL) && | |
1316 (elem->comp->inst->doc == def->comp->inst->doc)) | |
1317 { | |
1318 xsltTransformError(ctxt, style, elem->comp->inst, | |
1319 "Global variable %s already defined\n", elem->name); | |
1320 if (style != NULL) style->errors++; | |
1321 } | |
1322 } | |
1323 elem = elem->next; | |
1324 } | |
1325 | |
1326 style = xsltNextImport(style); | |
1327 } | |
1328 | |
1329 /* | |
1330 * This part does the actual evaluation | |
1331 */ | |
1332 xmlHashScan(ctxt->globalVars, | |
1333 (xmlHashScanner) xsltEvalGlobalVariable, ctxt); | |
1334 | |
1335 return(0); | |
1336 } | |
1337 | |
1338 /** | |
1339 * xsltRegisterGlobalVariable: | |
1340 * @style: the XSLT transformation context | |
1341 * @name: the variable name | |
1342 * @ns_uri: the variable namespace URI | |
1343 * @sel: the expression which need to be evaluated to generate a value | |
1344 * @tree: the subtree if sel is NULL | |
1345 * @comp: the precompiled value | |
1346 * @value: the string value if available | |
1347 * | |
1348 * Register a new variable value. If @value is NULL it unregisters | |
1349 * the variable | |
1350 * | |
1351 * Returns 0 in case of success, -1 in case of error | |
1352 */ | |
1353 static int | |
1354 xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name, | |
1355 const xmlChar *ns_uri, const xmlChar *sel, | |
1356 xmlNodePtr tree, xsltStylePreCompPtr comp, | |
1357 const xmlChar *value) { | |
1358 xsltStackElemPtr elem, tmp; | |
1359 if (style == NULL) | |
1360 return(-1); | |
1361 if (name == NULL) | |
1362 return(-1); | |
1363 if (comp == NULL) | |
1364 return(-1); | |
1365 | |
1366 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1367 if (comp->type == XSLT_FUNC_PARAM) | |
1368 xsltGenericDebug(xsltGenericDebugContext, | |
1369 "Defining global param %s\n", name); | |
1370 else | |
1371 xsltGenericDebug(xsltGenericDebugContext, | |
1372 "Defining global variable %s\n", name); | |
1373 #endif | |
1374 | |
1375 elem = xsltNewStackElem(NULL); | |
1376 if (elem == NULL) | |
1377 return(-1); | |
1378 elem->comp = comp; | |
1379 elem->name = xmlDictLookup(style->dict, name, -1); | |
1380 elem->select = xmlDictLookup(style->dict, sel, -1); | |
1381 if (ns_uri) | |
1382 elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1); | |
1383 elem->tree = tree; | |
1384 tmp = style->variables; | |
1385 if (tmp == NULL) { | |
1386 elem->next = NULL; | |
1387 style->variables = elem; | |
1388 } else { | |
1389 while (tmp != NULL) { | |
1390 if ((elem->comp->type == XSLT_FUNC_VARIABLE) && | |
1391 (tmp->comp->type == XSLT_FUNC_VARIABLE) && | |
1392 (xmlStrEqual(elem->name, tmp->name)) && | |
1393 ((elem->nameURI == tmp->nameURI) || | |
1394 (xmlStrEqual(elem->nameURI, tmp->nameURI)))) | |
1395 { | |
1396 xsltTransformError(NULL, style, comp->inst, | |
1397 "redefinition of global variable %s\n", elem->name); | |
1398 style->errors++; | |
1399 } | |
1400 if (tmp->next == NULL) | |
1401 break; | |
1402 tmp = tmp->next; | |
1403 } | |
1404 elem->next = NULL; | |
1405 tmp->next = elem; | |
1406 } | |
1407 if (value != NULL) { | |
1408 elem->computed = 1; | |
1409 elem->value = xmlXPathNewString(value); | |
1410 } | |
1411 return(0); | |
1412 } | |
1413 | |
1414 /** | |
1415 * xsltProcessUserParamInternal | |
1416 * | |
1417 * @ctxt: the XSLT transformation context | |
1418 * @name: a null terminated parameter name | |
1419 * @value: a null terminated value (may be an XPath expression) | |
1420 * @eval: 0 to treat the value literally, else evaluate as XPath expression | |
1421 * | |
1422 * If @eval is 0 then @value is treated literally and is stored in the global | |
1423 * parameter/variable table without any change. | |
1424 * | |
1425 * Uf @eval is 1 then @value is treated as an XPath expression and is | |
1426 * evaluated. In this case, if you want to pass a string which will be | |
1427 * interpreted literally then it must be enclosed in single or double quotes. | |
1428 * If the string contains single quotes (double quotes) then it cannot be | |
1429 * enclosed single quotes (double quotes). If the string which you want to | |
1430 * be treated literally contains both single and double quotes (e.g. Meet | |
1431 * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable | |
1432 * quoting character. You cannot use ' or " inside the string | |
1433 * because the replacement of character entities with their equivalents is | |
1434 * done at a different stage of processing. The solution is to call | |
1435 * xsltQuoteUserParams or xsltQuoteOneUserParam. | |
1436 * | |
1437 * This needs to be done on parsed stylesheets before starting to apply | |
1438 * transformations. Normally this will be called (directly or indirectly) | |
1439 * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams, | |
1440 * or xsltQuoteOneUserParam. | |
1441 * | |
1442 * Returns 0 in case of success, -1 in case of error | |
1443 */ | |
1444 | |
1445 static | |
1446 int | |
1447 xsltProcessUserParamInternal(xsltTransformContextPtr ctxt, | |
1448 const xmlChar * name, | |
1449 const xmlChar * value, | |
1450 int eval) { | |
1451 | |
1452 xsltStylesheetPtr style; | |
1453 const xmlChar *prefix; | |
1454 const xmlChar *href; | |
1455 xmlXPathCompExprPtr xpExpr; | |
1456 xmlXPathObjectPtr result; | |
1457 | |
1458 xsltStackElemPtr elem; | |
1459 int res; | |
1460 void *res_ptr; | |
1461 | |
1462 if (ctxt == NULL) | |
1463 return(-1); | |
1464 if (name == NULL) | |
1465 return(0); | |
1466 if (value == NULL) | |
1467 return(0); | |
1468 | |
1469 style = ctxt->style; | |
1470 | |
1471 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1472 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
1473 "Evaluating user parameter %s=%s\n", name, value)); | |
1474 #endif | |
1475 | |
1476 /* | |
1477 * Name lookup | |
1478 */ | |
1479 href = NULL; | |
1480 | |
1481 if (name[0] == '{') { | |
1482 int len = 0; | |
1483 | |
1484 while ((name[len] != 0) && (name[len] != '}')) len++; | |
1485 if (name[len] == 0) { | |
1486 xsltTransformError(ctxt, style, NULL, | |
1487 "user param : malformed parameter name : %s\n", name); | |
1488 } else { | |
1489 href = xmlDictLookup(ctxt->dict, &name[1], len-1); | |
1490 name = xmlDictLookup(ctxt->dict, &name[len + 1], -1); | |
1491 } | |
1492 } | |
1493 else { | |
1494 name = xsltSplitQName(ctxt->dict, name, &prefix); | |
1495 if (prefix != NULL) { | |
1496 xmlNsPtr ns; | |
1497 | |
1498 ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc), | |
1499 prefix); | |
1500 if (ns == NULL) { | |
1501 xsltTransformError(ctxt, style, NULL, | |
1502 "user param : no namespace bound to prefix %s\n", prefix); | |
1503 href = NULL; | |
1504 } else { | |
1505 href = ns->href; | |
1506 } | |
1507 } | |
1508 } | |
1509 | |
1510 if (name == NULL) | |
1511 return (-1); | |
1512 | |
1513 res_ptr = xmlHashLookup2(ctxt->globalVars, name, href); | |
1514 if (res_ptr != 0) { | |
1515 xsltTransformError(ctxt, style, NULL, | |
1516 "Global parameter %s already defined\n", name); | |
1517 } | |
1518 if (ctxt->globalVars == NULL) | |
1519 ctxt->globalVars = xmlHashCreate(20); | |
1520 | |
1521 /* | |
1522 * do not overwrite variables with parameters from the command line | |
1523 */ | |
1524 while (style != NULL) { | |
1525 elem = ctxt->style->variables; | |
1526 while (elem != NULL) { | |
1527 if ((elem->comp != NULL) && | |
1528 (elem->comp->type == XSLT_FUNC_VARIABLE) && | |
1529 (xmlStrEqual(elem->name, name)) && | |
1530 (xmlStrEqual(elem->nameURI, href))) { | |
1531 return(0); | |
1532 } | |
1533 elem = elem->next; | |
1534 } | |
1535 style = xsltNextImport(style); | |
1536 } | |
1537 style = ctxt->style; | |
1538 elem = NULL; | |
1539 | |
1540 /* | |
1541 * Do the evaluation if @eval is non-zero. | |
1542 */ | |
1543 | |
1544 result = NULL; | |
1545 if (eval != 0) { | |
1546 xpExpr = xmlXPathCompile(value); | |
1547 if (xpExpr != NULL) { | |
1548 xmlDocPtr oldXPDoc; | |
1549 xmlNodePtr oldXPContextNode; | |
1550 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr; | |
1551 xmlNsPtr *oldXPNamespaces; | |
1552 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt; | |
1553 | |
1554 /* | |
1555 * Save context states. | |
1556 */ | |
1557 oldXPDoc = xpctxt->doc; | |
1558 oldXPContextNode = xpctxt->node; | |
1559 oldXPProximityPosition = xpctxt->proximityPosition; | |
1560 oldXPContextSize = xpctxt->contextSize; | |
1561 oldXPNamespaces = xpctxt->namespaces; | |
1562 oldXPNsNr = xpctxt->nsNr; | |
1563 | |
1564 /* | |
1565 * SPEC XSLT 1.0: | |
1566 * "At top-level, the expression or template specifying the | |
1567 * variable value is evaluated with the same context as that used | |
1568 * to process the root node of the source document: the current | |
1569 * node is the root node of the source document and the current | |
1570 * node list is a list containing just the root node of the source | |
1571 * document." | |
1572 */ | |
1573 xpctxt->doc = ctxt->initialContextDoc; | |
1574 xpctxt->node = ctxt->initialContextNode; | |
1575 xpctxt->contextSize = 1; | |
1576 xpctxt->proximityPosition = 1; | |
1577 /* | |
1578 * There is really no in scope namespace for parameters on the | |
1579 * command line. | |
1580 */ | |
1581 xpctxt->namespaces = NULL; | |
1582 xpctxt->nsNr = 0; | |
1583 | |
1584 result = xmlXPathCompiledEval(xpExpr, xpctxt); | |
1585 | |
1586 /* | |
1587 * Restore Context states. | |
1588 */ | |
1589 xpctxt->doc = oldXPDoc; | |
1590 xpctxt->node = oldXPContextNode; | |
1591 xpctxt->contextSize = oldXPContextSize; | |
1592 xpctxt->proximityPosition = oldXPProximityPosition; | |
1593 xpctxt->namespaces = oldXPNamespaces; | |
1594 xpctxt->nsNr = oldXPNsNr; | |
1595 | |
1596 xmlXPathFreeCompExpr(xpExpr); | |
1597 } | |
1598 if (result == NULL) { | |
1599 xsltTransformError(ctxt, style, NULL, | |
1600 "Evaluating user parameter %s failed\n", name); | |
1601 ctxt->state = XSLT_STATE_STOPPED; | |
1602 return(-1); | |
1603 } | |
1604 } | |
1605 | |
1606 /* | |
1607 * If @eval is 0 then @value is to be taken literally and result is NULL | |
1608 * | |
1609 * If @eval is not 0, then @value is an XPath expression and has been | |
1610 * successfully evaluated and result contains the resulting value and | |
1611 * is not NULL. | |
1612 * | |
1613 * Now create an xsltStackElemPtr for insertion into the context's | |
1614 * global variable/parameter hash table. | |
1615 */ | |
1616 | |
1617 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1618 #ifdef LIBXML_DEBUG_ENABLED | |
1619 if ((xsltGenericDebugContext == stdout) || | |
1620 (xsltGenericDebugContext == stderr)) | |
1621 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext, | |
1622 result, 0); | |
1623 #endif | |
1624 #endif | |
1625 | |
1626 elem = xsltNewStackElem(NULL); | |
1627 if (elem != NULL) { | |
1628 elem->name = name; | |
1629 elem->select = xmlDictLookup(ctxt->dict, value, -1); | |
1630 if (href != NULL) | |
1631 elem->nameURI = xmlDictLookup(ctxt->dict, href, -1); | |
1632 elem->tree = NULL; | |
1633 elem->computed = 1; | |
1634 if (eval == 0) { | |
1635 elem->value = xmlXPathNewString(value); | |
1636 } | |
1637 else { | |
1638 elem->value = result; | |
1639 } | |
1640 } | |
1641 | |
1642 /* | |
1643 * Global parameters are stored in the XPath context variables pool. | |
1644 */ | |
1645 | |
1646 res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem); | |
1647 if (res != 0) { | |
1648 xsltFreeStackElem(elem); | |
1649 xsltTransformError(ctxt, style, NULL, | |
1650 "Global parameter %s already defined\n", name); | |
1651 } | |
1652 return(0); | |
1653 } | |
1654 | |
1655 /** | |
1656 * xsltEvalUserParams: | |
1657 * | |
1658 * @ctxt: the XSLT transformation context | |
1659 * @params: a NULL terminated array of parameters name/value tuples | |
1660 * | |
1661 * Evaluate the global variables of a stylesheet. This needs to be | |
1662 * done on parsed stylesheets before starting to apply transformations. | |
1663 * Each of the parameters is evaluated as an XPath expression and stored | |
1664 * in the global variables/parameter hash table. If you want your | |
1665 * parameter used literally, use xsltQuoteUserParams. | |
1666 * | |
1667 * Returns 0 in case of success, -1 in case of error | |
1668 */ | |
1669 | |
1670 int | |
1671 xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) { | |
1672 int indx = 0; | |
1673 const xmlChar *name; | |
1674 const xmlChar *value; | |
1675 | |
1676 if (params == NULL) | |
1677 return(0); | |
1678 while (params[indx] != NULL) { | |
1679 name = (const xmlChar *) params[indx++]; | |
1680 value = (const xmlChar *) params[indx++]; | |
1681 if (xsltEvalOneUserParam(ctxt, name, value) != 0) | |
1682 return(-1); | |
1683 } | |
1684 return 0; | |
1685 } | |
1686 | |
1687 /** | |
1688 * xsltQuoteUserParams: | |
1689 * | |
1690 * @ctxt: the XSLT transformation context | |
1691 * @params: a NULL terminated arry of parameters names/values tuples | |
1692 * | |
1693 * Similar to xsltEvalUserParams, but the values are treated literally and | |
1694 * are * *not* evaluated as XPath expressions. This should be done on parsed | |
1695 * stylesheets before starting to apply transformations. | |
1696 * | |
1697 * Returns 0 in case of success, -1 in case of error. | |
1698 */ | |
1699 | |
1700 int | |
1701 xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) { | |
1702 int indx = 0; | |
1703 const xmlChar *name; | |
1704 const xmlChar *value; | |
1705 | |
1706 if (params == NULL) | |
1707 return(0); | |
1708 while (params[indx] != NULL) { | |
1709 name = (const xmlChar *) params[indx++]; | |
1710 value = (const xmlChar *) params[indx++]; | |
1711 if (xsltQuoteOneUserParam(ctxt, name, value) != 0) | |
1712 return(-1); | |
1713 } | |
1714 return 0; | |
1715 } | |
1716 | |
1717 /** | |
1718 * xsltEvalOneUserParam: | |
1719 * @ctxt: the XSLT transformation context | |
1720 * @name: a null terminated string giving the name of the parameter | |
1721 * @value: a null terminated string giving the XPath expression to be evaluated | |
1722 * | |
1723 * This is normally called from xsltEvalUserParams to process a single | |
1724 * parameter from a list of parameters. The @value is evaluated as an | |
1725 * XPath expression and the result is stored in the context's global | |
1726 * variable/parameter hash table. | |
1727 * | |
1728 * To have a parameter treated literally (not as an XPath expression) | |
1729 * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more | |
1730 * details see description of xsltProcessOneUserParamInternal. | |
1731 * | |
1732 * Returns 0 in case of success, -1 in case of error. | |
1733 */ | |
1734 | |
1735 int | |
1736 xsltEvalOneUserParam(xsltTransformContextPtr ctxt, | |
1737 const xmlChar * name, | |
1738 const xmlChar * value) { | |
1739 return xsltProcessUserParamInternal(ctxt, name, value, | |
1740 1 /* xpath eval ? */); | |
1741 } | |
1742 | |
1743 /** | |
1744 * xsltQuoteOneUserParam: | |
1745 * @ctxt: the XSLT transformation context | |
1746 * @name: a null terminated string giving the name of the parameter | |
1747 * @value: a null terminated string giving the parameter value | |
1748 * | |
1749 * This is normally called from xsltQuoteUserParams to process a single | |
1750 * parameter from a list of parameters. The @value is stored in the | |
1751 * context's global variable/parameter hash table. | |
1752 * | |
1753 * Returns 0 in case of success, -1 in case of error. | |
1754 */ | |
1755 | |
1756 int | |
1757 xsltQuoteOneUserParam(xsltTransformContextPtr ctxt, | |
1758 const xmlChar * name, | |
1759 const xmlChar * value) { | |
1760 return xsltProcessUserParamInternal(ctxt, name, value, | |
1761 0 /* xpath eval ? */); | |
1762 } | |
1763 | |
1764 /** | |
1765 * xsltBuildVariable: | |
1766 * @ctxt: the XSLT transformation context | |
1767 * @comp: the precompiled form | |
1768 * @tree: the tree if select is NULL | |
1769 * | |
1770 * Computes a new variable value. | |
1771 * | |
1772 * Returns the xsltStackElemPtr or NULL in case of error | |
1773 */ | |
1774 static xsltStackElemPtr | |
1775 xsltBuildVariable(xsltTransformContextPtr ctxt, | |
1776 xsltStylePreCompPtr castedComp, | |
1777 xmlNodePtr tree) | |
1778 { | |
1779 #ifdef XSLT_REFACTORED | |
1780 xsltStyleBasicItemVariablePtr comp = | |
1781 (xsltStyleBasicItemVariablePtr) castedComp; | |
1782 #else | |
1783 xsltStylePreCompPtr comp = castedComp; | |
1784 #endif | |
1785 xsltStackElemPtr elem; | |
1786 | |
1787 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1788 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
1789 "Building variable %s", comp->name)); | |
1790 if (comp->select != NULL) | |
1791 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
1792 " select %s", comp->select)); | |
1793 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, "\n")); | |
1794 #endif | |
1795 | |
1796 elem = xsltNewStackElem(ctxt); | |
1797 if (elem == NULL) | |
1798 return(NULL); | |
1799 elem->comp = (xsltStylePreCompPtr) comp; | |
1800 elem->name = comp->name; | |
1801 elem->select = comp->select; | |
1802 elem->nameURI = comp->ns; | |
1803 elem->tree = tree; | |
1804 elem->value = xsltEvalVariable(ctxt, elem, | |
1805 (xsltStylePreCompPtr) comp); | |
1806 elem->computed = 1; | |
1807 return(elem); | |
1808 } | |
1809 | |
1810 /** | |
1811 * xsltRegisterVariable: | |
1812 * @ctxt: the XSLT transformation context | |
1813 * @comp: the compiled XSLT-variable (or param) instruction | |
1814 * @tree: the tree if select is NULL | |
1815 * @isParam: indicates if this is a parameter | |
1816 * | |
1817 * Computes and registers a new variable. | |
1818 * | |
1819 * Returns 0 in case of success, -1 in case of error | |
1820 */ | |
1821 static int | |
1822 xsltRegisterVariable(xsltTransformContextPtr ctxt, | |
1823 xsltStylePreCompPtr castedComp, | |
1824 xmlNodePtr tree, int isParam) | |
1825 { | |
1826 #ifdef XSLT_REFACTORED | |
1827 xsltStyleBasicItemVariablePtr comp = | |
1828 (xsltStyleBasicItemVariablePtr) castedComp; | |
1829 #else | |
1830 xsltStylePreCompPtr comp = castedComp; | |
1831 int present; | |
1832 #endif | |
1833 xsltStackElemPtr variable; | |
1834 | |
1835 #ifdef XSLT_REFACTORED | |
1836 /* | |
1837 * REFACTORED NOTE: Redefinitions of vars/params are checked | |
1838 * at compilation time in the refactored code. | |
1839 * xsl:with-param parameters are checked in xsltApplyXSLTTemplate(). | |
1840 */ | |
1841 #else | |
1842 present = xsltCheckStackElem(ctxt, comp->name, comp->ns); | |
1843 if (isParam == 0) { | |
1844 if ((present != 0) && (present != 3)) { | |
1845 /* TODO: report QName. */ | |
1846 xsltTransformError(ctxt, NULL, comp->inst, | |
1847 "XSLT-variable: Redefinition of variable '%s'.\n", comp->name); | |
1848 return(0); | |
1849 } | |
1850 } else if (present != 0) { | |
1851 if ((present == 1) || (present == 2)) { | |
1852 /* TODO: report QName. */ | |
1853 xsltTransformError(ctxt, NULL, comp->inst, | |
1854 "XSLT-param: Redefinition of parameter '%s'.\n", comp->name); | |
1855 return(0); | |
1856 } | |
1857 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1858 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
1859 "param %s defined by caller\n", comp->name)); | |
1860 #endif | |
1861 return(0); | |
1862 } | |
1863 #endif /* else of XSLT_REFACTORED */ | |
1864 | |
1865 variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); | |
1866 xsltAddStackElem(ctxt, variable); | |
1867 return(0); | |
1868 } | |
1869 | |
1870 /** | |
1871 * xsltGlobalVariableLookup: | |
1872 * @ctxt: the XSLT transformation context | |
1873 * @name: the variable name | |
1874 * @ns_uri: the variable namespace URI | |
1875 * | |
1876 * Search in the Variable array of the context for the given | |
1877 * variable value. | |
1878 * | |
1879 * Returns the value or NULL if not found | |
1880 */ | |
1881 static xmlXPathObjectPtr | |
1882 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, | |
1883 const xmlChar *ns_uri) { | |
1884 xsltStackElemPtr elem; | |
1885 xmlXPathObjectPtr ret = NULL; | |
1886 | |
1887 /* | |
1888 * Lookup the global variables in XPath global variable hash table | |
1889 */ | |
1890 if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL)) | |
1891 return(NULL); | |
1892 elem = (xsltStackElemPtr) | |
1893 xmlHashLookup2(ctxt->globalVars, name, ns_uri); | |
1894 if (elem == NULL) { | |
1895 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1896 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
1897 "global variable not found %s\n", name)); | |
1898 #endif | |
1899 return(NULL); | |
1900 } | |
1901 /* | |
1902 * URGENT TODO: Move the detection of recursive definitions | |
1903 * to compile-time. | |
1904 */ | |
1905 if (elem->computed == 0) { | |
1906 if (elem->name == xsltComputingGlobalVarMarker) { | |
1907 xsltTransformError(ctxt, NULL, elem->comp->inst, | |
1908 "Recursive definition of %s\n", name); | |
1909 return(NULL); | |
1910 } | |
1911 ret = xsltEvalGlobalVariable(elem, ctxt); | |
1912 } else | |
1913 ret = elem->value; | |
1914 return(xmlXPathObjectCopy(ret)); | |
1915 } | |
1916 | |
1917 /** | |
1918 * xsltVariableLookup: | |
1919 * @ctxt: the XSLT transformation context | |
1920 * @name: the variable name | |
1921 * @ns_uri: the variable namespace URI | |
1922 * | |
1923 * Search in the Variable array of the context for the given | |
1924 * variable value. | |
1925 * | |
1926 * Returns the value or NULL if not found | |
1927 */ | |
1928 xmlXPathObjectPtr | |
1929 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name, | |
1930 const xmlChar *ns_uri) { | |
1931 xsltStackElemPtr elem; | |
1932 | |
1933 if (ctxt == NULL) | |
1934 return(NULL); | |
1935 | |
1936 elem = xsltStackLookup(ctxt, name, ns_uri); | |
1937 if (elem == NULL) { | |
1938 return(xsltGlobalVariableLookup(ctxt, name, ns_uri)); | |
1939 } | |
1940 if (elem->computed == 0) { | |
1941 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1942 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
1943 "uncomputed variable %s\n", name)); | |
1944 #endif | |
1945 elem->value = xsltEvalVariable(ctxt, elem, NULL); | |
1946 elem->computed = 1; | |
1947 } | |
1948 if (elem->value != NULL) | |
1949 return(xmlXPathObjectCopy(elem->value)); | |
1950 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
1951 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
1952 "variable not found %s\n", name)); | |
1953 #endif | |
1954 return(NULL); | |
1955 } | |
1956 | |
1957 /** | |
1958 * xsltParseStylesheetCallerParam: | |
1959 * @ctxt: the XSLT transformation context | |
1960 * @inst: the xsl:with-param instruction element | |
1961 * | |
1962 * Processes an xsl:with-param instruction at transformation time. | |
1963 * The value is compute, but not recorded. | |
1964 * NOTE that this is also called with an *xsl:param* element | |
1965 * from exsltFuncFunctionFunction(). | |
1966 * | |
1967 * Returns the new xsltStackElemPtr or NULL | |
1968 */ | |
1969 | |
1970 xsltStackElemPtr | |
1971 xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst) | |
1972 { | |
1973 #ifdef XSLT_REFACTORED | |
1974 xsltStyleBasicItemVariablePtr comp; | |
1975 #else | |
1976 xsltStylePreCompPtr comp; | |
1977 #endif | |
1978 xmlNodePtr tree = NULL; /* The first child node of the instruction or | |
1979 the instruction itself. */ | |
1980 xsltStackElemPtr param = NULL; | |
1981 | |
1982 if ((ctxt == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE)) | |
1983 return(NULL); | |
1984 | |
1985 #ifdef XSLT_REFACTORED | |
1986 comp = (xsltStyleBasicItemVariablePtr) inst->psvi; | |
1987 #else | |
1988 comp = (xsltStylePreCompPtr) inst->psvi; | |
1989 #endif | |
1990 | |
1991 if (comp == NULL) { | |
1992 xsltTransformError(ctxt, NULL, inst, | |
1993 "Internal error in xsltParseStylesheetCallerParam(): " | |
1994 "The XSLT 'with-param' instruction was not compiled.\n"); | |
1995 return(NULL); | |
1996 } | |
1997 if (comp->name == NULL) { | |
1998 xsltTransformError(ctxt, NULL, inst, | |
1999 "Internal error in xsltParseStylesheetCallerParam(): " | |
2000 "XSLT 'with-param': The attribute 'name' was not compiled.\n"); | |
2001 return(NULL); | |
2002 } | |
2003 | |
2004 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2005 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
2006 "Handling xsl:with-param %s\n", comp->name)); | |
2007 #endif | |
2008 | |
2009 if (comp->select == NULL) { | |
2010 tree = inst->children; | |
2011 } else { | |
2012 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2013 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugCo
ntext, | |
2014 " select %s\n", comp->select)); | |
2015 #endif | |
2016 tree = inst; | |
2017 } | |
2018 | |
2019 param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree); | |
2020 | |
2021 return(param); | |
2022 } | |
2023 | |
2024 /** | |
2025 * xsltParseGlobalVariable: | |
2026 * @style: the XSLT stylesheet | |
2027 * @cur: the "variable" element | |
2028 * | |
2029 * Parses a global XSLT 'variable' declaration at compilation time | |
2030 * and registers it | |
2031 */ | |
2032 void | |
2033 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) | |
2034 { | |
2035 #ifdef XSLT_REFACTORED | |
2036 xsltStyleItemVariablePtr comp; | |
2037 #else | |
2038 xsltStylePreCompPtr comp; | |
2039 #endif | |
2040 | |
2041 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
2042 return; | |
2043 | |
2044 #ifdef XSLT_REFACTORED | |
2045 /* | |
2046 * Note that xsltStylePreCompute() will be called from | |
2047 * xslt.c only. | |
2048 */ | |
2049 comp = (xsltStyleItemVariablePtr) cur->psvi; | |
2050 #else | |
2051 xsltStylePreCompute(style, cur); | |
2052 comp = (xsltStylePreCompPtr) cur->psvi; | |
2053 #endif | |
2054 if (comp == NULL) { | |
2055 xsltTransformError(NULL, style, cur, | |
2056 "xsl:variable : compilation failed\n"); | |
2057 return; | |
2058 } | |
2059 | |
2060 if (comp->name == NULL) { | |
2061 xsltTransformError(NULL, style, cur, | |
2062 "xsl:variable : missing name attribute\n"); | |
2063 return; | |
2064 } | |
2065 | |
2066 /* | |
2067 * Parse the content (a sequence constructor) of xsl:variable. | |
2068 */ | |
2069 if (cur->children != NULL) { | |
2070 #ifdef XSLT_REFACTORED | |
2071 xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); | |
2072 #else | |
2073 xsltParseTemplateContent(style, cur); | |
2074 #endif | |
2075 } | |
2076 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2077 xsltGenericDebug(xsltGenericDebugContext, | |
2078 "Registering global variable %s\n", comp->name); | |
2079 #endif | |
2080 | |
2081 xsltRegisterGlobalVariable(style, comp->name, comp->ns, | |
2082 comp->select, cur->children, (xsltStylePreCompPtr) comp, | |
2083 NULL); | |
2084 } | |
2085 | |
2086 /** | |
2087 * xsltParseGlobalParam: | |
2088 * @style: the XSLT stylesheet | |
2089 * @cur: the "param" element | |
2090 * | |
2091 * parse an XSLT transformation param declaration and record | |
2092 * its value. | |
2093 */ | |
2094 | |
2095 void | |
2096 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) { | |
2097 #ifdef XSLT_REFACTORED | |
2098 xsltStyleItemParamPtr comp; | |
2099 #else | |
2100 xsltStylePreCompPtr comp; | |
2101 #endif | |
2102 | |
2103 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
2104 return; | |
2105 | |
2106 #ifdef XSLT_REFACTORED | |
2107 /* | |
2108 * Note that xsltStylePreCompute() will be called from | |
2109 * xslt.c only. | |
2110 */ | |
2111 comp = (xsltStyleItemParamPtr) cur->psvi; | |
2112 #else | |
2113 xsltStylePreCompute(style, cur); | |
2114 comp = (xsltStylePreCompPtr) cur->psvi; | |
2115 #endif | |
2116 if (comp == NULL) { | |
2117 xsltTransformError(NULL, style, cur, | |
2118 "xsl:param : compilation failed\n"); | |
2119 return; | |
2120 } | |
2121 | |
2122 if (comp->name == NULL) { | |
2123 xsltTransformError(NULL, style, cur, | |
2124 "xsl:param : missing name attribute\n"); | |
2125 return; | |
2126 } | |
2127 | |
2128 /* | |
2129 * Parse the content (a sequence constructor) of xsl:param. | |
2130 */ | |
2131 if (cur->children != NULL) { | |
2132 #ifdef XSLT_REFACTORED | |
2133 xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children); | |
2134 #else | |
2135 xsltParseTemplateContent(style, cur); | |
2136 #endif | |
2137 } | |
2138 | |
2139 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2140 xsltGenericDebug(xsltGenericDebugContext, | |
2141 "Registering global param %s\n", comp->name); | |
2142 #endif | |
2143 | |
2144 xsltRegisterGlobalVariable(style, comp->name, comp->ns, | |
2145 comp->select, cur->children, (xsltStylePreCompPtr) comp, | |
2146 NULL); | |
2147 } | |
2148 | |
2149 /** | |
2150 * xsltParseStylesheetVariable: | |
2151 * @ctxt: the XSLT transformation context | |
2152 * @inst: the xsl:variable instruction element | |
2153 * | |
2154 * Registers a local XSLT 'variable' instruction at transformation time | |
2155 * and evaluates its value. | |
2156 */ | |
2157 void | |
2158 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst) | |
2159 { | |
2160 #ifdef XSLT_REFACTORED | |
2161 xsltStyleItemVariablePtr comp; | |
2162 #else | |
2163 xsltStylePreCompPtr comp; | |
2164 #endif | |
2165 | |
2166 if ((inst == NULL) || (ctxt == NULL) || (inst->type != XML_ELEMENT_NODE)) | |
2167 return; | |
2168 | |
2169 comp = inst->psvi; | |
2170 if (comp == NULL) { | |
2171 xsltTransformError(ctxt, NULL, inst, | |
2172 "Internal error in xsltParseStylesheetVariable(): " | |
2173 "The XSLT 'variable' instruction was not compiled.\n"); | |
2174 return; | |
2175 } | |
2176 if (comp->name == NULL) { | |
2177 xsltTransformError(ctxt, NULL, inst, | |
2178 "Internal error in xsltParseStylesheetVariable(): " | |
2179 "The attribute 'name' was not compiled.\n"); | |
2180 return; | |
2181 } | |
2182 | |
2183 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2184 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
2185 "Registering variable '%s'\n", comp->name)); | |
2186 #endif | |
2187 | |
2188 xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0); | |
2189 } | |
2190 | |
2191 /** | |
2192 * xsltParseStylesheetParam: | |
2193 * @ctxt: the XSLT transformation context | |
2194 * @cur: the XSLT 'param' element | |
2195 * | |
2196 * Registers a local XSLT 'param' declaration at transformation time and | |
2197 * evaluates its value. | |
2198 */ | |
2199 void | |
2200 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) | |
2201 { | |
2202 #ifdef XSLT_REFACTORED | |
2203 xsltStyleItemParamPtr comp; | |
2204 #else | |
2205 xsltStylePreCompPtr comp; | |
2206 #endif | |
2207 | |
2208 if ((cur == NULL) || (ctxt == NULL) || (cur->type != XML_ELEMENT_NODE)) | |
2209 return; | |
2210 | |
2211 comp = cur->psvi; | |
2212 if ((comp == NULL) || (comp->name == NULL)) { | |
2213 xsltTransformError(ctxt, NULL, cur, | |
2214 "Internal error in xsltParseStylesheetParam(): " | |
2215 "The XSLT 'param' declaration was not compiled correctly.\n"); | |
2216 return; | |
2217 } | |
2218 | |
2219 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2220 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContex
t, | |
2221 "Registering param %s\n", comp->name)); | |
2222 #endif | |
2223 | |
2224 xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1); | |
2225 } | |
2226 | |
2227 /** | |
2228 * xsltFreeGlobalVariables: | |
2229 * @ctxt: the XSLT transformation context | |
2230 * | |
2231 * Free up the data associated to the global variables | |
2232 * its value. | |
2233 */ | |
2234 | |
2235 void | |
2236 xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) { | |
2237 xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem); | |
2238 } | |
2239 | |
2240 /** | |
2241 * xsltXPathVariableLookup: | |
2242 * @ctxt: a void * but the the XSLT transformation context actually | |
2243 * @name: the variable name | |
2244 * @ns_uri: the variable namespace URI | |
2245 * | |
2246 * This is the entry point when a varibale is needed by the XPath | |
2247 * interpretor. | |
2248 * | |
2249 * Returns the value or NULL if not found | |
2250 */ | |
2251 xmlXPathObjectPtr | |
2252 xsltXPathVariableLookup(void *ctxt, const xmlChar *name, | |
2253 const xmlChar *ns_uri) { | |
2254 xsltTransformContextPtr tctxt; | |
2255 xmlXPathObjectPtr valueObj = NULL; | |
2256 | |
2257 if ((ctxt == NULL) || (name == NULL)) | |
2258 return(NULL); | |
2259 | |
2260 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2261 XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericD
ebug(xsltGenericDebugContext, | |
2262 "Lookup variable '%s'\n", name)); | |
2263 #endif | |
2264 | |
2265 tctxt = (xsltTransformContextPtr) ctxt; | |
2266 /* | |
2267 * Local variables/params --------------------------------------------- | |
2268 * | |
2269 * Do the lookup from the top of the stack, but | |
2270 * don't use params being computed in a call-param | |
2271 * First lookup expects the variable name and URI to | |
2272 * come from the disctionnary and hence pointer comparison. | |
2273 */ | |
2274 if (tctxt->varsNr != 0) { | |
2275 int i; | |
2276 xsltStackElemPtr variable = NULL, cur; | |
2277 | |
2278 for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { | |
2279 cur = tctxt->varsTab[i-1]; | |
2280 if ((cur->name == name) && (cur->nameURI == ns_uri)) { | |
2281 #if 0 | |
2282 stack_addr++; | |
2283 #endif | |
2284 variable = cur; | |
2285 goto local_variable_found; | |
2286 } | |
2287 cur = cur->next; | |
2288 } | |
2289 /* | |
2290 * Redo the lookup with interned strings to avoid string comparison. | |
2291 * | |
2292 * OPTIMIZE TODO: The problem here is, that if we request a | |
2293 * global variable, then this will be also executed. | |
2294 */ | |
2295 { | |
2296 const xmlChar *tmpName = name, *tmpNsName = ns_uri; | |
2297 | |
2298 name = xmlDictLookup(tctxt->dict, name, -1); | |
2299 if (ns_uri) | |
2300 ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1); | |
2301 if ((tmpName != name) || (tmpNsName != ns_uri)) { | |
2302 for (i = tctxt->varsNr; i > tctxt->varsBase; i--) { | |
2303 cur = tctxt->varsTab[i-1]; | |
2304 if ((cur->name == name) && (cur->nameURI == ns_uri)) { | |
2305 #if 0 | |
2306 stack_cmp++; | |
2307 #endif | |
2308 variable = cur; | |
2309 goto local_variable_found; | |
2310 } | |
2311 } | |
2312 } | |
2313 } | |
2314 | |
2315 local_variable_found: | |
2316 | |
2317 if (variable) { | |
2318 if (variable->computed == 0) { | |
2319 | |
2320 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2321 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGener
icDebugContext, | |
2322 "uncomputed variable '%s'\n", name)); | |
2323 #endif | |
2324 variable->value = xsltEvalVariable(tctxt, variable, NULL); | |
2325 variable->computed = 1; | |
2326 } | |
2327 if (variable->value != NULL) { | |
2328 valueObj = xmlXPathObjectCopy(variable->value); | |
2329 } | |
2330 return(valueObj); | |
2331 } | |
2332 } | |
2333 /* | |
2334 * Global variables/params -------------------------------------------- | |
2335 */ | |
2336 if (tctxt->globalVars) { | |
2337 valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri); | |
2338 } | |
2339 | |
2340 if (valueObj == NULL) { | |
2341 | |
2342 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2343 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugConte
xt, | |
2344 "variable not found '%s'\n", name)); | |
2345 #endif | |
2346 | |
2347 if (ns_uri) { | |
2348 xsltTransformError(tctxt, NULL, tctxt->inst, | |
2349 "Variable '{%s}%s' has not been declared.\n", ns_uri, name); | |
2350 } else { | |
2351 xsltTransformError(tctxt, NULL, tctxt->inst, | |
2352 "Variable '%s' has not been declared.\n", name); | |
2353 } | |
2354 } else { | |
2355 | |
2356 #ifdef WITH_XSLT_DEBUG_VARIABLE | |
2357 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugC
ontext, | |
2358 "found variable '%s'\n", name)); | |
2359 #endif | |
2360 } | |
2361 | |
2362 return(valueObj); | |
2363 } | |
2364 | |
2365 | |
OLD | NEW |