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