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