source: trunk/third/libxslt/libxslt/variables.c @ 19102

Revision 19102, 36.4 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19101, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * variables.c: Implementation of the variable storage and lookup
3 *
4 * Reference:
5 *   http://www.w3.org/TR/1999/REC-xslt-19991116
6 *
7 * See Copyright for the status of this software.
8 *
9 * daniel@veillard.com
10 */
11
12#define IN_LIBXSLT
13#include "libxslt.h"
14
15#include <string.h>
16
17#include <libxml/xmlmemory.h>
18#include <libxml/tree.h>
19#include <libxml/valid.h>
20#include <libxml/hash.h>
21#include <libxml/xmlerror.h>
22#include <libxml/xpath.h>
23#include <libxml/xpathInternals.h>
24#include <libxml/parserInternals.h>
25#include "xslt.h"
26#include "xsltInternals.h"
27#include "xsltutils.h"
28#include "variables.h"
29#include "transform.h"
30#include "imports.h"
31#include "preproc.h"
32
33#ifdef WITH_XSLT_DEBUG
34#define WITH_XSLT_DEBUG_VARIABLE
35#endif
36
37/************************************************************************
38 *                                                                      *
39 *                      Module interfaces                               *
40 *                                                                      *
41 ************************************************************************/
42
43/**
44 * xsltNewStackElem:
45 *
46 * Create a new XSLT ParserContext
47 *
48 * Returns the newly allocated xsltParserStackElem or NULL in case of error
49 */
50static xsltStackElemPtr
51xsltNewStackElem(void) {
52    xsltStackElemPtr cur;
53
54    cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
55    if (cur == NULL) {
56        xsltTransformError(NULL, NULL, NULL,
57                "xsltNewStackElem : malloc failed\n");
58        return(NULL);
59    }
60    cur->computed = 0;
61    cur->name = NULL;
62    cur->nameURI = NULL;
63    cur->select = NULL;
64    cur->tree = NULL;
65    cur->value = NULL;
66    cur->comp = NULL;
67    return(cur);
68}
69
70/**
71 * xsltCopyStackElem:
72 * @elem:  an XSLT stack element
73 *
74 * Makes a copy of the stack element
75 *
76 * Returns the copy of NULL
77 */
78static xsltStackElemPtr
79xsltCopyStackElem(xsltStackElemPtr elem) {
80    xsltStackElemPtr cur;
81
82    cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
83    if (cur == NULL) {
84        xsltTransformError(NULL, NULL, NULL,
85                "xsltCopyStackElem : malloc failed\n");
86        return(NULL);
87    }
88    cur->name = xmlStrdup(elem->name);
89    cur->nameURI = xmlStrdup(elem->nameURI);
90    cur->select = xmlStrdup(elem->select);
91    cur->tree = elem->tree;
92    cur->comp = elem->comp;
93    cur->computed = 0;
94    cur->value = NULL;
95    return(cur);
96}
97
98/**
99 * xsltFreeStackElem:
100 * @elem:  an XSLT stack element
101 *
102 * Free up the memory allocated by @elem
103 */
104static void
105xsltFreeStackElem(xsltStackElemPtr elem) {
106    if (elem == NULL)
107        return;
108    if (elem->name != NULL)
109        xmlFree(elem->name);
110    if (elem->nameURI != NULL)
111        xmlFree(elem->nameURI);
112    if (elem->select != NULL)
113        xmlFree(elem->select);
114    if (elem->value != NULL)
115        xmlXPathFreeObject(elem->value);
116
117    xmlFree(elem);
118}
119
120/**
121 * xsltFreeStackElemList:
122 * @elem:  an XSLT stack element
123 *
124 * Free up the memory allocated by @elem
125 */
126void
127xsltFreeStackElemList(xsltStackElemPtr elem) {
128    xsltStackElemPtr next;
129
130    while(elem != NULL) {
131        next = elem->next;
132        xsltFreeStackElem(elem);
133        elem = next;
134    }
135}
136
137/**
138 * xsltCheckStackElem:
139 * @ctxt:  xn XSLT transformation context
140 * @name:  the variable name
141 * @nameURI:  the variable namespace URI
142 *
143 * check wether the variable or param is already defined
144 *
145 * Returns 1 if variable is present, 2 if param is present, 3 if this
146 *         is an inherited param, 0 if not found, -1 in case of failure.
147 */
148static int
149xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
150                   const xmlChar *nameURI) {
151    xsltStackElemPtr cur;
152
153    if ((ctxt == NULL) || (name == NULL))
154        return(-1);
155
156    cur = ctxt->vars;
157    while (cur != NULL) {
158        if (xmlStrEqual(name, cur->name)) {
159            if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
160                ((nameURI != NULL) && (cur->nameURI != NULL) &&
161                 (xmlStrEqual(nameURI, cur->nameURI)))) {
162                if ((cur->comp != NULL) &&
163                    (cur->comp->type == XSLT_FUNC_WITHPARAM))
164                    return(3);
165                if ((cur->comp != NULL) &&
166                    (cur->comp->type == XSLT_FUNC_PARAM))
167                    return(2);
168                return(1);
169            }
170        }
171        cur = cur->next;
172    }
173    return(0);
174}
175
176/**
177 * xsltAddStackElem:
178 * @ctxt:  xn XSLT transformation context
179 * @elem:  a stack element
180 *
181 * add a new element at this level of the stack.
182 *
183 * Returns 0 in case of success, -1 in case of failure.
184 */
185static int
186xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
187    if ((ctxt == NULL) || (elem == NULL))
188        return(-1);
189
190    elem->next = ctxt->varsTab[ctxt->varsNr - 1];
191    ctxt->varsTab[ctxt->varsNr - 1] = elem;
192    ctxt->vars = elem;
193    return(0);
194}
195
196/**
197 * xsltAddStackElemList:
198 * @ctxt:  xn XSLT transformation context
199 * @elems:  a stack element list
200 *
201 * add the new element list at this level of the stack.
202 *
203 * Returns 0 in case of success, -1 in case of failure.
204 */
205int
206xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) {
207    xsltStackElemPtr cur;
208
209    if ((ctxt == NULL) || (elems == NULL))
210        return(-1);
211
212    /* TODO: check doublons */
213    if (ctxt->varsTab[ctxt->varsNr - 1] != NULL) {
214        cur = ctxt->varsTab[ctxt->varsNr - 1];
215        while (cur->next != NULL)
216            cur = cur->next;
217        cur->next = elems;
218    } else {
219        elems->next = ctxt->varsTab[ctxt->varsNr - 1];
220        ctxt->varsTab[ctxt->varsNr - 1] = elems;
221        ctxt->vars = elems;
222    }
223    return(0);
224}
225
226/**
227 * xsltStackLookup:
228 * @ctxt:  an XSLT transformation context
229 * @name:  the local part of the name
230 * @nameURI:  the URI part of the name
231 *
232 * Locate an element in the stack based on its name.
233 */
234static xsltStackElemPtr
235xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
236                const xmlChar *nameURI) {
237    xsltStackElemPtr ret = NULL;
238    int i;
239    xsltStackElemPtr cur;
240
241    if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0))
242        return(NULL);
243
244    /*
245     * Do the lookup from the top of the stack, but
246     * don't use params being computed in a call-param
247     */
248    ;
249
250    for (i = ctxt->varsNr; i > ctxt->varsBase; i--) {
251        cur = ctxt->varsTab[i-1];
252        while (cur != NULL) {
253            if (xmlStrEqual(cur->name, name)) {
254                if (nameURI == NULL) {
255                    if (cur->nameURI == NULL) {
256                        return(cur);
257                    }
258                } else {
259                    if ((cur->nameURI != NULL) &&
260                        (xmlStrEqual(cur->nameURI, nameURI))) {
261                        return(cur);
262                    }
263                }
264
265            }
266            cur = cur->next;
267        }
268    }
269    return(ret);
270}
271
272/************************************************************************
273 *                                                                      *
274 *                      Module interfaces                               *
275 *                                                                      *
276 ************************************************************************/
277
278/**
279 * xsltEvalVariable:
280 * @ctxt:  the XSLT transformation context
281 * @elem:  the variable or parameter.
282 * @precomp: pointer to precompiled data
283 *
284 * Evaluate a variable value.
285 *
286 * Returns the XPath Object value or NULL in case of error
287 */
288static xmlXPathObjectPtr
289xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr elem,
290                 xsltStylePreCompPtr precomp) {
291    xmlXPathObjectPtr result = NULL;
292    int oldProximityPosition, oldContextSize;
293    xmlNodePtr oldInst, oldNode;
294    xsltDocumentPtr oldDoc;
295    int oldNsNr;
296    xmlNsPtr *oldNamespaces;
297
298    if ((ctxt == NULL) || (elem == NULL))
299        return(NULL);
300
301#ifdef WITH_XSLT_DEBUG_VARIABLE
302    xsltGenericDebug(xsltGenericDebugContext,
303        "Evaluating variable %s\n", elem->name);
304#endif
305    if (elem->select != NULL) {
306        xmlXPathCompExprPtr comp = NULL;
307
308        if ((precomp != NULL) && (precomp->comp != NULL)) {
309            comp = precomp->comp;
310        } else {
311            comp = xmlXPathCompile(elem->select);
312        }
313        if (comp == NULL)
314            return(NULL);
315        oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
316        oldContextSize = ctxt->xpathCtxt->contextSize;
317        ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
318        oldDoc = ctxt->document;
319        oldNode = ctxt->node;
320        oldInst = ctxt->inst;
321        oldNsNr = ctxt->xpathCtxt->nsNr;
322        oldNamespaces = ctxt->xpathCtxt->namespaces;
323        if (precomp != NULL) {
324            ctxt->inst = precomp->inst;
325            ctxt->xpathCtxt->namespaces = precomp->nsList;
326            ctxt->xpathCtxt->nsNr = precomp->nsNr;
327        } else {
328            ctxt->inst = NULL;
329            ctxt->xpathCtxt->namespaces = NULL;
330            ctxt->xpathCtxt->nsNr = 0;
331        }
332        result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
333        ctxt->xpathCtxt->contextSize = oldContextSize;
334        ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
335        ctxt->xpathCtxt->nsNr = oldNsNr;
336        ctxt->xpathCtxt->namespaces = oldNamespaces;
337        ctxt->inst = oldInst;
338        ctxt->node = oldNode;
339        ctxt->document = oldDoc;
340        if ((precomp == NULL) || (precomp->comp == NULL))
341            xmlXPathFreeCompExpr(comp);
342        if (result == NULL) {
343            xsltTransformError(ctxt, NULL, precomp->inst,
344                "Evaluating variable %s failed\n", elem->name);
345            ctxt->state = XSLT_STATE_STOPPED;
346#ifdef WITH_XSLT_DEBUG_VARIABLE
347#ifdef LIBXML_DEBUG_ENABLED
348        } else {
349            if ((xsltGenericDebugContext == stdout) ||
350                (xsltGenericDebugContext == stderr))
351                xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
352                                        result, 0);
353#endif
354#endif
355        }
356    } else {
357        if (elem->tree == NULL) {
358            result = xmlXPathNewCString("");
359        } else {
360            /*
361             * This is a result tree fragment.
362             */
363            xmlNodePtr container;
364            xmlNodePtr oldInsert;
365            xmlDocPtr  oldoutput;
366
367            container = xmlNewDocNode(ctxt->document->doc, NULL,
368                              (const xmlChar *) " fake node libxslt", NULL);
369            if (container == NULL)
370                return(NULL);
371            container->parent = NULL;
372
373            oldoutput = ctxt->output;
374            ctxt->output = NULL;
375            oldInsert = ctxt->insert;
376            ctxt->insert = container;
377            xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
378            ctxt->insert = oldInsert;
379            ctxt->output = oldoutput;
380
381            result = xmlXPathNewValueTree(container);
382            if (result == NULL) {
383                result = xmlXPathNewCString("");
384            } else {
385                /*
386                 * Tag the subtree for removal once consumed
387                 */
388                result->boolval = 1;
389            }
390#ifdef WITH_XSLT_DEBUG_VARIABLE
391#ifdef LIBXML_DEBUG_ENABLED
392            if ((xsltGenericDebugContext == stdout) ||
393                (xsltGenericDebugContext == stderr))
394                xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
395                                        result, 0);
396#endif
397#endif
398        }
399    }
400    return(result);
401}
402
403/**
404 * xsltEvalGlobalVariable:
405 * @elem:  the variable or parameter.
406 * @ctxt:  the XSLT transformation context
407 *
408 * Evaluate a global variable value.
409 *
410 * Returns the XPath Object value or NULL in case of error
411 */
412static xmlXPathObjectPtr
413xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) {
414    xmlXPathObjectPtr result = NULL;
415    xsltStylePreCompPtr precomp;
416    int oldProximityPosition, oldContextSize;
417    xmlNodePtr oldInst;
418    int oldNsNr;
419    xmlNsPtr *oldNamespaces;
420
421    if ((ctxt == NULL) || (elem == NULL))
422        return(NULL);
423    if (elem->computed)
424        return(elem->value);
425
426
427#ifdef WITH_XSLT_DEBUG_VARIABLE
428    xsltGenericDebug(xsltGenericDebugContext,
429        "Evaluating global variable %s\n", elem->name);
430#endif
431
432#ifdef WITH_DEBUGGER
433    if ((xslDebugStatus != XSLT_DEBUG_NONE) &&
434        elem->comp && elem->comp->inst)
435        xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt);
436#endif
437
438    precomp = elem->comp;
439    if (elem->select != NULL) {
440        xmlXPathCompExprPtr comp = NULL;
441
442        if ((precomp != NULL) && (precomp->comp != NULL)) {
443            comp = precomp->comp;
444        } else {
445            comp = xmlXPathCompile(elem->select);
446        }
447        if (comp == NULL)
448            return(NULL);
449        oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
450        oldContextSize = ctxt->xpathCtxt->contextSize;
451        oldInst = ctxt->inst;
452        oldNsNr = ctxt->xpathCtxt->nsNr;
453        oldNamespaces = ctxt->xpathCtxt->namespaces;
454        if (precomp != NULL) {
455            ctxt->inst = precomp->inst;
456            ctxt->xpathCtxt->namespaces = precomp->nsList;
457            ctxt->xpathCtxt->nsNr = precomp->nsNr;
458        } else {
459            ctxt->inst = NULL;
460            ctxt->xpathCtxt->namespaces = NULL;
461            ctxt->xpathCtxt->nsNr = 0;
462        }
463        ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
464        result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
465        ctxt->xpathCtxt->contextSize = oldContextSize;
466        ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
467        ctxt->inst = oldInst;
468        ctxt->xpathCtxt->nsNr = oldNsNr;
469        ctxt->xpathCtxt->namespaces = oldNamespaces;
470        if ((precomp == NULL) || (precomp->comp == NULL))
471            xmlXPathFreeCompExpr(comp);
472        if (result == NULL) {
473            xsltTransformError(ctxt, NULL, precomp->inst,
474                "Evaluating global variable %s failed\n", elem->name);
475            ctxt->state = XSLT_STATE_STOPPED;
476#ifdef WITH_XSLT_DEBUG_VARIABLE
477#ifdef LIBXML_DEBUG_ENABLED
478        } else {
479            if ((xsltGenericDebugContext == stdout) ||
480                (xsltGenericDebugContext == stderr))
481                xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
482                                        result, 0);
483#endif
484#endif
485        }
486    } else {
487        if (elem->tree == NULL) {
488            result = xmlXPathNewCString("");
489        } else {
490            /*
491             * This is a result tree fragment.
492             */
493            xmlNodePtr container;
494            xmlNodePtr oldInsert;
495            xmlDocPtr  oldoutput;
496
497            container = xmlNewDocNode(ctxt->document->doc, NULL,
498                              (const xmlChar *) " fake node libxslt", NULL);
499            if (container == NULL)
500                return(NULL);
501            container->parent = NULL;
502
503            oldoutput = ctxt->output;
504            ctxt->output = NULL;
505            oldInsert = ctxt->insert;
506            ctxt->insert = container;
507            xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
508            ctxt->insert = oldInsert;
509            ctxt->output = oldoutput;
510
511            result = xmlXPathNewValueTree(container);
512            if (result == NULL) {
513                result = xmlXPathNewCString("");
514            } else {
515                /*
516                 * Tag the subtree for removal once consumed
517                 */
518                result->boolval = 1;
519            }
520#ifdef WITH_XSLT_DEBUG_VARIABLE
521#ifdef LIBXML_DEBUG_ENABLED
522            if ((xsltGenericDebugContext == stdout) ||
523                (xsltGenericDebugContext == stderr))
524                xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
525                                        result, 0);
526#endif
527#endif
528        }
529    }
530    if (result != NULL) {
531        elem->value = result;
532        elem->computed = 1;
533    }
534    return(result);
535}
536
537/**
538 * xsltEvalGlobalVariables:
539 * @ctxt:  the XSLT transformation context
540 *
541 * Evaluate the global variables of a stylesheet. This need to be
542 * done on parsed stylesheets before starting to apply transformations
543 *
544 * Returns 0 in case of success, -1 in case of error
545 */
546int
547xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
548    xsltStackElemPtr elem;
549    xsltStylesheetPtr style;
550
551    if (ctxt == NULL)
552        return(-1);
553 
554#ifdef WITH_XSLT_DEBUG_VARIABLE
555    xsltGenericDebug(xsltGenericDebugContext,
556        "Registering global variables\n");
557#endif
558    ctxt->node = (xmlNodePtr) ctxt->document->doc;
559    ctxt->xpathCtxt->contextSize = 1;
560    ctxt->xpathCtxt->proximityPosition = 1;
561
562    /*
563     * Walk the list from the stylesheets and populate the hash table
564     */
565    style = ctxt->style;
566    while (style != NULL) {
567        elem = style->variables;
568       
569#ifdef WITH_XSLT_DEBUG_VARIABLE
570        if ((style->doc != NULL) && (style->doc->URL != NULL)) {
571            xsltGenericDebug(xsltGenericDebugContext,
572                             "Registering global variables from %s\n",
573                             style->doc->URL);
574        }
575#endif
576
577        while (elem != NULL) {
578            xsltStackElemPtr def;
579
580            /*
581             * Global variables are stored in the variables pool.
582             */
583            def = (xsltStackElemPtr)
584                    xmlHashLookup2(ctxt->globalVars,
585                                 elem->name, elem->nameURI);
586            if (def == NULL) {
587                int res;
588
589                def = xsltCopyStackElem(elem);
590                res = xmlHashAddEntry2(ctxt->globalVars,
591                                 elem->name, elem->nameURI, def);
592            } else if ((elem->comp != NULL) &&
593                       (elem->comp->type == XSLT_FUNC_VARIABLE)) {
594                /*
595                 * Redefinition of variables from a different stylesheet
596                 * should not generate a message.
597                 */
598                if ((elem->comp->inst != NULL) &&
599                    (def->comp != NULL) && (def->comp->inst != NULL) &&
600                    (elem->comp->inst->doc == def->comp->inst->doc)) {
601                    xsltTransformError(ctxt, style, elem->comp->inst,
602                        "Global variable %s already defined\n", elem->name);
603                    if (style != NULL) style->errors++;
604                }
605            }
606            elem = elem->next;
607        }
608
609        style = xsltNextImport(style);
610    }
611
612    /*
613     * This part does the actual evaluation
614     */
615    ctxt->node = (xmlNodePtr) ctxt->document->doc;
616    ctxt->xpathCtxt->contextSize = 1;
617    ctxt->xpathCtxt->proximityPosition = 1;
618    xmlHashScan(ctxt->globalVars,
619                (xmlHashScanner) xsltEvalGlobalVariable, ctxt);
620
621    return(0);
622}
623
624/**
625 * xsltRegisterGlobalVariable:
626 * @style:  the XSLT transformation context
627 * @name:  the variable name
628 * @ns_uri:  the variable namespace URI
629 * @select:  the expression which need to be evaluated to generate a value
630 * @tree:  the subtree if select is NULL
631 * @comp:  the precompiled value
632 * @value:  the string value if available
633 *
634 * Register a new variable value. If @value is NULL it unregisters
635 * the variable
636 *
637 * Returns 0 in case of success, -1 in case of error
638 */
639static int
640xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
641                     const xmlChar *ns_uri, const xmlChar *select,
642                     xmlNodePtr tree, xsltStylePreCompPtr comp,
643                     const xmlChar *value) {
644    xsltStackElemPtr elem, tmp;
645    if (style == NULL)
646        return(-1);
647    if (name == NULL)
648        return(-1);
649    if (comp == NULL)
650        return(-1);
651
652#ifdef WITH_XSLT_DEBUG_VARIABLE
653    if (comp->type == XSLT_FUNC_PARAM)
654        xsltGenericDebug(xsltGenericDebugContext,
655                         "Defining global param %s\n", name);
656    else
657        xsltGenericDebug(xsltGenericDebugContext,
658                         "Defining global variable %s\n", name);
659#endif
660
661    elem = xsltNewStackElem();
662    if (elem == NULL)
663        return(-1);
664    elem->comp = comp;
665    elem->name = xmlStrdup(name);
666    elem->select = xmlStrdup(select);
667    if (ns_uri)
668        elem->nameURI = xmlStrdup(ns_uri);
669    elem->tree = tree;
670    tmp = style->variables;
671    if (tmp == NULL) {
672        elem->next = NULL;
673        style->variables = elem;
674    } else {
675        while (tmp != NULL) {
676            if ((elem->comp->type == XSLT_FUNC_VARIABLE) &&
677                (tmp->comp->type == XSLT_FUNC_VARIABLE) &&
678                (xmlStrEqual(elem->name, tmp->name)) &&
679                ((elem->nameURI == tmp->nameURI) ||
680                 (xmlStrEqual(elem->nameURI, tmp->nameURI)))) {
681                xsltTransformError(NULL, style, comp->inst,
682                "redefinition of global variable %s\n", elem->name);
683                if (style != NULL) style->errors++;
684            }
685            if (tmp->next == NULL)
686                break;
687            tmp = tmp->next;
688        }
689        elem->next = NULL;
690        tmp->next = elem;
691    }
692    if (value != NULL) {
693        elem->computed = 1;
694        elem->value = xmlXPathNewString(value);
695    }
696    return(0);
697}
698
699/**
700 * xsltProcessUserParamInternal
701 *
702 * @ctxt:  the XSLT transformation context
703 * @name:  a null terminated parameter name
704 * @value: a null terminated value (may be an XPath expression)
705 * @eval:  0 to treat the value literally, else evaluate as XPath expression
706 *
707 * If @eval is 0 then @value is treated literally and is stored in the global
708 * parameter/variable table without any change.
709 *
710 * Uf @eval is 1 then @value is treated as an XPath expression and is
711 * evaluated.  In this case, if you want to pass a string which will be
712 * interpreted literally then it must be enclosed in single or double quotes.
713 * If the string contains single quotes (double quotes) then it cannot be
714 * enclosed single quotes (double quotes).  If the string which you want to
715 * be treated literally contains both single and double quotes (e.g. Meet
716 * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable
717 * quoting character.  You cannot use &apos; or &quot; inside the string
718 * because the replacement of character entities with their equivalents is
719 * done at a different stage of processing.  The solution is to call
720 * xsltQuoteUserParams or xsltQuoteOneUserParam.
721 *
722 * This needs to be done on parsed stylesheets before starting to apply
723 * transformations.  Normally this will be called (directly or indirectly)
724 * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams,
725 * or xsltQuoteOneUserParam.
726 *
727 * Returns 0 in case of success, -1 in case of error
728 */
729
730static
731int
732xsltProcessUserParamInternal(xsltTransformContextPtr ctxt,
733                             const xmlChar * name,
734                             const xmlChar * value,
735                             int eval) {
736
737    xsltStylesheetPtr style;
738    xmlChar *ncname;
739    xmlChar *prefix;
740    const xmlChar *href;
741    xmlXPathCompExprPtr comp;
742    xmlXPathObjectPtr result;
743    int oldProximityPosition;
744    int oldContextSize;
745    int oldNsNr;
746    xmlNsPtr *oldNamespaces;
747    xsltStackElemPtr elem;
748    int res;
749
750    if (ctxt == NULL)
751        return(-1);
752    if (name == NULL)
753        return(0);
754    if (value == NULL)
755        return(0);
756
757    style = ctxt->style;
758
759#ifdef WITH_XSLT_DEBUG_VARIABLE
760    xsltGenericDebug(xsltGenericDebugContext,
761            "Evaluating user parameter %s=%s\n", name, value);
762#endif
763
764    /*
765     * Name lookup
766     */
767
768    ncname = xmlSplitQName2(name, &prefix);
769    href = NULL;
770    if (ncname != NULL) {
771        if (prefix != NULL) {
772            xmlNsPtr ns;
773
774            ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc),
775                             prefix);
776            if (ns == NULL) {
777                xsltTransformError(ctxt, style, NULL,
778                "user param : no namespace bound to prefix %s\n", prefix);
779                href = NULL;
780            } else {
781                href = ns->href;
782            }
783            xmlFree(prefix);
784            prefix = NULL;
785        } else {
786            href = NULL;
787        }
788        xmlFree(ncname);
789        ncname = NULL;
790    } else {
791        href = NULL;
792        ncname = xmlStrdup(name);
793    }
794
795    if (ncname == NULL)
796        return (-1);
797
798    /*
799     * Do the evaluation if @eval is non-zero.
800     */
801
802    result = NULL;
803    if (eval != 0) {
804        comp = xmlXPathCompile(value);
805        if (comp != NULL) {
806            oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
807            oldContextSize = ctxt->xpathCtxt->contextSize;
808            ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
809
810            /*
811             * There is really no in scope namespace for parameters on the
812             * command line.
813             */
814
815            oldNsNr = ctxt->xpathCtxt->nsNr;
816            oldNamespaces = ctxt->xpathCtxt->namespaces;
817            ctxt->xpathCtxt->namespaces = NULL;
818            ctxt->xpathCtxt->nsNr = 0;
819            result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
820            ctxt->xpathCtxt->contextSize = oldContextSize;
821            ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
822            ctxt->xpathCtxt->nsNr = oldNsNr;
823            ctxt->xpathCtxt->namespaces = oldNamespaces;
824            xmlXPathFreeCompExpr(comp);
825        }
826        if (result == NULL) {
827            xsltTransformError(ctxt, style, NULL,
828                "Evaluating user parameter %s failed\n", name);
829            ctxt->state = XSLT_STATE_STOPPED;
830            xmlFree(ncname);
831            return(-1);
832        }
833    }
834
835    /*
836     * If @eval is 0 then @value is to be taken literally and result is NULL
837     *
838     * If @eval is not 0, then @value is an XPath expression and has been
839     * successfully evaluated and result contains the resulting value and
840     * is not NULL.
841     *
842     * Now create an xsltStackElemPtr for insertion into the context's
843     * global variable/parameter hash table.
844     */
845
846#ifdef WITH_XSLT_DEBUG_VARIABLE
847#ifdef LIBXML_DEBUG_ENABLED
848    if ((xsltGenericDebugContext == stdout) ||
849        (xsltGenericDebugContext == stderr))
850            xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
851                                    result, 0);
852#endif
853#endif
854
855    elem = xsltNewStackElem();
856    if (elem != NULL) {
857        elem->name = xmlStrdup(ncname);
858        if (value != NULL)
859            elem->select = xmlStrdup(value);
860        else
861            elem->select = NULL;
862        if (href)
863            elem->nameURI = xmlStrdup(href);
864        elem->tree = NULL;
865        elem->computed = 1;
866        if (eval == 0) {
867            elem->value = xmlXPathNewString(value);
868        }
869        else {
870            elem->value = result;
871        }
872    }
873
874    /*
875     * Global parameters are stored in the XPath context variables pool.
876     */
877
878    res = xmlHashAddEntry2(ctxt->globalVars, ncname, href, elem);
879    if (res != 0) {
880        xsltFreeStackElem(elem);
881        xsltTransformError(ctxt, style, NULL,
882            "Global parameter %s already defined\n", ncname);
883    }
884    xmlFree(ncname);
885    return(0);
886}
887
888/**
889 * xsltEvalUserParams:
890 *
891 * @ctxt:  the XSLT transformation context
892 * @params:  a NULL terminated array of parameters name/value tuples
893 *
894 * Evaluate the global variables of a stylesheet. This needs to be
895 * done on parsed stylesheets before starting to apply transformations.
896 * Each of the parameters is evaluated as an XPath expression and stored
897 * in the global variables/parameter hash table.  If you want your
898 * parameter used literally, use xsltQuoteUserParams.
899 *
900 * Returns 0 in case of success, -1 in case of error
901 */
902 
903int
904xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) {
905    int indx = 0;
906    const xmlChar *name;
907    const xmlChar *value;
908
909    if (params == NULL)
910        return(0);
911    while (params[indx] != NULL) {
912        name = (const xmlChar *) params[indx++];
913        value = (const xmlChar *) params[indx++];
914        if (xsltEvalOneUserParam(ctxt, name, value) != 0)
915            return(-1);
916    }
917    return 0;
918}
919
920/**
921 * xsltQuoteUserParams:
922 *
923 * @ctxt:  the XSLT transformation context
924 * @params:  a NULL terminated arry of parameters names/values tuples
925 *
926 * Similar to xsltEvalUserParams, but the values are treated literally and
927 * are * *not* evaluated as XPath expressions. This should be done on parsed
928 * stylesheets before starting to apply transformations.
929 *
930 * Returns 0 in case of success, -1 in case of error.
931 */
932 
933int
934xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) {
935    int indx = 0;
936    const xmlChar *name;
937    const xmlChar *value;
938
939    if (params == NULL)
940        return(0);
941    while (params[indx] != NULL) {
942        name = (const xmlChar *) params[indx++];
943        value = (const xmlChar *) params[indx++];
944        if (xsltQuoteOneUserParam(ctxt, name, value) != 0)
945            return(-1);
946    }
947    return 0;
948}
949
950/**
951 * xsltEvalOneUserParam:
952 * @ctxt:  the XSLT transformation context
953 * @name:  a null terminated string giving the name of the parameter
954 * @value:  a null terminated string giving the XPath expression to be evaluated
955 *
956 * This is normally called from xsltEvalUserParams to process a single
957 * parameter from a list of parameters.  The @value is evaluated as an
958 * XPath expression and the result is stored in the context's global
959 * variable/parameter hash table.
960 *
961 * To have a parameter treated literally (not as an XPath expression)
962 * use xsltQuoteUserParams (or xsltQuoteOneUserParam).  For more
963 * details see description of xsltProcessOneUserParamInternal.
964 *
965 * Returns 0 in case of success, -1 in case of error.
966 */
967
968int
969xsltEvalOneUserParam(xsltTransformContextPtr ctxt,
970                     const xmlChar * name,
971                     const xmlChar * value) {
972    return xsltProcessUserParamInternal(ctxt, name, value,
973                                        1 /* xpath eval ? */);
974}
975
976/**
977 * xsltQuoteOneUserParam:
978 * @ctxt:  the XSLT transformation context
979 * @name:  a null terminated string giving the name of the parameter
980 * @value:  a null terminated string giving the parameter value
981 *
982 * This is normally called from xsltQuoteUserParams to process a single
983 * parameter from a list of parameters.  The @value is stored in the
984 * context's global variable/parameter hash table.
985 *
986 * Returns 0 in case of success, -1 in case of error.
987 */
988
989int
990xsltQuoteOneUserParam(xsltTransformContextPtr ctxt,
991                         const xmlChar * name,
992                         const xmlChar * value) {
993    return xsltProcessUserParamInternal(ctxt, name, value,
994                                        0 /* xpath eval ? */);
995}
996
997/**
998 * xsltBuildVariable:
999 * @ctxt:  the XSLT transformation context
1000 * @comp:  the precompiled form
1001 * @tree:  the tree if select is NULL
1002 *
1003 * Computes a new variable value.
1004 *
1005 * Returns the xsltStackElemPtr or NULL in case of error
1006 */
1007static xsltStackElemPtr
1008xsltBuildVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
1009                  xmlNodePtr tree) {
1010    xsltStackElemPtr elem;
1011
1012#ifdef WITH_XSLT_DEBUG_VARIABLE
1013    xsltGenericDebug(xsltGenericDebugContext,
1014                     "Building variable %s", comp->name);
1015    if (comp->select != NULL)
1016        xsltGenericDebug(xsltGenericDebugContext,
1017                         " select %s", comp->select);
1018    xsltGenericDebug(xsltGenericDebugContext, "\n");
1019#endif
1020
1021    elem = xsltNewStackElem();
1022    if (elem == NULL)
1023        return(NULL);
1024    elem->comp = comp;
1025    elem->name = xmlStrdup(comp->name);
1026    if (comp->select != NULL)
1027        elem->select = xmlStrdup(comp->select);
1028    else
1029        elem->select = NULL;
1030    if (comp->ns)
1031        elem->nameURI = xmlStrdup(comp->ns);
1032    elem->tree = tree;
1033    if (elem->computed == 0) {
1034        elem->value = xsltEvalVariable(ctxt, elem, comp);
1035        if (elem->value != NULL)
1036            elem->computed = 1;
1037    }
1038    return(elem);
1039}
1040
1041/**
1042 * xsltRegisterVariable:
1043 * @ctxt:  the XSLT transformation context
1044 * @comp:  pointer to precompiled data
1045 * @tree:  the tree if select is NULL
1046 * @param:  this is a parameter actually
1047 *
1048 * Computes and register a new variable value.
1049 *
1050 * Returns 0 in case of success, -1 in case of error
1051 */
1052static int
1053xsltRegisterVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
1054                     xmlNodePtr tree, int param) {
1055    xsltStackElemPtr elem;
1056    int present;
1057
1058    present = xsltCheckStackElem(ctxt, comp->name, comp->ns);
1059    if (param == 0) {
1060        if ((present != 0) && (present != 3)) {
1061            xsltTransformError(ctxt, NULL, comp->inst,
1062                "xsl:variable : redefining %s\n", comp->name);
1063            return(0);
1064        }
1065    } else if (present != 0) {
1066        if ((present == 1) || (present == 2)) {
1067            xsltTransformError(ctxt, NULL, comp->inst,
1068                "xsl:param : redefining %s\n", comp->name);
1069            return(0);
1070        }
1071#ifdef WITH_XSLT_DEBUG_VARIABLE
1072        xsltGenericDebug(xsltGenericDebugContext,
1073                 "param %s defined by caller\n", comp->name);
1074#endif
1075        return(0);
1076    }
1077    elem = xsltBuildVariable(ctxt, comp, tree);
1078    xsltAddStackElem(ctxt, elem);
1079    return(0);
1080}
1081
1082/**
1083 * xsltGlobalVariableLookup:
1084 * @ctxt:  the XSLT transformation context
1085 * @name:  the variable name
1086 * @ns_uri:  the variable namespace URI
1087 *
1088 * Search in the Variable array of the context for the given
1089 * variable value.
1090 *
1091 * Returns the value or NULL if not found
1092 */
1093static xmlXPathObjectPtr
1094xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1095                         const xmlChar *ns_uri) {
1096    xsltStackElemPtr elem;
1097    xmlXPathObjectPtr ret = NULL;
1098
1099    /*
1100     * Lookup the global variables in XPath global variable hash table
1101     */
1102    if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL))
1103        return(NULL);
1104    elem = (xsltStackElemPtr)
1105            xmlHashLookup2(ctxt->globalVars, name, ns_uri);
1106    if (elem == NULL) {
1107#ifdef WITH_XSLT_DEBUG_VARIABLE
1108        xsltGenericDebug(xsltGenericDebugContext,
1109                         "global variable not found %s\n", name);
1110#endif
1111        return(NULL);
1112    }
1113    if (elem->computed == 0)
1114        ret = xsltEvalGlobalVariable(elem, ctxt);
1115    else
1116        ret = elem->value;
1117    return(xmlXPathObjectCopy(ret));
1118}
1119
1120/**
1121 * xsltVariableLookup:
1122 * @ctxt:  the XSLT transformation context
1123 * @name:  the variable name
1124 * @ns_uri:  the variable namespace URI
1125 *
1126 * Search in the Variable array of the context for the given
1127 * variable value.
1128 *
1129 * Returns the value or NULL if not found
1130 */
1131xmlXPathObjectPtr
1132xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
1133                   const xmlChar *ns_uri) {
1134    xsltStackElemPtr elem;
1135
1136    if (ctxt == NULL)
1137        return(NULL);
1138
1139    elem = xsltStackLookup(ctxt, name, ns_uri);
1140    if (elem == NULL) {
1141        return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
1142    }
1143    if (elem->computed == 0) {
1144#ifdef WITH_XSLT_DEBUG_VARIABLE
1145        xsltGenericDebug(xsltGenericDebugContext,
1146                         "uncomputed variable %s\n", name);
1147#endif
1148        elem->value = xsltEvalVariable(ctxt, elem, NULL);
1149        elem->computed = 1;
1150    }
1151    if (elem->value != NULL)
1152        return(xmlXPathObjectCopy(elem->value));
1153#ifdef WITH_XSLT_DEBUG_VARIABLE
1154    xsltGenericDebug(xsltGenericDebugContext,
1155                     "variable not found %s\n", name);
1156#endif
1157    return(NULL);
1158}
1159
1160/**
1161 * xsltParseStylesheetCallerParam:
1162 * @ctxt:  the XSLT transformation context
1163 * @cur:  the "param" element
1164 *
1165 * parse an XSLT transformation param declaration, compute
1166 * its value but doesn't record it.
1167 *
1168 * Returns the new xsltStackElemPtr or NULL
1169 */
1170
1171xsltStackElemPtr
1172xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1173    xmlNodePtr tree = NULL;
1174    xsltStackElemPtr elem = NULL;
1175    xsltStylePreCompPtr comp;
1176
1177    if ((cur == NULL) || (ctxt == NULL))
1178        return(NULL);
1179    comp = (xsltStylePreCompPtr) cur->_private;
1180    if (comp == NULL) {
1181        xsltTransformError(ctxt, NULL, cur,
1182            "xsl:param : compilation error\n");
1183        return(NULL);
1184    }
1185
1186    if (comp->name == NULL) {
1187        xsltTransformError(ctxt, NULL, cur,
1188            "xsl:param : missing name attribute\n");
1189        return(NULL);
1190    }
1191
1192#ifdef WITH_XSLT_DEBUG_VARIABLE
1193    xsltGenericDebug(xsltGenericDebugContext,
1194            "Handling param %s\n", comp->name);
1195#endif
1196
1197    if (comp->select == NULL) {
1198        tree = cur->children;
1199    } else {
1200#ifdef WITH_XSLT_DEBUG_VARIABLE
1201        xsltGenericDebug(xsltGenericDebugContext,
1202            "        select %s\n", comp->select);
1203#endif
1204        tree = cur;
1205    }
1206
1207    elem = xsltBuildVariable(ctxt, comp, tree);
1208
1209    return(elem);
1210}
1211
1212/**
1213 * xsltParseGlobalVariable:
1214 * @style:  the XSLT stylesheet
1215 * @cur:  the "variable" element
1216 *
1217 * parse an XSLT transformation variable declaration and record
1218 * its value.
1219 */
1220
1221void
1222xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
1223    xsltStylePreCompPtr comp;
1224
1225    if ((cur == NULL) || (style == NULL))
1226        return;
1227
1228    xsltStylePreCompute(style, cur);
1229    comp = (xsltStylePreCompPtr) cur->_private;
1230    if (comp == NULL) {
1231        xsltTransformError(NULL, style, cur,
1232             "xsl:variable : compilation failed\n");
1233        return;
1234    }
1235
1236    if (comp->name == NULL) {
1237        xsltTransformError(NULL, style, cur,
1238            "xsl:variable : missing name attribute\n");
1239        return;
1240    }
1241
1242#ifdef WITH_XSLT_DEBUG_VARIABLE
1243    xsltGenericDebug(xsltGenericDebugContext,
1244        "Registering global variable %s\n", comp->name);
1245#endif
1246
1247    xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
1248                               cur->children, comp, NULL);
1249}
1250
1251/**
1252 * xsltParseGlobalParam:
1253 * @style:  the XSLT stylesheet
1254 * @cur:  the "param" element
1255 *
1256 * parse an XSLT transformation param declaration and record
1257 * its value.
1258 */
1259
1260void
1261xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
1262    xsltStylePreCompPtr comp;
1263
1264    if ((cur == NULL) || (style == NULL))
1265        return;
1266
1267    xsltStylePreCompute(style, cur);
1268    comp = (xsltStylePreCompPtr) cur->_private;
1269    if (comp == NULL) {
1270        xsltTransformError(NULL, style, cur,
1271             "xsl:param : compilation failed\n");
1272        return;
1273    }
1274
1275    if (comp->name == NULL) {
1276        xsltTransformError(NULL, style, cur,
1277            "xsl:param : missing name attribute\n");
1278        return;
1279    }
1280
1281#ifdef WITH_XSLT_DEBUG_VARIABLE
1282    xsltGenericDebug(xsltGenericDebugContext,
1283        "Registering global param %s\n", comp->name);
1284#endif
1285
1286    xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
1287                               cur->children, comp, NULL);
1288}
1289
1290/**
1291 * xsltParseStylesheetVariable:
1292 * @ctxt:  the XSLT transformation context
1293 * @cur:  the "variable" element
1294 *
1295 * parse an XSLT transformation variable declaration and record
1296 * its value.
1297 */
1298
1299void
1300xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1301    xsltStylePreCompPtr comp;
1302
1303    if ((cur == NULL) || (ctxt == NULL))
1304        return;
1305
1306    comp = (xsltStylePreCompPtr) cur->_private;
1307    if (comp == NULL) {
1308        xsltTransformError(ctxt, NULL, cur,
1309             "xsl:variable : compilation failed\n");
1310        return;
1311    }
1312
1313    if (comp->name == NULL) {
1314        xsltTransformError(ctxt, NULL, cur,
1315            "xsl:variable : missing name attribute\n");
1316        return;
1317    }
1318
1319#ifdef WITH_XSLT_DEBUG_VARIABLE
1320    xsltGenericDebug(xsltGenericDebugContext,
1321        "Registering variable %s\n", comp->name);
1322#endif
1323
1324    xsltRegisterVariable(ctxt, comp, cur->children, 0);
1325}
1326
1327/**
1328 * xsltParseStylesheetParam:
1329 * @ctxt:  the XSLT transformation context
1330 * @cur:  the "param" element
1331 *
1332 * parse an XSLT transformation param declaration and record
1333 * its value.
1334 */
1335
1336void
1337xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
1338    xsltStylePreCompPtr comp;
1339
1340    if ((cur == NULL) || (ctxt == NULL))
1341        return;
1342
1343    comp = (xsltStylePreCompPtr) cur->_private;
1344    if (comp == NULL) {
1345        xsltTransformError(ctxt, NULL, cur,
1346             "xsl:param : compilation failed\n");
1347        return;
1348    }
1349
1350    if (comp->name == NULL) {
1351        xsltTransformError(ctxt, NULL, cur,
1352            "xsl:param : missing name attribute\n");
1353        return;
1354    }
1355
1356#ifdef WITH_XSLT_DEBUG_VARIABLE
1357    xsltGenericDebug(xsltGenericDebugContext,
1358        "Registering param %s\n", comp->name);
1359#endif
1360
1361    xsltRegisterVariable(ctxt, comp, cur->children, 1);
1362}
1363
1364/**
1365 * xsltFreeGlobalVariables:
1366 * @ctxt:  the XSLT transformation context
1367 *
1368 * Free up the data associated to the global variables
1369 * its value.
1370 */
1371
1372void
1373xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) {
1374    xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem);
1375}
1376
1377/**
1378 * xsltXPathVariableLookup:
1379 * @ctxt:  a void * but the the XSLT transformation context actually
1380 * @name:  the variable name
1381 * @ns_uri:  the variable namespace URI
1382 *
1383 * This is the entry point when a varibale is needed by the XPath
1384 * interpretor.
1385 *
1386 * Returns the value or NULL if not found
1387 */
1388xmlXPathObjectPtr
1389xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
1390                        const xmlChar *ns_uri) {
1391    xsltTransformContextPtr context;
1392    xmlXPathObjectPtr ret;
1393
1394    if ((ctxt == NULL) || (name == NULL))
1395        return(NULL);
1396
1397#ifdef WITH_XSLT_DEBUG_VARIABLE
1398    xsltGenericDebug(xsltGenericDebugContext,
1399            "Lookup variable %s\n", name);
1400#endif
1401    context = (xsltTransformContextPtr) ctxt;
1402    ret = xsltVariableLookup(context, name, ns_uri);
1403    if (ret == NULL) {
1404        xsltTransformError(ctxt, NULL, NULL,
1405            "unregistered variable %s\n", name);
1406    }
1407#ifdef WITH_XSLT_DEBUG_VARIABLE
1408    if (ret != NULL)
1409        xsltGenericDebug(xsltGenericDebugContext,
1410            "found variable %s\n", name);
1411#endif
1412    return(ret);
1413}
1414
1415
Note: See TracBrowser for help on using the repository browser.