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