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