[REACTOS]
[reactos.git] / reactos / dll / 3rdparty / libxslt / variables.c
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
65 xsltCreateRVT(xsltTransformContextPtr ctxt)
66 {
67 xmlDocPtr container;
68
69 /*
70 * Question: Why is this function public?
71 * Answer: It is called by the EXSLT module.
72 */
73 if (ctxt == NULL)
74 return(NULL);
75
76 /*
77 * Reuse a RTF from the cache if available.
78 */
79 if (ctxt->cache->RVT) {
80 container = ctxt->cache->RVT;
81 ctxt->cache->RVT = (xmlDocPtr) container->next;
82 /* clear the internal pointers */
83 container->next = NULL;
84 container->prev = NULL;
85 if (ctxt->cache->nbRVT > 0)
86 ctxt->cache->nbRVT--;
87 #ifdef XSLT_DEBUG_PROFILE_CACHE
88 ctxt->cache->dbgReusedRVTs++;
89 #endif
90 return(container);
91 }
92
93 container = xmlNewDoc(NULL);
94 if (container == NULL)
95 return(NULL);
96 container->dict = ctxt->dict;
97 xmlDictReference(container->dict);
98 XSLT_MARK_RES_TREE_FRAG(container);
99 container->doc = container;
100 container->parent = NULL;
101 return(container);
102 }
103
104 /**
105 * xsltRegisterTmpRVT:
106 * @ctxt: an XSLT transformation context
107 * @RVT: a result value tree (Result Tree Fragment)
108 *
109 * Registers the result value tree (XSLT 1.0 term: Result Tree Fragment)
110 * in the garbage collector.
111 * The fragment will be freed at the exit of the currently
112 * instantiated xsl:template.
113 * Obsolete; this function might produce massive memory overhead,
114 * since the fragment is only freed when the current xsl:template
115 * exits. Use xsltRegisterLocalRVT() instead.
116 *
117 * Returns 0 in case of success and -1 in case of API or internal errors.
118 */
119 int
120 xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
121 {
122 if ((ctxt == NULL) || (RVT == NULL))
123 return(-1);
124
125 /*
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 = xmlXPathCompile(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 xsltStackElemPtr oldVar = ctxt->contextVariable;
946
947 /*
948 * Generate a result tree fragment.
949 */
950 container = xsltCreateRVT(ctxt);
951 if (container == NULL)
952 goto error;
953 /*
954 * NOTE: Local Result Tree Fragments of params/variables
955 * are not registered globally anymore; the life-time
956 * is not directly dependant of the param/variable itself.
957 *
958 * OLD: xsltRegisterTmpRVT(ctxt, container);
959 */
960 /*
961 * Attach the Result Tree Fragment to the variable;
962 * when the variable is freed, it will also free
963 * the Result Tree Fragment.
964 */
965 variable->fragment = container;
966
967 oldOutput = ctxt->output;
968 oldInsert = ctxt->insert;
969
970 ctxt->output = container;
971 ctxt->insert = (xmlNodePtr) container;
972 ctxt->contextVariable = variable;
973 /*
974 * Process the sequence constructor (variable->tree).
975 * The resulting tree will be held by @container.
976 */
977 xsltApplyOneTemplate(ctxt, ctxt->node, variable->tree,
978 NULL, NULL);
979
980 ctxt->contextVariable = oldVar;
981 ctxt->insert = oldInsert;
982 ctxt->output = oldOutput;
983
984 result = xmlXPathNewValueTree((xmlNodePtr) container);
985 }
986 if (result == NULL) {
987 result = xmlXPathNewCString("");
988 } else {
989 /*
990 * Freeing is not handled there anymore.
991 * QUESTION TODO: What does the above comment mean?
992 */
993 result->boolval = 0;
994 }
995 #ifdef WITH_XSLT_DEBUG_VARIABLE
996 #ifdef LIBXML_DEBUG_ENABLED
997
998 if ((xsltGenericDebugContext == stdout) ||
999 (xsltGenericDebugContext == stderr))
1000 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1001 result, 0);
1002 #endif
1003 #endif
1004 }
1005 }
1006
1007 error:
1008 ctxt->inst = oldInst;
1009 return(result);
1010 }
1011
1012 /**
1013 * xsltEvalGlobalVariable:
1014 * @elem: the variable or parameter
1015 * @ctxt: the XSLT transformation context
1016 *
1017 * Evaluates a the value of a global xsl:variable or
1018 * xsl:param declaration.
1019 *
1020 * Returns the XPath Object value or NULL in case of error
1021 */
1022 static xmlXPathObjectPtr
1023 xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt)
1024 {
1025 xmlXPathObjectPtr result = NULL;
1026 xmlNodePtr oldInst;
1027 const xmlChar* oldVarName;
1028
1029 #ifdef XSLT_REFACTORED
1030 xsltStyleBasicItemVariablePtr comp;
1031 #else
1032 xsltStylePreCompPtr comp;
1033 #endif
1034
1035 if ((ctxt == NULL) || (elem == NULL))
1036 return(NULL);
1037 if (elem->computed)
1038 return(elem->value);
1039
1040
1041 #ifdef WITH_XSLT_DEBUG_VARIABLE
1042 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1043 "Evaluating global variable %s\n", elem->name));
1044 #endif
1045
1046 #ifdef WITH_DEBUGGER
1047 if ((ctxt->debugStatus != XSLT_DEBUG_NONE) &&
1048 elem->comp && elem->comp->inst)
1049 xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt);
1050 #endif
1051
1052 oldInst = ctxt->inst;
1053 #ifdef XSLT_REFACTORED
1054 comp = (xsltStyleBasicItemVariablePtr) elem->comp;
1055 #else
1056 comp = elem->comp;
1057 #endif
1058 oldVarName = elem->name;
1059 elem->name = xsltComputingGlobalVarMarker;
1060 /*
1061 * OPTIMIZE TODO: We should consider instantiating global vars/params
1062 * on-demand. The vars/params don't need to be evaluated if never
1063 * called; and in the case of global params, if values for such params
1064 * are provided by the user.
1065 */
1066 if (elem->select != NULL) {
1067 xmlXPathCompExprPtr xpExpr = NULL;
1068 xmlDocPtr oldXPDoc;
1069 xmlNodePtr oldXPContextNode;
1070 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
1071 xmlNsPtr *oldXPNamespaces;
1072 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
1073
1074 if ((comp != NULL) && (comp->comp != NULL)) {
1075 xpExpr = comp->comp;
1076 } else {
1077 xpExpr = xmlXPathCompile(elem->select);
1078 }
1079 if (xpExpr == NULL)
1080 goto error;
1081
1082
1083 if (comp != NULL)
1084 ctxt->inst = comp->inst;
1085 else
1086 ctxt->inst = NULL;
1087 /*
1088 * SPEC XSLT 1.0:
1089 * "At top-level, the expression or template specifying the
1090 * variable value is evaluated with the same context as that used
1091 * to process the root node of the source document: the current
1092 * node is the root node of the source document and the current
1093 * node list is a list containing just the root node of the source
1094 * document."
1095 */
1096 /*
1097 * Save context states.
1098 */
1099 oldXPDoc = xpctxt->doc;
1100 oldXPContextNode = xpctxt->node;
1101 oldXPProximityPosition = xpctxt->proximityPosition;
1102 oldXPContextSize = xpctxt->contextSize;
1103 oldXPNamespaces = xpctxt->namespaces;
1104 oldXPNsNr = xpctxt->nsNr;
1105
1106 xpctxt->node = ctxt->initialContextNode;
1107 xpctxt->doc = ctxt->initialContextDoc;
1108 xpctxt->contextSize = 1;
1109 xpctxt->proximityPosition = 1;
1110
1111 if (comp != NULL) {
1112
1113 #ifdef XSLT_REFACTORED
1114 if (comp->inScopeNs != NULL) {
1115 xpctxt->namespaces = comp->inScopeNs->list;
1116 xpctxt->nsNr = comp->inScopeNs->xpathNumber;
1117 } else {
1118 xpctxt->namespaces = NULL;
1119 xpctxt->nsNr = 0;
1120 }
1121 #else
1122 xpctxt->namespaces = comp->nsList;
1123 xpctxt->nsNr = comp->nsNr;
1124 #endif
1125 } else {
1126 xpctxt->namespaces = NULL;
1127 xpctxt->nsNr = 0;
1128 }
1129
1130 result = xmlXPathCompiledEval(xpExpr, xpctxt);
1131
1132 /*
1133 * Restore Context states.
1134 */
1135 xpctxt->doc = oldXPDoc;
1136 xpctxt->node = oldXPContextNode;
1137 xpctxt->contextSize = oldXPContextSize;
1138 xpctxt->proximityPosition = oldXPProximityPosition;
1139 xpctxt->namespaces = oldXPNamespaces;
1140 xpctxt->nsNr = oldXPNsNr;
1141
1142 if ((comp == NULL) || (comp->comp == NULL))
1143 xmlXPathFreeCompExpr(xpExpr);
1144 if (result == NULL) {
1145 if (comp == NULL)
1146 xsltTransformError(ctxt, NULL, NULL,
1147 "Evaluating global variable %s failed\n", elem->name);
1148 else
1149 xsltTransformError(ctxt, NULL, comp->inst,
1150 "Evaluating global variable %s failed\n", elem->name);
1151 ctxt->state = XSLT_STATE_STOPPED;
1152 #ifdef WITH_XSLT_DEBUG_VARIABLE
1153 #ifdef LIBXML_DEBUG_ENABLED
1154 } else {
1155 if ((xsltGenericDebugContext == stdout) ||
1156 (xsltGenericDebugContext == stderr))
1157 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1158 result, 0);
1159 #endif
1160 #endif
1161 }
1162 } else {
1163 if (elem->tree == NULL) {
1164 result = xmlXPathNewCString("");
1165 } else {
1166 xmlDocPtr container;
1167 xmlNodePtr oldInsert;
1168 xmlDocPtr oldOutput, oldXPDoc;
1169 /*
1170 * Generate a result tree fragment.
1171 */
1172 container = xsltCreateRVT(ctxt);
1173 if (container == NULL)
1174 goto error;
1175 /*
1176 * Let the lifetime of the tree fragment be handled by
1177 * the Libxslt's garbage collector.
1178 */
1179 xsltRegisterPersistRVT(ctxt, container);
1180
1181 oldOutput = ctxt->output;
1182 oldInsert = ctxt->insert;
1183
1184 oldXPDoc = ctxt->xpathCtxt->doc;
1185
1186 ctxt->output = container;
1187 ctxt->insert = (xmlNodePtr) container;
1188
1189 ctxt->xpathCtxt->doc = ctxt->initialContextDoc;
1190 /*
1191 * Process the sequence constructor.
1192 */
1193 xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
1194
1195 ctxt->xpathCtxt->doc = oldXPDoc;
1196
1197 ctxt->insert = oldInsert;
1198 ctxt->output = oldOutput;
1199
1200 result = xmlXPathNewValueTree((xmlNodePtr) container);
1201 if (result == NULL) {
1202 result = xmlXPathNewCString("");
1203 } else {
1204 result->boolval = 0; /* Freeing is not handled there anymore */
1205 }
1206 #ifdef WITH_XSLT_DEBUG_VARIABLE
1207 #ifdef LIBXML_DEBUG_ENABLED
1208 if ((xsltGenericDebugContext == stdout) ||
1209 (xsltGenericDebugContext == stderr))
1210 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1211 result, 0);
1212 #endif
1213 #endif
1214 }
1215 }
1216
1217 error:
1218 elem->name = oldVarName;
1219 ctxt->inst = oldInst;
1220 if (result != NULL) {
1221 elem->value = result;
1222 elem->computed = 1;
1223 }
1224 return(result);
1225 }
1226
1227 /**
1228 * xsltEvalGlobalVariables:
1229 * @ctxt: the XSLT transformation context
1230 *
1231 * Evaluates all global variables and parameters of a stylesheet.
1232 * For internal use only. This is called at start of a transformation.
1233 *
1234 * Returns 0 in case of success, -1 in case of error
1235 */
1236 int
1237 xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
1238 xsltStackElemPtr elem;
1239 xsltStylesheetPtr style;
1240
1241 if ((ctxt == NULL) || (ctxt->document == NULL))
1242 return(-1);
1243
1244 #ifdef WITH_XSLT_DEBUG_VARIABLE
1245 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1246 "Registering global variables\n"));
1247 #endif
1248 /*
1249 * Walk the list from the stylesheets and populate the hash table
1250 */
1251 style = ctxt->style;
1252 while (style != NULL) {
1253 elem = style->variables;
1254
1255 #ifdef WITH_XSLT_DEBUG_VARIABLE
1256 if ((style->doc != NULL) && (style->doc->URL != NULL)) {
1257 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1258 "Registering global variables from %s\n",
1259 style->doc->URL));
1260 }
1261 #endif
1262
1263 while (elem != NULL) {
1264 xsltStackElemPtr def;
1265
1266 /*
1267 * Global variables are stored in the variables pool.
1268 */
1269 def = (xsltStackElemPtr)
1270 xmlHashLookup2(ctxt->globalVars,
1271 elem->name, elem->nameURI);
1272 if (def == NULL) {
1273
1274 def = xsltCopyStackElem(elem);
1275 xmlHashAddEntry2(ctxt->globalVars,
1276 elem->name, elem->nameURI, def);
1277 } else if ((elem->comp != NULL) &&
1278 (elem->comp->type == XSLT_FUNC_VARIABLE)) {
1279 /*
1280 * Redefinition of variables from a different stylesheet
1281 * should not generate a message.
1282 */
1283 if ((elem->comp->inst != NULL) &&
1284 (def->comp != NULL) && (def->comp->inst != NULL) &&
1285 (elem->comp->inst->doc == def->comp->inst->doc))
1286 {
1287 xsltTransformError(ctxt, style, elem->comp->inst,
1288 "Global variable %s already defined\n", elem->name);
1289 if (style != NULL) style->errors++;
1290 }
1291 }
1292 elem = elem->next;
1293 }
1294
1295 style = xsltNextImport(style);
1296 }
1297
1298 /*
1299 * This part does the actual evaluation
1300 */
1301 xmlHashScan(ctxt->globalVars,
1302 (xmlHashScanner) xsltEvalGlobalVariable, ctxt);
1303
1304 return(0);
1305 }
1306
1307 /**
1308 * xsltRegisterGlobalVariable:
1309 * @style: the XSLT transformation context
1310 * @name: the variable name
1311 * @ns_uri: the variable namespace URI
1312 * @sel: the expression which need to be evaluated to generate a value
1313 * @tree: the subtree if sel is NULL
1314 * @comp: the precompiled value
1315 * @value: the string value if available
1316 *
1317 * Register a new variable value. If @value is NULL it unregisters
1318 * the variable
1319 *
1320 * Returns 0 in case of success, -1 in case of error
1321 */
1322 static int
1323 xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
1324 const xmlChar *ns_uri, const xmlChar *sel,
1325 xmlNodePtr tree, xsltStylePreCompPtr comp,
1326 const xmlChar *value) {
1327 xsltStackElemPtr elem, tmp;
1328 if (style == NULL)
1329 return(-1);
1330 if (name == NULL)
1331 return(-1);
1332 if (comp == NULL)
1333 return(-1);
1334
1335 #ifdef WITH_XSLT_DEBUG_VARIABLE
1336 if (comp->type == XSLT_FUNC_PARAM)
1337 xsltGenericDebug(xsltGenericDebugContext,
1338 "Defining global param %s\n", name);
1339 else
1340 xsltGenericDebug(xsltGenericDebugContext,
1341 "Defining global variable %s\n", name);
1342 #endif
1343
1344 elem = xsltNewStackElem(NULL);
1345 if (elem == NULL)
1346 return(-1);
1347 elem->comp = comp;
1348 elem->name = xmlDictLookup(style->dict, name, -1);
1349 elem->select = xmlDictLookup(style->dict, sel, -1);
1350 if (ns_uri)
1351 elem->nameURI = xmlDictLookup(style->dict, ns_uri, -1);
1352 elem->tree = tree;
1353 tmp = style->variables;
1354 if (tmp == NULL) {
1355 elem->next = NULL;
1356 style->variables = elem;
1357 } else {
1358 while (tmp != NULL) {
1359 if ((elem->comp->type == XSLT_FUNC_VARIABLE) &&
1360 (tmp->comp->type == XSLT_FUNC_VARIABLE) &&
1361 (xmlStrEqual(elem->name, tmp->name)) &&
1362 ((elem->nameURI == tmp->nameURI) ||
1363 (xmlStrEqual(elem->nameURI, tmp->nameURI))))
1364 {
1365 xsltTransformError(NULL, style, comp->inst,
1366 "redefinition of global variable %s\n", elem->name);
1367 style->errors++;
1368 }
1369 if (tmp->next == NULL)
1370 break;
1371 tmp = tmp->next;
1372 }
1373 elem->next = NULL;
1374 tmp->next = elem;
1375 }
1376 if (value != NULL) {
1377 elem->computed = 1;
1378 elem->value = xmlXPathNewString(value);
1379 }
1380 return(0);
1381 }
1382
1383 /**
1384 * xsltProcessUserParamInternal
1385 *
1386 * @ctxt: the XSLT transformation context
1387 * @name: a null terminated parameter name
1388 * @value: a null terminated value (may be an XPath expression)
1389 * @eval: 0 to treat the value literally, else evaluate as XPath expression
1390 *
1391 * If @eval is 0 then @value is treated literally and is stored in the global
1392 * parameter/variable table without any change.
1393 *
1394 * Uf @eval is 1 then @value is treated as an XPath expression and is
1395 * evaluated. In this case, if you want to pass a string which will be
1396 * interpreted literally then it must be enclosed in single or double quotes.
1397 * If the string contains single quotes (double quotes) then it cannot be
1398 * enclosed single quotes (double quotes). If the string which you want to
1399 * be treated literally contains both single and double quotes (e.g. Meet
1400 * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable
1401 * quoting character. You cannot use &apos; or &quot; inside the string
1402 * because the replacement of character entities with their equivalents is
1403 * done at a different stage of processing. The solution is to call
1404 * xsltQuoteUserParams or xsltQuoteOneUserParam.
1405 *
1406 * This needs to be done on parsed stylesheets before starting to apply
1407 * transformations. Normally this will be called (directly or indirectly)
1408 * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams,
1409 * or xsltQuoteOneUserParam.
1410 *
1411 * Returns 0 in case of success, -1 in case of error
1412 */
1413
1414 static
1415 int
1416 xsltProcessUserParamInternal(xsltTransformContextPtr ctxt,
1417 const xmlChar * name,
1418 const xmlChar * value,
1419 int eval) {
1420
1421 xsltStylesheetPtr style;
1422 const xmlChar *prefix;
1423 const xmlChar *href;
1424 xmlXPathCompExprPtr xpExpr;
1425 xmlXPathObjectPtr result;
1426
1427 xsltStackElemPtr elem;
1428 int res;
1429 void *res_ptr;
1430
1431 if (ctxt == NULL)
1432 return(-1);
1433 if (name == NULL)
1434 return(0);
1435 if (value == NULL)
1436 return(0);
1437
1438 style = ctxt->style;
1439
1440 #ifdef WITH_XSLT_DEBUG_VARIABLE
1441 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1442 "Evaluating user parameter %s=%s\n", name, value));
1443 #endif
1444
1445 /*
1446 * Name lookup
1447 */
1448
1449 name = xsltSplitQName(ctxt->dict, name, &prefix);
1450 href = NULL;
1451 if (prefix != NULL) {
1452 xmlNsPtr ns;
1453
1454 ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc),
1455 prefix);
1456 if (ns == NULL) {
1457 xsltTransformError(ctxt, style, NULL,
1458 "user param : no namespace bound to prefix %s\n", prefix);
1459 href = NULL;
1460 } else {
1461 href = ns->href;
1462 }
1463 }
1464
1465 if (name == NULL)
1466 return (-1);
1467
1468 res_ptr = xmlHashLookup2(ctxt->globalVars, name, href);
1469 if (res_ptr != 0) {
1470 xsltTransformError(ctxt, style, NULL,
1471 "Global parameter %s already defined\n", name);
1472 }
1473 if (ctxt->globalVars == NULL)
1474 ctxt->globalVars = xmlHashCreate(20);
1475
1476 /*
1477 * do not overwrite variables with parameters from the command line
1478 */
1479 while (style != NULL) {
1480 elem = ctxt->style->variables;
1481 while (elem != NULL) {
1482 if ((elem->comp != NULL) &&
1483 (elem->comp->type == XSLT_FUNC_VARIABLE) &&
1484 (xmlStrEqual(elem->name, name)) &&
1485 (xmlStrEqual(elem->nameURI, href))) {
1486 return(0);
1487 }
1488 elem = elem->next;
1489 }
1490 style = xsltNextImport(style);
1491 }
1492 style = ctxt->style;
1493 elem = NULL;
1494
1495 /*
1496 * Do the evaluation if @eval is non-zero.
1497 */
1498
1499 result = NULL;
1500 if (eval != 0) {
1501 xpExpr = xmlXPathCompile(value);
1502 if (xpExpr != NULL) {
1503 xmlDocPtr oldXPDoc;
1504 xmlNodePtr oldXPContextNode;
1505 int oldXPProximityPosition, oldXPContextSize, oldXPNsNr;
1506 xmlNsPtr *oldXPNamespaces;
1507 xmlXPathContextPtr xpctxt = ctxt->xpathCtxt;
1508
1509 /*
1510 * Save context states.
1511 */
1512 oldXPDoc = xpctxt->doc;
1513 oldXPContextNode = xpctxt->node;
1514 oldXPProximityPosition = xpctxt->proximityPosition;
1515 oldXPContextSize = xpctxt->contextSize;
1516 oldXPNamespaces = xpctxt->namespaces;
1517 oldXPNsNr = xpctxt->nsNr;
1518
1519 /*
1520 * SPEC XSLT 1.0:
1521 * "At top-level, the expression or template specifying the
1522 * variable value is evaluated with the same context as that used
1523 * to process the root node of the source document: the current
1524 * node is the root node of the source document and the current
1525 * node list is a list containing just the root node of the source
1526 * document."
1527 */
1528 xpctxt->doc = ctxt->initialContextDoc;
1529 xpctxt->node = ctxt->initialContextNode;
1530 xpctxt->contextSize = 1;
1531 xpctxt->proximityPosition = 1;
1532 /*
1533 * There is really no in scope namespace for parameters on the
1534 * command line.
1535 */
1536 xpctxt->namespaces = NULL;
1537 xpctxt->nsNr = 0;
1538
1539 result = xmlXPathCompiledEval(xpExpr, xpctxt);
1540
1541 /*
1542 * Restore Context states.
1543 */
1544 xpctxt->doc = oldXPDoc;
1545 xpctxt->node = oldXPContextNode;
1546 xpctxt->contextSize = oldXPContextSize;
1547 xpctxt->proximityPosition = oldXPProximityPosition;
1548 xpctxt->namespaces = oldXPNamespaces;
1549 xpctxt->nsNr = oldXPNsNr;
1550
1551 xmlXPathFreeCompExpr(xpExpr);
1552 }
1553 if (result == NULL) {
1554 xsltTransformError(ctxt, style, NULL,
1555 "Evaluating user parameter %s failed\n", name);
1556 ctxt->state = XSLT_STATE_STOPPED;
1557 return(-1);
1558 }
1559 }
1560
1561 /*
1562 * If @eval is 0 then @value is to be taken literally and result is NULL
1563 *
1564 * If @eval is not 0, then @value is an XPath expression and has been
1565 * successfully evaluated and result contains the resulting value and
1566 * is not NULL.
1567 *
1568 * Now create an xsltStackElemPtr for insertion into the context's
1569 * global variable/parameter hash table.
1570 */
1571
1572 #ifdef WITH_XSLT_DEBUG_VARIABLE
1573 #ifdef LIBXML_DEBUG_ENABLED
1574 if ((xsltGenericDebugContext == stdout) ||
1575 (xsltGenericDebugContext == stderr))
1576 xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
1577 result, 0);
1578 #endif
1579 #endif
1580
1581 elem = xsltNewStackElem(NULL);
1582 if (elem != NULL) {
1583 elem->name = name;
1584 elem->select = xmlDictLookup(ctxt->dict, value, -1);
1585 if (href != NULL)
1586 elem->nameURI = xmlDictLookup(ctxt->dict, href, -1);
1587 elem->tree = NULL;
1588 elem->computed = 1;
1589 if (eval == 0) {
1590 elem->value = xmlXPathNewString(value);
1591 }
1592 else {
1593 elem->value = result;
1594 }
1595 }
1596
1597 /*
1598 * Global parameters are stored in the XPath context variables pool.
1599 */
1600
1601 res = xmlHashAddEntry2(ctxt->globalVars, name, href, elem);
1602 if (res != 0) {
1603 xsltFreeStackElem(elem);
1604 xsltTransformError(ctxt, style, NULL,
1605 "Global parameter %s already defined\n", name);
1606 }
1607 return(0);
1608 }
1609
1610 /**
1611 * xsltEvalUserParams:
1612 *
1613 * @ctxt: the XSLT transformation context
1614 * @params: a NULL terminated array of parameters name/value tuples
1615 *
1616 * Evaluate the global variables of a stylesheet. This needs to be
1617 * done on parsed stylesheets before starting to apply transformations.
1618 * Each of the parameters is evaluated as an XPath expression and stored
1619 * in the global variables/parameter hash table. If you want your
1620 * parameter used literally, use xsltQuoteUserParams.
1621 *
1622 * Returns 0 in case of success, -1 in case of error
1623 */
1624
1625 int
1626 xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) {
1627 int indx = 0;
1628 const xmlChar *name;
1629 const xmlChar *value;
1630
1631 if (params == NULL)
1632 return(0);
1633 while (params[indx] != NULL) {
1634 name = (const xmlChar *) params[indx++];
1635 value = (const xmlChar *) params[indx++];
1636 if (xsltEvalOneUserParam(ctxt, name, value) != 0)
1637 return(-1);
1638 }
1639 return 0;
1640 }
1641
1642 /**
1643 * xsltQuoteUserParams:
1644 *
1645 * @ctxt: the XSLT transformation context
1646 * @params: a NULL terminated arry of parameters names/values tuples
1647 *
1648 * Similar to xsltEvalUserParams, but the values are treated literally and
1649 * are * *not* evaluated as XPath expressions. This should be done on parsed
1650 * stylesheets before starting to apply transformations.
1651 *
1652 * Returns 0 in case of success, -1 in case of error.
1653 */
1654
1655 int
1656 xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) {
1657 int indx = 0;
1658 const xmlChar *name;
1659 const xmlChar *value;
1660
1661 if (params == NULL)
1662 return(0);
1663 while (params[indx] != NULL) {
1664 name = (const xmlChar *) params[indx++];
1665 value = (const xmlChar *) params[indx++];
1666 if (xsltQuoteOneUserParam(ctxt, name, value) != 0)
1667 return(-1);
1668 }
1669 return 0;
1670 }
1671
1672 /**
1673 * xsltEvalOneUserParam:
1674 * @ctxt: the XSLT transformation context
1675 * @name: a null terminated string giving the name of the parameter
1676 * @value: a null terminated string giving the XPath expression to be evaluated
1677 *
1678 * This is normally called from xsltEvalUserParams to process a single
1679 * parameter from a list of parameters. The @value is evaluated as an
1680 * XPath expression and the result is stored in the context's global
1681 * variable/parameter hash table.
1682 *
1683 * To have a parameter treated literally (not as an XPath expression)
1684 * use xsltQuoteUserParams (or xsltQuoteOneUserParam). For more
1685 * details see description of xsltProcessOneUserParamInternal.
1686 *
1687 * Returns 0 in case of success, -1 in case of error.
1688 */
1689
1690 int
1691 xsltEvalOneUserParam(xsltTransformContextPtr ctxt,
1692 const xmlChar * name,
1693 const xmlChar * value) {
1694 return xsltProcessUserParamInternal(ctxt, name, value,
1695 1 /* xpath eval ? */);
1696 }
1697
1698 /**
1699 * xsltQuoteOneUserParam:
1700 * @ctxt: the XSLT transformation context
1701 * @name: a null terminated string giving the name of the parameter
1702 * @value: a null terminated string giving the parameter value
1703 *
1704 * This is normally called from xsltQuoteUserParams to process a single
1705 * parameter from a list of parameters. The @value is stored in the
1706 * context's global variable/parameter hash table.
1707 *
1708 * Returns 0 in case of success, -1 in case of error.
1709 */
1710
1711 int
1712 xsltQuoteOneUserParam(xsltTransformContextPtr ctxt,
1713 const xmlChar * name,
1714 const xmlChar * value) {
1715 return xsltProcessUserParamInternal(ctxt, name, value,
1716 0 /* xpath eval ? */);
1717 }
1718
1719 /**
1720 * xsltBuildVariable:
1721 * @ctxt: the XSLT transformation context
1722 * @comp: the precompiled form
1723 * @tree: the tree if select is NULL
1724 *
1725 * Computes a new variable value.
1726 *
1727 * Returns the xsltStackElemPtr or NULL in case of error
1728 */
1729 static xsltStackElemPtr
1730 xsltBuildVariable(xsltTransformContextPtr ctxt,
1731 xsltStylePreCompPtr castedComp,
1732 xmlNodePtr tree)
1733 {
1734 #ifdef XSLT_REFACTORED
1735 xsltStyleBasicItemVariablePtr comp =
1736 (xsltStyleBasicItemVariablePtr) castedComp;
1737 #else
1738 xsltStylePreCompPtr comp = castedComp;
1739 #endif
1740 xsltStackElemPtr elem;
1741
1742 #ifdef WITH_XSLT_DEBUG_VARIABLE
1743 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1744 "Building variable %s", comp->name));
1745 if (comp->select != NULL)
1746 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1747 " select %s", comp->select));
1748 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext, "\n"));
1749 #endif
1750
1751 elem = xsltNewStackElem(ctxt);
1752 if (elem == NULL)
1753 return(NULL);
1754 elem->comp = (xsltStylePreCompPtr) comp;
1755 elem->name = comp->name;
1756 elem->select = comp->select;
1757 elem->nameURI = comp->ns;
1758 elem->tree = tree;
1759 elem->value = xsltEvalVariable(ctxt, elem,
1760 (xsltStylePreCompPtr) comp);
1761 if (elem->value != NULL)
1762 elem->computed = 1;
1763 return(elem);
1764 }
1765
1766 /**
1767 * xsltRegisterVariable:
1768 * @ctxt: the XSLT transformation context
1769 * @comp: the compiled XSLT-variable (or param) instruction
1770 * @tree: the tree if select is NULL
1771 * @isParam: indicates if this is a parameter
1772 *
1773 * Computes and registers a new variable.
1774 *
1775 * Returns 0 in case of success, -1 in case of error
1776 */
1777 static int
1778 xsltRegisterVariable(xsltTransformContextPtr ctxt,
1779 xsltStylePreCompPtr castedComp,
1780 xmlNodePtr tree, int isParam)
1781 {
1782 #ifdef XSLT_REFACTORED
1783 xsltStyleBasicItemVariablePtr comp =
1784 (xsltStyleBasicItemVariablePtr) castedComp;
1785 #else
1786 xsltStylePreCompPtr comp = castedComp;
1787 int present;
1788 #endif
1789 xsltStackElemPtr variable;
1790
1791 #ifdef XSLT_REFACTORED
1792 /*
1793 * REFACTORED NOTE: Redefinitions of vars/params are checked
1794 * at compilation time in the refactored code.
1795 * xsl:with-param parameters are checked in xsltApplyXSLTTemplate().
1796 */
1797 #else
1798 present = xsltCheckStackElem(ctxt, comp->name, comp->ns);
1799 if (isParam == 0) {
1800 if ((present != 0) && (present != 3)) {
1801 /* TODO: report QName. */
1802 xsltTransformError(ctxt, NULL, comp->inst,
1803 "XSLT-variable: Redefinition of variable '%s'.\n", comp->name);
1804 return(0);
1805 }
1806 } else if (present != 0) {
1807 if ((present == 1) || (present == 2)) {
1808 /* TODO: report QName. */
1809 xsltTransformError(ctxt, NULL, comp->inst,
1810 "XSLT-param: Redefinition of parameter '%s'.\n", comp->name);
1811 return(0);
1812 }
1813 #ifdef WITH_XSLT_DEBUG_VARIABLE
1814 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1815 "param %s defined by caller\n", comp->name));
1816 #endif
1817 return(0);
1818 }
1819 #endif /* else of XSLT_REFACTORED */
1820
1821 variable = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree);
1822 xsltAddStackElem(ctxt, variable);
1823 return(0);
1824 }
1825
1826 /**
1827 * xsltGlobalVariableLookup:
1828 * @ctxt: the XSLT transformation context
1829 * @name: the variable name
1830 * @ns_uri: the variable namespace URI
1831 *
1832 * Search in the Variable array of the context for the given
1833 * variable value.
1834 *
1835 * Returns the value or NULL if not found
1836 */
1837 static xmlXPathObjectPtr
1838 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1839 const xmlChar *ns_uri) {
1840 xsltStackElemPtr elem;
1841 xmlXPathObjectPtr ret = NULL;
1842
1843 /*
1844 * Lookup the global variables in XPath global variable hash table
1845 */
1846 if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL))
1847 return(NULL);
1848 elem = (xsltStackElemPtr)
1849 xmlHashLookup2(ctxt->globalVars, name, ns_uri);
1850 if (elem == NULL) {
1851 #ifdef WITH_XSLT_DEBUG_VARIABLE
1852 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1853 "global variable not found %s\n", name));
1854 #endif
1855 return(NULL);
1856 }
1857 /*
1858 * URGENT TODO: Move the detection of recursive definitions
1859 * to compile-time.
1860 */
1861 if (elem->computed == 0) {
1862 if (elem->name == xsltComputingGlobalVarMarker) {
1863 xsltTransformError(ctxt, NULL, elem->comp->inst,
1864 "Recursive definition of %s\n", name);
1865 return(NULL);
1866 }
1867 ret = xsltEvalGlobalVariable(elem, ctxt);
1868 } else
1869 ret = elem->value;
1870 return(xmlXPathObjectCopy(ret));
1871 }
1872
1873 /**
1874 * xsltVariableLookup:
1875 * @ctxt: the XSLT transformation context
1876 * @name: the variable name
1877 * @ns_uri: the variable namespace URI
1878 *
1879 * Search in the Variable array of the context for the given
1880 * variable value.
1881 *
1882 * Returns the value or NULL if not found
1883 */
1884 xmlXPathObjectPtr
1885 xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1886 const xmlChar *ns_uri) {
1887 xsltStackElemPtr elem;
1888
1889 if (ctxt == NULL)
1890 return(NULL);
1891
1892 elem = xsltStackLookup(ctxt, name, ns_uri);
1893 if (elem == NULL) {
1894 return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
1895 }
1896 if (elem->computed == 0) {
1897 #ifdef WITH_XSLT_DEBUG_VARIABLE
1898 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1899 "uncomputed variable %s\n", name));
1900 #endif
1901 elem->value = xsltEvalVariable(ctxt, elem, NULL);
1902 elem->computed = 1;
1903 }
1904 if (elem->value != NULL)
1905 return(xmlXPathObjectCopy(elem->value));
1906 #ifdef WITH_XSLT_DEBUG_VARIABLE
1907 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1908 "variable not found %s\n", name));
1909 #endif
1910 return(NULL);
1911 }
1912
1913 /**
1914 * xsltParseStylesheetCallerParam:
1915 * @ctxt: the XSLT transformation context
1916 * @inst: the xsl:with-param instruction element
1917 *
1918 * Processes an xsl:with-param instruction at transformation time.
1919 * The value is compute, but not recorded.
1920 * NOTE that this is also called with an *xsl:param* element
1921 * from exsltFuncFunctionFunction().
1922 *
1923 * Returns the new xsltStackElemPtr or NULL
1924 */
1925
1926 xsltStackElemPtr
1927 xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr inst)
1928 {
1929 #ifdef XSLT_REFACTORED
1930 xsltStyleBasicItemVariablePtr comp;
1931 #else
1932 xsltStylePreCompPtr comp;
1933 #endif
1934 xmlNodePtr tree = NULL; /* The first child node of the instruction or
1935 the instruction itself. */
1936 xsltStackElemPtr param = NULL;
1937
1938 if ((ctxt == NULL) || (inst == NULL) || (inst->type != XML_ELEMENT_NODE))
1939 return(NULL);
1940
1941 #ifdef XSLT_REFACTORED
1942 comp = (xsltStyleBasicItemVariablePtr) inst->psvi;
1943 #else
1944 comp = (xsltStylePreCompPtr) inst->psvi;
1945 #endif
1946
1947 if (comp == NULL) {
1948 xsltTransformError(ctxt, NULL, inst,
1949 "Internal error in xsltParseStylesheetCallerParam(): "
1950 "The XSLT 'with-param' instruction was not compiled.\n");
1951 return(NULL);
1952 }
1953 if (comp->name == NULL) {
1954 xsltTransformError(ctxt, NULL, inst,
1955 "Internal error in xsltParseStylesheetCallerParam(): "
1956 "XSLT 'with-param': The attribute 'name' was not compiled.\n");
1957 return(NULL);
1958 }
1959
1960 #ifdef WITH_XSLT_DEBUG_VARIABLE
1961 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1962 "Handling xsl:with-param %s\n", comp->name));
1963 #endif
1964
1965 if (comp->select == NULL) {
1966 tree = inst->children;
1967 } else {
1968 #ifdef WITH_XSLT_DEBUG_VARIABLE
1969 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
1970 " select %s\n", comp->select));
1971 #endif
1972 tree = inst;
1973 }
1974
1975 param = xsltBuildVariable(ctxt, (xsltStylePreCompPtr) comp, tree);
1976
1977 return(param);
1978 }
1979
1980 /**
1981 * xsltParseGlobalVariable:
1982 * @style: the XSLT stylesheet
1983 * @cur: the "variable" element
1984 *
1985 * Parses a global XSLT 'variable' declaration at compilation time
1986 * and registers it
1987 */
1988 void
1989 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur)
1990 {
1991 #ifdef XSLT_REFACTORED
1992 xsltStyleItemVariablePtr comp;
1993 #else
1994 xsltStylePreCompPtr comp;
1995 #endif
1996
1997 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
1998 return;
1999
2000 #ifdef XSLT_REFACTORED
2001 /*
2002 * Note that xsltStylePreCompute() will be called from
2003 * xslt.c only.
2004 */
2005 comp = (xsltStyleItemVariablePtr) cur->psvi;
2006 #else
2007 xsltStylePreCompute(style, cur);
2008 comp = (xsltStylePreCompPtr) cur->psvi;
2009 #endif
2010 if (comp == NULL) {
2011 xsltTransformError(NULL, style, cur,
2012 "xsl:variable : compilation failed\n");
2013 return;
2014 }
2015
2016 if (comp->name == NULL) {
2017 xsltTransformError(NULL, style, cur,
2018 "xsl:variable : missing name attribute\n");
2019 return;
2020 }
2021
2022 /*
2023 * Parse the content (a sequence constructor) of xsl:variable.
2024 */
2025 if (cur->children != NULL) {
2026 #ifdef XSLT_REFACTORED
2027 xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children);
2028 #else
2029 xsltParseTemplateContent(style, cur);
2030 #endif
2031 }
2032 #ifdef WITH_XSLT_DEBUG_VARIABLE
2033 xsltGenericDebug(xsltGenericDebugContext,
2034 "Registering global variable %s\n", comp->name);
2035 #endif
2036
2037 xsltRegisterGlobalVariable(style, comp->name, comp->ns,
2038 comp->select, cur->children, (xsltStylePreCompPtr) comp,
2039 NULL);
2040 }
2041
2042 /**
2043 * xsltParseGlobalParam:
2044 * @style: the XSLT stylesheet
2045 * @cur: the "param" element
2046 *
2047 * parse an XSLT transformation param declaration and record
2048 * its value.
2049 */
2050
2051 void
2052 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
2053 #ifdef XSLT_REFACTORED
2054 xsltStyleItemParamPtr comp;
2055 #else
2056 xsltStylePreCompPtr comp;
2057 #endif
2058
2059 if ((cur == NULL) || (style == NULL) || (cur->type != XML_ELEMENT_NODE))
2060 return;
2061
2062 #ifdef XSLT_REFACTORED
2063 /*
2064 * Note that xsltStylePreCompute() will be called from
2065 * xslt.c only.
2066 */
2067 comp = (xsltStyleItemParamPtr) cur->psvi;
2068 #else
2069 xsltStylePreCompute(style, cur);
2070 comp = (xsltStylePreCompPtr) cur->psvi;
2071 #endif
2072 if (comp == NULL) {
2073 xsltTransformError(NULL, style, cur,
2074 "xsl:param : compilation failed\n");
2075 return;
2076 }
2077
2078 if (comp->name == NULL) {
2079 xsltTransformError(NULL, style, cur,
2080 "xsl:param : missing name attribute\n");
2081 return;
2082 }
2083
2084 /*
2085 * Parse the content (a sequence constructor) of xsl:param.
2086 */
2087 if (cur->children != NULL) {
2088 #ifdef XSLT_REFACTORED
2089 xsltParseSequenceConstructor(XSLT_CCTXT(style), cur->children);
2090 #else
2091 xsltParseTemplateContent(style, cur);
2092 #endif
2093 }
2094
2095 #ifdef WITH_XSLT_DEBUG_VARIABLE
2096 xsltGenericDebug(xsltGenericDebugContext,
2097 "Registering global param %s\n", comp->name);
2098 #endif
2099
2100 xsltRegisterGlobalVariable(style, comp->name, comp->ns,
2101 comp->select, cur->children, (xsltStylePreCompPtr) comp,
2102 NULL);
2103 }
2104
2105 /**
2106 * xsltParseStylesheetVariable:
2107 * @ctxt: the XSLT transformation context
2108 * @inst: the xsl:variable instruction element
2109 *
2110 * Registers a local XSLT 'variable' instruction at transformation time
2111 * and evaluates its value.
2112 */
2113 void
2114 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr inst)
2115 {
2116 #ifdef XSLT_REFACTORED
2117 xsltStyleItemVariablePtr comp;
2118 #else
2119 xsltStylePreCompPtr comp;
2120 #endif
2121
2122 if ((inst == NULL) || (ctxt == NULL) || (inst->type != XML_ELEMENT_NODE))
2123 return;
2124
2125 comp = inst->psvi;
2126 if (comp == NULL) {
2127 xsltTransformError(ctxt, NULL, inst,
2128 "Internal error in xsltParseStylesheetVariable(): "
2129 "The XSLT 'variable' instruction was not compiled.\n");
2130 return;
2131 }
2132 if (comp->name == NULL) {
2133 xsltTransformError(ctxt, NULL, inst,
2134 "Internal error in xsltParseStylesheetVariable(): "
2135 "The attribute 'name' was not compiled.\n");
2136 return;
2137 }
2138
2139 #ifdef WITH_XSLT_DEBUG_VARIABLE
2140 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2141 "Registering variable '%s'\n", comp->name));
2142 #endif
2143
2144 xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, inst->children, 0);
2145 }
2146
2147 /**
2148 * xsltParseStylesheetParam:
2149 * @ctxt: the XSLT transformation context
2150 * @cur: the XSLT 'param' element
2151 *
2152 * Registers a local XSLT 'param' declaration at transformation time and
2153 * evaluates its value.
2154 */
2155 void
2156 xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur)
2157 {
2158 #ifdef XSLT_REFACTORED
2159 xsltStyleItemParamPtr comp;
2160 #else
2161 xsltStylePreCompPtr comp;
2162 #endif
2163
2164 if ((cur == NULL) || (ctxt == NULL) || (cur->type != XML_ELEMENT_NODE))
2165 return;
2166
2167 comp = cur->psvi;
2168 if ((comp == NULL) || (comp->name == NULL)) {
2169 xsltTransformError(ctxt, NULL, cur,
2170 "Internal error in xsltParseStylesheetParam(): "
2171 "The XSLT 'param' declaration was not compiled correctly.\n");
2172 return;
2173 }
2174
2175 #ifdef WITH_XSLT_DEBUG_VARIABLE
2176 XSLT_TRACE(ctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2177 "Registering param %s\n", comp->name));
2178 #endif
2179
2180 xsltRegisterVariable(ctxt, (xsltStylePreCompPtr) comp, cur->children, 1);
2181 }
2182
2183 /**
2184 * xsltFreeGlobalVariables:
2185 * @ctxt: the XSLT transformation context
2186 *
2187 * Free up the data associated to the global variables
2188 * its value.
2189 */
2190
2191 void
2192 xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) {
2193 xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem);
2194 }
2195
2196 /**
2197 * xsltXPathVariableLookup:
2198 * @ctxt: a void * but the the XSLT transformation context actually
2199 * @name: the variable name
2200 * @ns_uri: the variable namespace URI
2201 *
2202 * This is the entry point when a varibale is needed by the XPath
2203 * interpretor.
2204 *
2205 * Returns the value or NULL if not found
2206 */
2207 xmlXPathObjectPtr
2208 xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
2209 const xmlChar *ns_uri) {
2210 xsltTransformContextPtr tctxt;
2211 xmlXPathObjectPtr valueObj = NULL;
2212
2213 if ((ctxt == NULL) || (name == NULL))
2214 return(NULL);
2215
2216 #ifdef WITH_XSLT_DEBUG_VARIABLE
2217 XSLT_TRACE(((xsltTransformContextPtr)ctxt),XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2218 "Lookup variable '%s'\n", name));
2219 #endif
2220
2221 tctxt = (xsltTransformContextPtr) ctxt;
2222 /*
2223 * Local variables/params ---------------------------------------------
2224 *
2225 * Do the lookup from the top of the stack, but
2226 * don't use params being computed in a call-param
2227 * First lookup expects the variable name and URI to
2228 * come from the disctionnary and hence pointer comparison.
2229 */
2230 if (tctxt->varsNr != 0) {
2231 int i;
2232 xsltStackElemPtr variable = NULL, cur;
2233
2234 for (i = tctxt->varsNr; i > tctxt->varsBase; i--) {
2235 cur = tctxt->varsTab[i-1];
2236 if ((cur->name == name) && (cur->nameURI == ns_uri)) {
2237 #if 0
2238 stack_addr++;
2239 #endif
2240 variable = cur;
2241 goto local_variable_found;
2242 }
2243 cur = cur->next;
2244 }
2245 /*
2246 * Redo the lookup with interned strings to avoid string comparison.
2247 *
2248 * OPTIMIZE TODO: The problem here is, that if we request a
2249 * global variable, then this will be also executed.
2250 */
2251 {
2252 const xmlChar *tmpName = name, *tmpNsName = ns_uri;
2253
2254 name = xmlDictLookup(tctxt->dict, name, -1);
2255 if (ns_uri)
2256 ns_uri = xmlDictLookup(tctxt->dict, ns_uri, -1);
2257 if ((tmpName != name) || (tmpNsName != ns_uri)) {
2258 for (i = tctxt->varsNr; i > tctxt->varsBase; i--) {
2259 cur = tctxt->varsTab[i-1];
2260 if ((cur->name == name) && (cur->nameURI == ns_uri)) {
2261 #if 0
2262 stack_cmp++;
2263 #endif
2264 variable = cur;
2265 goto local_variable_found;
2266 }
2267 }
2268 }
2269 }
2270
2271 local_variable_found:
2272
2273 if (variable) {
2274 if (variable->computed == 0) {
2275
2276 #ifdef WITH_XSLT_DEBUG_VARIABLE
2277 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2278 "uncomputed variable '%s'\n", name));
2279 #endif
2280 variable->value = xsltEvalVariable(tctxt, variable, NULL);
2281 variable->computed = 1;
2282 }
2283 if (variable->value != NULL) {
2284 valueObj = xmlXPathObjectCopy(variable->value);
2285 }
2286 return(valueObj);
2287 }
2288 }
2289 /*
2290 * Global variables/params --------------------------------------------
2291 */
2292 if (tctxt->globalVars) {
2293 valueObj = xsltGlobalVariableLookup(tctxt, name, ns_uri);
2294 }
2295
2296 if (valueObj == NULL) {
2297
2298 #ifdef WITH_XSLT_DEBUG_VARIABLE
2299 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2300 "variable not found '%s'\n", name));
2301 #endif
2302
2303 if (ns_uri) {
2304 xsltTransformError(tctxt, NULL, tctxt->inst,
2305 "Variable '{%s}%s' has not been declared.\n", ns_uri, name);
2306 } else {
2307 xsltTransformError(tctxt, NULL, tctxt->inst,
2308 "Variable '%s' has not been declared.\n", name);
2309 }
2310 } else {
2311
2312 #ifdef WITH_XSLT_DEBUG_VARIABLE
2313 XSLT_TRACE(tctxt,XSLT_TRACE_VARIABLES,xsltGenericDebug(xsltGenericDebugContext,
2314 "found variable '%s'\n", name));
2315 #endif
2316 }
2317
2318 return(valueObj);
2319 }
2320
2321