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