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