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