fixed bug 119699 (missing error on shadowed variable) removed dependency
[platform/upstream/libxslt.git] / libxslt / variables.c
index f6560af..a543ae4 100644 (file)
@@ -6,10 +6,11 @@
  *
  * See Copyright for the status of this software.
  *
- * Daniel.Veillard@imag.fr
+ * daniel@veillard.com
  */
 
-#include "xsltconfig.h"
+#define IN_LIBXSLT
+#include "libxslt.h"
 
 #include <string.h>
 
 #include <libxml/xpath.h>
 #include <libxml/xpathInternals.h>
 #include <libxml/parserInternals.h>
-#include <libxml/xmlversion.h>
 #include "xslt.h"
 #include "xsltInternals.h"
 #include "xsltutils.h"
 #include "variables.h"
 #include "transform.h"
 #include "imports.h"
+#include "preproc.h"
 
-#define DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_VARIABLE
+#endif
+
+/************************************************************************
+ *                                                                     *
+ *                     Result Value Tree interfaces                    *
+ *                                                                     *
+ ************************************************************************/
+/**
+ * xsltCreateRVT:
+ * @ctxt:  an XSLT transformation context
+ *
+ * Create a result value tree
+ *
+ * Returns the result value tree or NULL in case of error
+ */
+xmlDocPtr
+xsltCreateRVT(xsltTransformContextPtr ctxt)
+{
+    xmlDocPtr container;
+
+    if (ctxt == NULL) return(NULL);
+
+    container = xmlNewDoc(NULL);
+    if (container == NULL)
+       return(NULL);
+
+    container->name = (char *) xmlStrdup(BAD_CAST " fake node libxslt");
+    container->doc = container;
+    container->parent = NULL;
+    return(container);
+}
+
+/**
+ * xsltRegisterTmpRVT:
+ * @ctxt:  an XSLT transformation context
+ * @RVT:  a result value tree
+ *
+ * Register the result value tree for destruction at the end of the context
+ *
+ * Returns 0 in case of success and -1 in case of error.
+ */
+int
+xsltRegisterTmpRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
+{
+    if ((ctxt == NULL) || (RVT == NULL)) return(-1);
+
+    RVT->next = (xmlNodePtr) ctxt->tmpRVT;
+    if (ctxt->tmpRVT != NULL)
+       ctxt->tmpRVT->prev = (xmlNodePtr) RVT;
+    ctxt->tmpRVT = RVT;
+    return(0);
+}
+
+/**
+ * xsltRegisterPersistRVT:
+ * @ctxt:  an XSLT transformation context
+ * @RVT:  a result value tree
+ *
+ * Register the result value tree for destruction at the end of the processing
+ *
+ * Returns 0 in case of success and -1 in case of error.
+ */
+int
+xsltRegisterPersistRVT(xsltTransformContextPtr ctxt, xmlDocPtr RVT)
+{
+    if ((ctxt == NULL) || (RVT == NULL)) return(-1);
+
+    RVT->next = (xmlNodePtr) ctxt->persistRVT;
+    if (ctxt->persistRVT != NULL)
+       ctxt->persistRVT->prev = (xmlNodePtr) RVT;
+    ctxt->persistRVT = RVT;
+    return(0);
+}
+
+/**
+ * xsltFreeRVTs:
+ * @ctxt:  an XSLT transformation context
+ *
+ * Free all the registered result value tree of the transformation
+ */
+void
+xsltFreeRVTs(xsltTransformContextPtr ctxt)
+{
+    xmlDocPtr cur, next;
+
+    if (ctxt == NULL) return;
+
+    cur = ctxt->tmpRVT;
+    while (cur != NULL) {
+        next = (xmlDocPtr) cur->next;
+       xmlFreeDoc(cur);
+       cur = next;
+    }
+    cur = ctxt->persistRVT;
+    while (cur != NULL) {
+        next = (xmlDocPtr) cur->next;
+       xmlFreeDoc(cur);
+       cur = next;
+    }
+}
 
 /************************************************************************
  *                                                                     *
  *
  * Returns the newly allocated xsltParserStackElem or NULL in case of error
  */
-xsltStackElemPtr
+static xsltStackElemPtr
 xsltNewStackElem(void) {
     xsltStackElemPtr cur;
 
     cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
     if (cur == NULL) {
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
                "xsltNewStackElem : malloc failed\n");
        return(NULL);
     }
-    memset(cur, 0, sizeof(xsltStackElem));
     cur->computed = 0;
+    cur->name = NULL;
+    cur->nameURI = NULL;
+    cur->select = NULL;
+    cur->tree = NULL;
+    cur->value = NULL;
+    cur->comp = NULL;
+    return(cur);
+}
+
+/**
+ * xsltCopyStackElem:
+ * @elem:  an XSLT stack element
+ *
+ * Makes a copy of the stack element
+ *
+ * Returns the copy of NULL
+ */
+static xsltStackElemPtr
+xsltCopyStackElem(xsltStackElemPtr elem) {
+    xsltStackElemPtr cur;
+
+    cur = (xsltStackElemPtr) xmlMalloc(sizeof(xsltStackElem));
+    if (cur == NULL) {
+       xsltTransformError(NULL, NULL, NULL,
+               "xsltCopyStackElem : malloc failed\n");
+       return(NULL);
+    }
+    cur->name = xmlStrdup(elem->name);
+    cur->nameURI = xmlStrdup(elem->nameURI);
+    cur->select = xmlStrdup(elem->select);
+    cur->tree = elem->tree;
+    cur->comp = elem->comp;
+    cur->computed = 0;
+    cur->value = NULL;
     return(cur);
 }
 
@@ -65,7 +200,7 @@ xsltNewStackElem(void) {
  *
  * Free up the memory allocated by @elem
  */
-void
+static void
 xsltFreeStackElem(xsltStackElemPtr elem) {
     if (elem == NULL)
        return;
@@ -78,7 +213,6 @@ xsltFreeStackElem(xsltStackElemPtr elem) {
     if (elem->value != NULL)
        xmlXPathFreeObject(elem->value);
 
-    memset(elem, -1, sizeof(xsltStackElem));
     xmlFree(elem);
 }
 
@@ -100,6 +234,52 @@ xsltFreeStackElemList(xsltStackElemPtr elem) {
 }
 
 /**
+ * xsltStackLookup:
+ * @ctxt:  an XSLT transformation context
+ * @name:  the local part of the name
+ * @nameURI:  the URI part of the name
+ *
+ * Locate an element in the stack based on its name.
+ */
+static xsltStackElemPtr
+xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
+               const xmlChar *nameURI) {
+    xsltStackElemPtr ret = NULL;
+    int i;
+    xsltStackElemPtr cur;
+
+    if ((ctxt == NULL) || (name == NULL) || (ctxt->varsNr == 0))
+       return(NULL);
+
+    /*
+     * Do the lookup from the top of the stack, but
+     * don't use params being computed in a call-param
+     */
+    ;
+
+    for (i = ctxt->varsNr; i > ctxt->varsBase; i--) {
+       cur = ctxt->varsTab[i-1];
+       while (cur != NULL) {
+           if (xmlStrEqual(cur->name, name)) {
+               if (nameURI == NULL) {
+                   if (cur->nameURI == NULL) {
+                       return(cur);
+                   }
+               } else {
+                   if ((cur->nameURI != NULL) &&
+                       (xmlStrEqual(cur->nameURI, nameURI))) {
+                       return(cur);
+                   }
+               }
+
+           }
+           cur = cur->next;
+       }
+    }
+    return(ret);
+}
+
+/**
  * xsltCheckStackElem:
  * @ctxt:  xn XSLT transformation context
  * @name:  the variable name
@@ -107,9 +287,10 @@ xsltFreeStackElemList(xsltStackElemPtr elem) {
  *
  * check wether the variable or param is already defined
  *
- * Returns 1 if present, 0 if not, -1 in case of failure.
+ * Returns 1 if variable is present, 2 if param is present, 3 if this
+ *         is an inherited param, 0 if not found, -1 in case of failure.
  */
-int
+static int
 xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
                   const xmlChar *nameURI) {
     xsltStackElemPtr cur;
@@ -117,18 +298,17 @@ xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
     if ((ctxt == NULL) || (name == NULL))
        return(-1);
 
-    cur = ctxt->vars;
-    while (cur != NULL) {
-       if (xmlStrEqual(name, cur->name)) {
-           if (((nameURI == NULL) && (cur->nameURI == NULL)) ||
-               ((nameURI != NULL) && (cur->nameURI != NULL) &&
-                (xmlStrEqual(nameURI, cur->nameURI)))) {
-               return(1);
-           }
-       }
-       cur = cur->next;
+    cur = xsltStackLookup(ctxt, name, nameURI);
+    if (cur == NULL)
+        return(0);
+    if (cur->comp != NULL) {
+        if (cur->comp->type == XSLT_FUNC_WITHPARAM)
+           return(3);
+       else if (cur->comp->type == XSLT_FUNC_PARAM)
+           return(2);
     }
-    return(0);
+    
+    return(1);
 }
 
 /**
@@ -140,7 +320,7 @@ xsltCheckStackElem(xsltTransformContextPtr ctxt, const xmlChar *name,
  *
  * Returns 0 in case of success, -1 in case of failure.
  */
-int
+static int
 xsltAddStackElem(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
     if ((ctxt == NULL) || (elem == NULL))
        return(-1);
@@ -181,156 +361,278 @@ xsltAddStackElemList(xsltTransformContextPtr ctxt, xsltStackElemPtr elems) {
     return(0);
 }
 
+/************************************************************************
+ *                                                                     *
+ *                     Module interfaces                               *
+ *                                                                     *
+ ************************************************************************/
+
 /**
- * xsltStackLookup:
- * @ctxt:  an XSLT transformation context
- * @name:  the local part of the name
- * @nameURI:  the URI part of the name
+ * xsltEvalVariable:
+ * @ctxt:  the XSLT transformation context
+ * @elem:  the variable or parameter.
+ * @precomp: pointer to precompiled data
  *
- * Locate an element in the stack based on its name.
+ * Evaluate a variable value.
+ *
+ * Returns the XPath Object value or NULL in case of error
  */
-xsltStackElemPtr
-xsltStackLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
-               const xmlChar *nameURI) {
-    xsltStackElemPtr ret = NULL;
-    int i;
-    xsltStackElemPtr cur;
+static xmlXPathObjectPtr
+xsltEvalVariable(xsltTransformContextPtr ctxt, xsltStackElemPtr elem,
+                xsltStylePreCompPtr precomp) {
+    xmlXPathObjectPtr result = NULL;
+    int oldProximityPosition, oldContextSize;
+    xmlNodePtr oldInst, oldNode;
+    xsltDocumentPtr oldDoc;
+    int oldNsNr;
+    xmlNsPtr *oldNamespaces;
 
-    if ((ctxt == NULL) || (name == NULL))
+    if ((ctxt == NULL) || (elem == NULL))
        return(NULL);
 
-    /*
-     * Do the lookup from the top of the stack, but
-     * don't use params being computed in a call-param
-     */
-    i = ctxt->varsNr - 1;
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+    xsltGenericDebug(xsltGenericDebugContext,
+       "Evaluating variable %s\n", elem->name);
+#endif
+    if (elem->select != NULL) {
+       xmlXPathCompExprPtr comp = NULL;
 
-    for (;i >= 0;i--) {
-       cur = ctxt->varsTab[i];
-       while (cur != NULL) {
-           if (xmlStrEqual(cur->name, name)) {
-               if (nameURI == NULL) {
-                   if (cur->nameURI == NULL) {
-                       return(cur);
-                   }
-               } else {
-                   if ((cur->nameURI != NULL) &&
-                       (xmlStrEqual(cur->nameURI, nameURI))) {
-                       return(cur);
-                   }
-               }
+       if ((precomp != NULL) && (precomp->comp != NULL)) {
+           comp = precomp->comp;
+       } else {
+           comp = xmlXPathCompile(elem->select);
+       }
+       if (comp == NULL)
+           return(NULL);
+       oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
+       oldContextSize = ctxt->xpathCtxt->contextSize;
+       ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
+       oldDoc = ctxt->document;
+       oldNode = ctxt->node;
+       oldInst = ctxt->inst;
+       oldNsNr = ctxt->xpathCtxt->nsNr;
+       oldNamespaces = ctxt->xpathCtxt->namespaces;
+       if (precomp != NULL) {
+           ctxt->inst = precomp->inst;
+           ctxt->xpathCtxt->namespaces = precomp->nsList;
+           ctxt->xpathCtxt->nsNr = precomp->nsNr;
+       } else {
+           ctxt->inst = NULL;
+           ctxt->xpathCtxt->namespaces = NULL;
+           ctxt->xpathCtxt->nsNr = 0;
+       }
+       result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
+       ctxt->xpathCtxt->contextSize = oldContextSize;
+       ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
+       ctxt->xpathCtxt->nsNr = oldNsNr;
+       ctxt->xpathCtxt->namespaces = oldNamespaces;
+       ctxt->inst = oldInst;
+       ctxt->node = oldNode;
+       ctxt->document = oldDoc;
+       if ((precomp == NULL) || (precomp->comp == NULL))
+           xmlXPathFreeCompExpr(comp);
+       if (result == NULL) {
+           xsltTransformError(ctxt, NULL, precomp->inst,
+               "Evaluating variable %s failed\n", elem->name);
+           ctxt->state = XSLT_STATE_STOPPED;
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+       } else {
+           if ((xsltGenericDebugContext == stdout) ||
+               (xsltGenericDebugContext == stderr))
+               xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+                                       result, 0);
+#endif
+#endif
+       }
+    } else {
+       if (elem->tree == NULL) {
+           result = xmlXPathNewCString("");
+       } else {
+           /*
+            * This is a result tree fragment.
+            */
+           xmlDocPtr container;
+           xmlNodePtr oldInsert;
+           xmlDocPtr  oldoutput;
 
+           container = xsltCreateRVT(ctxt);
+           if (container == NULL)
+               return(NULL);
+           /*
+            * Tag the subtree for removal once consumed
+            */
+           xsltRegisterTmpRVT(ctxt, container);
+           oldoutput = ctxt->output;
+           ctxt->output = container;
+           oldInsert = ctxt->insert;
+           ctxt->insert = (xmlNodePtr) container;
+           xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
+           ctxt->insert = oldInsert;
+           ctxt->output = oldoutput;
+
+           result = xmlXPathNewValueTree((xmlNodePtr) container);
+           if (result == NULL) {
+               result = xmlXPathNewCString("");
+           } else {
+               result->boolval = 0; /* Freeing is not handled there anymore */
            }
-           cur = cur->next;
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+           if ((xsltGenericDebugContext == stdout) ||
+               (xsltGenericDebugContext == stderr))
+               xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+                                       result, 0);
+#endif
+#endif
        }
     }
-    return(ret);
+    return(result);
 }
 
-/************************************************************************
- *                                                                     *
- *                     Module interfaces                               *
- *                                                                     *
- ************************************************************************/
-
 /**
- * xsltEvalVariable:
- * @ctxt:  the XSLT transformation context
+ * xsltEvalGlobalVariable:
  * @elem:  the variable or parameter.
+ * @ctxt:  the XSLT transformation context
  *
- * Evaluate a variable value.
+ * Evaluate a global variable value.
  *
- * Returns 0 in case of success, -1 in case of error
+ * Returns the XPath Object value or NULL in case of error
  */
-int
-xsltEvalVariables(xsltTransformContextPtr ctxt, xsltStackElemPtr elem) {
-    xmlXPathParserContextPtr xpathParserCtxt;
+static xmlXPathObjectPtr
+xsltEvalGlobalVariable(xsltStackElemPtr elem, xsltTransformContextPtr ctxt) {
+    xmlXPathObjectPtr result = NULL;
+    xsltStylePreCompPtr precomp;
+    int oldProximityPosition, oldContextSize;
+    xmlNodePtr oldInst;
+    int oldNsNr;
+    xmlNsPtr *oldNamespaces;
+    xmlChar *name;
 
     if ((ctxt == NULL) || (elem == NULL))
-       return(-1);
+       return(NULL);
+    if (elem->computed)
+       return(elem->value);
 
-#ifdef DEBUG_VARIABLE
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
-       "Evaluating variable %s\n", elem->name);
+       "Evaluating global variable %s\n", elem->name);
 #endif
+
+#ifdef WITH_DEBUGGER
+    if ((xslDebugStatus != XSLT_DEBUG_NONE) &&
+        elem->comp && elem->comp->inst)
+        xslHandleDebugger(elem->comp->inst, NULL, NULL, ctxt);
+#endif
+
+    name = elem->name;
+    elem->name = BAD_CAST "  being computed ... ";
+
+    precomp = elem->comp;
     if (elem->select != NULL) {
-       xmlXPathObjectPtr result, tmp;
+       xmlXPathCompExprPtr comp = NULL;
 
-       xpathParserCtxt = xmlXPathNewParserContext(elem->select,
-                                                  ctxt->xpathCtxt);
-       if (xpathParserCtxt == NULL)
-           return(-1);
+       if ((precomp != NULL) && (precomp->comp != NULL)) {
+           comp = precomp->comp;
+       } else {
+           comp = xmlXPathCompile(elem->select);
+       }
+       if (comp == NULL) {
+           elem->name = name;
+           return(NULL);
+       }
+       oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
+       oldContextSize = ctxt->xpathCtxt->contextSize;
+       oldInst = ctxt->inst;
+       oldNsNr = ctxt->xpathCtxt->nsNr;
+       oldNamespaces = ctxt->xpathCtxt->namespaces;
+       if (precomp != NULL) {
+           ctxt->inst = precomp->inst;
+           ctxt->xpathCtxt->namespaces = precomp->nsList;
+           ctxt->xpathCtxt->nsNr = precomp->nsNr;
+       } else {
+           ctxt->inst = NULL;
+           ctxt->xpathCtxt->namespaces = NULL;
+           ctxt->xpathCtxt->nsNr = 0;
+       }
        ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
-       xmlXPathEvalExpr(xpathParserCtxt);
-       result = valuePop(xpathParserCtxt);
-       do {
-           tmp = valuePop(xpathParserCtxt);
-           if (tmp != NULL) {
-               xmlXPathFreeObject(tmp);
-           }
-       } while (tmp != NULL);
-
+       result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
+       ctxt->xpathCtxt->contextSize = oldContextSize;
+       ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
+       ctxt->inst = oldInst;
+       ctxt->xpathCtxt->nsNr = oldNsNr;
+       ctxt->xpathCtxt->namespaces = oldNamespaces;
+       if ((precomp == NULL) || (precomp->comp == NULL))
+           xmlXPathFreeCompExpr(comp);
        if (result == NULL) {
-           xsltGenericError(xsltGenericErrorContext,
-               "Evaluating global variable %s failed\n");
-       }
-       if (xpathParserCtxt != NULL)
-           xmlXPathFreeParserContext(xpathParserCtxt);
-       if (result != NULL) {
-#ifdef DEBUG_VARIABLE
+           xsltTransformError(ctxt, NULL, precomp->inst,
+               "Evaluating global variable %s failed\n", elem->name);
+           ctxt->state = XSLT_STATE_STOPPED;
+#ifdef WITH_XSLT_DEBUG_VARIABLE
 #ifdef LIBXML_DEBUG_ENABLED
+       } else {
            if ((xsltGenericDebugContext == stdout) ||
                (xsltGenericDebugContext == stderr))
                xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
                                        result, 0);
 #endif
 #endif
-           if (elem->value != NULL)
-               xmlXPathFreeObject(elem->value);
-           elem->value = result;
        }
-       elem->computed = 1;
     } else {
        if (elem->tree == NULL) {
-           elem->value = xmlXPathNewCString("");
+           result = xmlXPathNewCString("");
        } else {
            /*
             * This is a result tree fragment.
             */
-           xmlNodePtr container;
-           xmlNodePtr oldInsert, oldNode;
+           xmlDocPtr container;
+           xmlNodePtr oldInsert;
+           xmlDocPtr  oldoutput;
 
-           container = xmlNewDocNode(ctxt->output, NULL,
-                                     (const xmlChar *) "fake", NULL);
+           container = xsltCreateRVT(ctxt);
            if (container == NULL)
-               return(-1);
-
+               return(NULL);
+           /*
+            * Tag the subtree for removal once consumed
+            */
+           xsltRegisterTmpRVT(ctxt, container);
+           /*
+            * Save a pointer to the global variable for later cleanup
+            */
+           container->_private = elem;
+           oldoutput = ctxt->output;
+           ctxt->output = container;
            oldInsert = ctxt->insert;
-           oldNode = ctxt->node;
-           ctxt->insert = container;
-           xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, 0);
+           ctxt->insert = (xmlNodePtr) container;
+           xsltApplyOneTemplate(ctxt, ctxt->node, elem->tree, NULL, NULL);
            ctxt->insert = oldInsert;
-           ctxt->node = oldNode;
+           ctxt->output = oldoutput;
 
-           if (elem->value != NULL)
-               xmlXPathFreeObject(elem->value);
-           elem->value = xmlXPathNewValueTree(container);
-           if (elem->value == NULL) {
-               elem->value = xmlXPathNewCString("");
+           result = xmlXPathNewValueTree((xmlNodePtr) container);
+           if (result == NULL) {
+               result = xmlXPathNewCString("");
+           } else {
+               result->boolval = 0; /* Freeing is not handled there anymore */
            }
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
 #ifdef LIBXML_DEBUG_ENABLED
            if ((xsltGenericDebugContext == stdout) ||
                (xsltGenericDebugContext == stderr))
                xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
-                                       elem->value, 0);
+                                       result, 0);
 #endif
 #endif
        }
+    }
+    if (result != NULL) {
+       elem->value = result;
        elem->computed = 1;
     }
-    return(0);
+    elem->name = name;
+    return(result);
 }
+
 /**
  * xsltEvalGlobalVariables:
  * @ctxt:  the XSLT transformation context
@@ -345,25 +647,76 @@ xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
     xsltStackElemPtr elem;
     xsltStylesheetPtr style;
 
-    if (ctxt == NULL)
+    if ((ctxt == NULL) || (ctxt->document == NULL))
        return(-1);
  
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
-       "Evaluating global variables\n");
+       "Registering global variables\n");
 #endif
+
     ctxt->node = (xmlNodePtr) ctxt->document->doc;
+    ctxt->xpathCtxt->contextSize = 1;
+    ctxt->xpathCtxt->proximityPosition = 1;
+
+    /*
+     * Walk the list from the stylesheets and populate the hash table
+     */
     style = ctxt->style;
     while (style != NULL) {
        elem = style->variables;
        
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+       if ((style->doc != NULL) && (style->doc->URL != NULL)) {
+           xsltGenericDebug(xsltGenericDebugContext,
+                            "Registering global variables from %s\n",
+                            style->doc->URL);
+       }
+#endif
+
        while (elem != NULL) {
-           xsltEvalVariables(ctxt, elem);
+           xsltStackElemPtr def;
+
+           /*
+            * Global variables are stored in the variables pool.
+            */
+           def = (xsltStackElemPtr) 
+                   xmlHashLookup2(ctxt->globalVars,
+                                elem->name, elem->nameURI);
+           if (def == NULL) {
+
+               def = xsltCopyStackElem(elem);
+               xmlHashAddEntry2(ctxt->globalVars,
+                                elem->name, elem->nameURI, def);
+           } else if ((elem->comp != NULL) &&
+                      (elem->comp->type == XSLT_FUNC_VARIABLE)) {
+               /*
+                * Redefinition of variables from a different stylesheet
+                * should not generate a message.
+                */
+               if ((elem->comp->inst != NULL) &&
+                   (def->comp != NULL) && (def->comp->inst != NULL) &&
+                   (elem->comp->inst->doc == def->comp->inst->doc)) {
+                   xsltTransformError(ctxt, style, elem->comp->inst,
+                       "Global variable %s already defined\n", elem->name);
+                   if (style != NULL) style->errors++;
+               }
+           }
            elem = elem->next;
        }
 
        style = xsltNextImport(style);
     }
+
+    /*
+     * This part does the actual evaluation
+     */
+    ctxt->node = (xmlNodePtr) ctxt->document->doc;
+    ctxt->xpathCtxt->contextSize = 1;
+    ctxt->xpathCtxt->proximityPosition = 1;
+    xmlHashScan(ctxt->globalVars,
+               (xmlHashScanner) xsltEvalGlobalVariable, ctxt);
+
     return(0);
 }
 
@@ -374,104 +727,447 @@ xsltEvalGlobalVariables(xsltTransformContextPtr ctxt) {
  * @ns_uri:  the variable namespace URI
  * @select:  the expression which need to be evaluated to generate a value
  * @tree:  the subtree if select is NULL
- * @param:  this is a parameter actually
+ * @comp:  the precompiled value
+ * @value:  the string value if available
+ *
+ * Register a new variable value. If @value is NULL it unregisters
+ * the variable
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+static int
+xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
+                    const xmlChar *ns_uri, const xmlChar *select,
+                    xmlNodePtr tree, xsltStylePreCompPtr comp,
+                    const xmlChar *value) {
+    xsltStackElemPtr elem, tmp;
+    if (style == NULL)
+       return(-1);
+    if (name == NULL)
+       return(-1);
+    if (comp == NULL)
+       return(-1);
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+    if (comp->type == XSLT_FUNC_PARAM)
+       xsltGenericDebug(xsltGenericDebugContext,
+                        "Defining global param %s\n", name);
+    else
+       xsltGenericDebug(xsltGenericDebugContext,
+                        "Defining global variable %s\n", name);
+#endif
+
+    elem = xsltNewStackElem();
+    if (elem == NULL)
+       return(-1);
+    elem->comp = comp;
+    elem->name = xmlStrdup(name);
+    elem->select = xmlStrdup(select);
+    if (ns_uri)
+       elem->nameURI = xmlStrdup(ns_uri);
+    elem->tree = tree;
+    tmp = style->variables;
+    if (tmp == NULL) {
+       elem->next = NULL;
+       style->variables = elem;
+    } else {
+       while (tmp != NULL) {
+           if ((elem->comp->type == XSLT_FUNC_VARIABLE) &&
+               (tmp->comp->type == XSLT_FUNC_VARIABLE) &&
+               (xmlStrEqual(elem->name, tmp->name)) &&
+               ((elem->nameURI == tmp->nameURI) ||
+                (xmlStrEqual(elem->nameURI, tmp->nameURI)))) {
+               xsltTransformError(NULL, style, comp->inst,
+               "redefinition of global variable %s\n", elem->name);
+               if (style != NULL) style->errors++;
+           }
+           if (tmp->next == NULL)
+               break;
+           tmp = tmp->next;
+       }
+       elem->next = NULL;
+       tmp->next = elem;
+    }
+    if (value != NULL) {
+       elem->computed = 1;
+       elem->value = xmlXPathNewString(value);
+    }
+    return(0);
+}
+
+/**
+ * xsltProcessUserParamInternal
+ *
+ * @ctxt:  the XSLT transformation context
+ * @name:  a null terminated parameter name
+ * @value: a null terminated value (may be an XPath expression)
+ * @eval:  0 to treat the value literally, else evaluate as XPath expression
+ *
+ * If @eval is 0 then @value is treated literally and is stored in the global
+ * parameter/variable table without any change.
+ *
+ * Uf @eval is 1 then @value is treated as an XPath expression and is
+ * evaluated.  In this case, if you want to pass a string which will be
+ * interpreted literally then it must be enclosed in single or double quotes.
+ * If the string contains single quotes (double quotes) then it cannot be
+ * enclosed single quotes (double quotes).  If the string which you want to
+ * be treated literally contains both single and double quotes (e.g. Meet
+ * at Joe's for "Twelfth Night" at 7 o'clock) then there is no suitable
+ * quoting character.  You cannot use &apos; or &quot; inside the string
+ * because the replacement of character entities with their equivalents is
+ * done at a different stage of processing.  The solution is to call
+ * xsltQuoteUserParams or xsltQuoteOneUserParam.
+ *
+ * This needs to be done on parsed stylesheets before starting to apply
+ * transformations.  Normally this will be called (directly or indirectly)
+ * only from xsltEvalUserParams, xsltEvalOneUserParam, xsltQuoteUserParams,
+ * or xsltQuoteOneUserParam.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+
+static
+int
+xsltProcessUserParamInternal(xsltTransformContextPtr ctxt,
+                            const xmlChar * name,
+                            const xmlChar * value,
+                            int eval) {
+
+    xsltStylesheetPtr style;
+    xmlChar *ncname;
+    xmlChar *prefix;
+    const xmlChar *href;
+    xmlXPathCompExprPtr comp;
+    xmlXPathObjectPtr result;
+    int oldProximityPosition;
+    int oldContextSize;
+    int oldNsNr;
+    xmlNsPtr *oldNamespaces;
+    xsltStackElemPtr elem;
+    int res;
+    void *res_ptr;
+
+    if (ctxt == NULL)
+       return(-1);
+    if (name == NULL)
+       return(0);
+    if (value == NULL)
+       return(0);
+
+    style = ctxt->style;
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+    xsltGenericDebug(xsltGenericDebugContext,
+           "Evaluating user parameter %s=%s\n", name, value);
+#endif
+
+    /*
+     * Name lookup
+     */
+
+    ncname = xmlSplitQName2(name, &prefix);
+    href = NULL;
+    if (ncname != NULL) {
+       if (prefix != NULL) {
+           xmlNsPtr ns;
+
+           ns = xmlSearchNs(style->doc, xmlDocGetRootElement(style->doc),
+                            prefix);
+           if (ns == NULL) {
+               xsltTransformError(ctxt, style, NULL,
+               "user param : no namespace bound to prefix %s\n", prefix);
+               href = NULL;
+           } else {
+               href = ns->href;
+           }
+           xmlFree(prefix);
+           prefix = NULL;
+       } else {
+           href = NULL;
+       }
+       xmlFree(ncname);
+       ncname = NULL;
+    } else {
+       href = NULL;
+       ncname = xmlStrdup(name);
+    }
+
+    if (ncname == NULL)
+       return (-1);
+
+    res_ptr = xmlHashLookup2(ctxt->globalVars, ncname, href);
+    if (res_ptr != 0) {
+       xsltTransformError(ctxt, style, NULL,
+           "Global parameter %s already defined\n", ncname);
+    }
+
+    /*
+     * do not overwrite variables with parameters from the command line
+     */
+    while (style != NULL) {
+        elem = ctxt->style->variables;
+       while (elem != NULL) {
+           if ((elem->comp != NULL) &&
+               (elem->comp->type == XSLT_FUNC_VARIABLE) &&
+               (xmlStrEqual(elem->name, ncname)) &&
+               (xmlStrEqual(elem->nameURI, href))) {
+               xmlFree(ncname);
+               return(0);
+           }
+            elem = elem->next;
+       }
+        style = xsltNextImport(style);
+    }
+    style = ctxt->style;
+    elem = NULL;
+
+    /*
+     * Do the evaluation if @eval is non-zero.
+     */
+
+    result = NULL;
+    if (eval != 0) {
+        comp = xmlXPathCompile(value);
+       if (comp != NULL) {
+           oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
+           oldContextSize = ctxt->xpathCtxt->contextSize;
+           ctxt->xpathCtxt->node = (xmlNodePtr) ctxt->node;
+
+           /* 
+            * There is really no in scope namespace for parameters on the
+            * command line.
+            */
+
+           oldNsNr = ctxt->xpathCtxt->nsNr;
+           oldNamespaces = ctxt->xpathCtxt->namespaces;
+           ctxt->xpathCtxt->namespaces = NULL;
+           ctxt->xpathCtxt->nsNr = 0;
+           result = xmlXPathCompiledEval(comp, ctxt->xpathCtxt);
+           ctxt->xpathCtxt->contextSize = oldContextSize;
+           ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
+           ctxt->xpathCtxt->nsNr = oldNsNr;
+           ctxt->xpathCtxt->namespaces = oldNamespaces;
+           xmlXPathFreeCompExpr(comp);
+       }
+       if (result == NULL) {
+           xsltTransformError(ctxt, style, NULL,
+               "Evaluating user parameter %s failed\n", name);
+           ctxt->state = XSLT_STATE_STOPPED;
+           xmlFree(ncname);
+           return(-1);
+       }
+    }
+
+    /* 
+     * If @eval is 0 then @value is to be taken literally and result is NULL
+     * 
+     * If @eval is not 0, then @value is an XPath expression and has been
+     * successfully evaluated and result contains the resulting value and
+     * is not NULL.
+     *
+     * Now create an xsltStackElemPtr for insertion into the context's
+     * global variable/parameter hash table.
+     */
+
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+#ifdef LIBXML_DEBUG_ENABLED
+    if ((xsltGenericDebugContext == stdout) ||
+        (xsltGenericDebugContext == stderr))
+           xmlXPathDebugDumpObject((FILE *)xsltGenericDebugContext,
+                                   result, 0);
+#endif
+#endif
+
+    elem = xsltNewStackElem();
+    if (elem != NULL) {
+       elem->name = xmlStrdup(ncname);
+       if (value != NULL)
+           elem->select = xmlStrdup(value);
+       else
+           elem->select = NULL;
+       if (href)
+           elem->nameURI = xmlStrdup(href);
+       elem->tree = NULL;
+       elem->computed = 1;
+       if (eval == 0) {
+           elem->value = xmlXPathNewString(value);
+       } 
+       else {
+           elem->value = result;
+       }
+    }
+
+    /*
+     * Global parameters are stored in the XPath context variables pool.
+     */
+
+    res = xmlHashAddEntry2(ctxt->globalVars, ncname, href, elem);
+    if (res != 0) {
+       xsltFreeStackElem(elem);
+       xsltTransformError(ctxt, style, NULL,
+           "Global parameter %s already defined\n", ncname);
+    }
+    xmlFree(ncname);
+    return(0);
+}
+
+/**
+ * xsltEvalUserParams:
+ *
+ * @ctxt:  the XSLT transformation context
+ * @params:  a NULL terminated array of parameters name/value tuples
+ *
+ * Evaluate the global variables of a stylesheet. This needs to be
+ * done on parsed stylesheets before starting to apply transformations.
+ * Each of the parameters is evaluated as an XPath expression and stored
+ * in the global variables/parameter hash table.  If you want your
+ * parameter used literally, use xsltQuoteUserParams.
+ *
+ * Returns 0 in case of success, -1 in case of error
+ */
+int
+xsltEvalUserParams(xsltTransformContextPtr ctxt, const char **params) {
+    int indx = 0;
+    const xmlChar *name;
+    const xmlChar *value;
+
+    if (params == NULL)
+       return(0);
+    while (params[indx] != NULL) {
+       name = (const xmlChar *) params[indx++];
+       value = (const xmlChar *) params[indx++];
+       if (xsltEvalOneUserParam(ctxt, name, value) != 0) 
+           return(-1);
+    }
+    return 0;
+}
+
+/**
+ * xsltQuoteUserParams:
+ *
+ * @ctxt:  the XSLT transformation context
+ * @params:  a NULL terminated arry of parameters names/values tuples
+ *
+ * Similar to xsltEvalUserParams, but the values are treated literally and
+ * are * *not* evaluated as XPath expressions. This should be done on parsed
+ * stylesheets before starting to apply transformations.
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+int
+xsltQuoteUserParams(xsltTransformContextPtr ctxt, const char **params) {
+    int indx = 0;
+    const xmlChar *name;
+    const xmlChar *value;
+
+    if (params == NULL)
+       return(0);
+    while (params[indx] != NULL) {
+       name = (const xmlChar *) params[indx++];
+       value = (const xmlChar *) params[indx++];
+       if (xsltQuoteOneUserParam(ctxt, name, value) != 0) 
+           return(-1);
+    }
+    return 0;
+}
+
+/**
+ * xsltEvalOneUserParam:
+ * @ctxt:  the XSLT transformation context
+ * @name:  a null terminated string giving the name of the parameter
+ * @value:  a null terminated string giving the XPath expression to be evaluated
+ *
+ * This is normally called from xsltEvalUserParams to process a single
+ * parameter from a list of parameters.  The @value is evaluated as an
+ * XPath expression and the result is stored in the context's global
+ * variable/parameter hash table.
+ *
+ * To have a parameter treated literally (not as an XPath expression)
+ * use xsltQuoteUserParams (or xsltQuoteOneUserParam).  For more
+ * details see description of xsltProcessOneUserParamInternal.
+ *
+ * Returns 0 in case of success, -1 in case of error.
+ */
+
+int
+xsltEvalOneUserParam(xsltTransformContextPtr ctxt,
+                    const xmlChar * name,
+                    const xmlChar * value) {
+    return xsltProcessUserParamInternal(ctxt, name, value,
+                                       1 /* xpath eval ? */);
+}
+
+/**
+ * xsltQuoteOneUserParam:
+ * @ctxt:  the XSLT transformation context
+ * @name:  a null terminated string giving the name of the parameter
+ * @value:  a null terminated string giving the parameter value
  *
- * Register a new variable value. If @value is NULL it unregisters
- * the variable
+ * This is normally called from xsltQuoteUserParams to process a single
+ * parameter from a list of parameters.  The @value is stored in the
+ * context's global variable/parameter hash table.
  *
- * Returns 0 in case of success, -1 in case of error
+ * Returns 0 in case of success, -1 in case of error.
  */
-int
-xsltRegisterGlobalVariable(xsltStylesheetPtr style, const xmlChar *name,
-                    const xmlChar *ns_uri, const xmlChar *select,
-                    xmlNodePtr tree, int param) {
-    xsltStackElemPtr elem;
-    if (style == NULL)
-       return(-1);
-    if (name == NULL)
-       return(-1);
 
-#ifdef DEBUG_VARIABLE
-    if (param)
-       xsltGenericDebug(xsltGenericDebugContext,
-                        "Defineing global param %s\n", name);
-    else
-       xsltGenericDebug(xsltGenericDebugContext,
-                        "Defineing global variable %s\n", name);
-#endif
-    elem = xsltNewStackElem();
-    if (elem == NULL)
-       return(-1);
-    if (param)
-       elem->type = XSLT_ELEM_PARAM;
-    else
-       elem->type = XSLT_ELEM_VARIABLE;
-    elem->name = xmlStrdup(name);
-    elem->select = xmlStrdup(select);
-    if (ns_uri)
-       elem->nameURI = xmlStrdup(ns_uri);
-    elem->tree = tree;
-    elem->next = style->variables;
-    style->variables = elem;
-    return(0);
+int
+xsltQuoteOneUserParam(xsltTransformContextPtr ctxt,
+                        const xmlChar * name,
+                        const xmlChar * value) {
+    return xsltProcessUserParamInternal(ctxt, name, value,
+                                       0 /* xpath eval ? */);
 }
 
 /**
  * xsltBuildVariable:
  * @ctxt:  the XSLT transformation context
- * @name:  the variable name
- * @ns_uri:  the variable namespace URI
- * @select:  the expression which need to be evaluated to generate a value
+ * @comp:  the precompiled form
  * @tree:  the tree if select is NULL
- * @param:  this is a parameter actually
  *
  * Computes a new variable value.
  *
  * Returns the xsltStackElemPtr or NULL in case of error
  */
-xsltStackElemPtr
-xsltBuildVariable(xsltTransformContextPtr ctxt, const xmlChar *name,
-                    const xmlChar *ns_uri, const xmlChar *select,
-                    xmlNodePtr tree, int param) {
+static xsltStackElemPtr
+xsltBuildVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
+                 xmlNodePtr tree) {
     xsltStackElemPtr elem;
-    if (ctxt == NULL)
-       return(NULL);
-    if (name == NULL)
-       return(NULL);
 
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
-                    "Building variable %s", name);
-    if (select != NULL)
+                    "Building variable %s", comp->name);
+    if (comp->select != NULL)
        xsltGenericDebug(xsltGenericDebugContext,
-                        " select %s",  select);
+                        " select %s", comp->select);
     xsltGenericDebug(xsltGenericDebugContext, "\n");
 #endif
+
     elem = xsltNewStackElem();
     if (elem == NULL)
        return(NULL);
-    if (param)
-       elem->type = XSLT_ELEM_PARAM;
-    else
-       elem->type = XSLT_ELEM_VARIABLE;
-    elem->name = xmlStrdup(name);
-    if (select != NULL)
-       elem->select = xmlStrdup(select);
+    elem->comp = comp;
+    elem->name = xmlStrdup(comp->name);
+    if (comp->select != NULL)
+       elem->select = xmlStrdup(comp->select);
     else
        elem->select = NULL;
-    if (ns_uri)
-       elem->nameURI = xmlStrdup(ns_uri);
+    if (comp->ns)
+       elem->nameURI = xmlStrdup(comp->ns);
     elem->tree = tree;
-    xsltEvalVariables(ctxt, elem);
+    if (elem->computed == 0) {
+       elem->value = xsltEvalVariable(ctxt, elem, comp);
+       if (elem->value != NULL)
+           elem->computed = 1;
+    }
     return(elem);
 }
 
 /**
  * xsltRegisterVariable:
  * @ctxt:  the XSLT transformation context
- * @name:  the variable name
- * @ns_uri:  the variable namespace URI
- * @select:  the expression which need to be evaluated to generate a value
+ * @comp:  pointer to precompiled data
  * @tree:  the tree if select is NULL
  * @param:  this is a parameter actually
  *
@@ -479,29 +1175,32 @@ xsltBuildVariable(xsltTransformContextPtr ctxt, const xmlChar *name,
  *
  * Returns 0 in case of success, -1 in case of error
  */
-int
-xsltRegisterVariable(xsltTransformContextPtr ctxt, const xmlChar *name,
-                    const xmlChar *ns_uri, const xmlChar *select,
+static int
+xsltRegisterVariable(xsltTransformContextPtr ctxt, xsltStylePreCompPtr comp,
                     xmlNodePtr tree, int param) {
     xsltStackElemPtr elem;
-    if (ctxt == NULL)
-       return(-1);
-    if (name == NULL)
-       return(-1);
-
-    if (xsltCheckStackElem(ctxt, name, ns_uri) != 0) {
-       if (!param) {
-           xsltGenericError(xsltGenericErrorContext,
-           "xsl:variable : redefining %s\n", name);
+    int present;
+
+    present = xsltCheckStackElem(ctxt, comp->name, comp->ns);
+    if (param == 0) {
+       if ((present != 0) && (present != 3)) {
+           xsltTransformError(ctxt, NULL, comp->inst,
+               "xsl:variable : redefining %s\n", comp->name);
+           return(0);
        }
-#ifdef DEBUG_VARIABLE
-       else
-           xsltGenericDebug(xsltGenericDebugContext,
-                    "param %s defined by caller", name);
+    } else if (present != 0) {
+       if ((present == 1) || (present == 2)) {
+           xsltTransformError(ctxt, NULL, comp->inst,
+               "xsl:param : redefining %s\n", comp->name);
+           return(0);
+       }
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+       xsltGenericDebug(xsltGenericDebugContext,
+                "param %s defined by caller\n", comp->name);
 #endif
        return(0);
     }
-    elem = xsltBuildVariable(ctxt, name, ns_uri, select, tree, param);
+    elem = xsltBuildVariable(ctxt, comp, tree);
     xsltAddStackElem(ctxt, elem);
     return(0);
 }
@@ -517,51 +1216,36 @@ xsltRegisterVariable(xsltTransformContextPtr ctxt, const xmlChar *name,
  *
  * Returns the value or NULL if not found
  */
-xmlXPathObjectPtr
+static xmlXPathObjectPtr
 xsltGlobalVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
                         const xmlChar *ns_uri) {
-    xsltStylesheetPtr style;
-    xsltStackElemPtr elem = NULL;
-
-    style = ctxt->style;
-    while (style != NULL) {
-       elem = style->variables;
-       
-       while (elem != NULL) {
-           if (xmlStrEqual(elem->name, name)) {
-               if (ns_uri == NULL) {
-                   if (elem->nameURI == NULL)
-                       break;
-               } else {
-                   if ((elem->nameURI != NULL) &&
-                       (xmlStrEqual(elem->nameURI, ns_uri)))
-                       break;
-               }
-
-           }
-           elem = elem->next;
-       }
-       if (elem != NULL) break;
+    xsltStackElemPtr elem;
+    xmlXPathObjectPtr ret = NULL;
 
-       style = xsltNextImport(style);
-    }
-    if (elem == NULL)
+    /*
+     * Lookup the global variables in XPath global variable hash table
+     */
+    if ((ctxt->xpathCtxt == NULL) || (ctxt->globalVars == NULL))
        return(NULL);
-
-    if (!elem->computed) {
-#ifdef DEBUG_VARIABLE
+    elem = (xsltStackElemPtr)
+           xmlHashLookup2(ctxt->globalVars, name, ns_uri);
+    if (elem == NULL) {
+#ifdef WITH_XSLT_DEBUG_VARIABLE
        xsltGenericDebug(xsltGenericDebugContext,
-                        "uncomputed global variable %s\n", name);
+                        "global variable not found %s\n", name);
 #endif
-        xsltEvalVariables(ctxt, elem);
+       return(NULL);
     }
-    if (elem->value != NULL)
-       return(xmlXPathObjectCopy(elem->value));
-#ifdef DEBUG_VARIABLE
-    xsltGenericDebug(xsltGenericDebugContext,
-                    "global variable not found %s\n", name);
-#endif
-    return(NULL);
+    if (elem->computed == 0) {
+       if (xmlStrEqual(elem->name, BAD_CAST "  being computed ... ")) {
+           xsltTransformError(ctxt, NULL, elem->comp->inst,
+               "Recursive definition of %s\n", name);
+           return(NULL);
+       }
+       ret = xsltEvalGlobalVariable(elem, ctxt);
+    } else
+       ret = elem->value;
+    return(xmlXPathObjectCopy(ret));
 }
 
 /**
@@ -587,16 +1271,17 @@ xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
     if (elem == NULL) {
        return(xsltGlobalVariableLookup(ctxt, name, ns_uri));
     }
-    if (!elem->computed) {
-#ifdef DEBUG_VARIABLE
+    if (elem->computed == 0) {
+#ifdef WITH_XSLT_DEBUG_VARIABLE
        xsltGenericDebug(xsltGenericDebugContext,
                         "uncomputed variable %s\n", name);
 #endif
-        xsltEvalVariables(ctxt, elem);
+        elem->value = xsltEvalVariable(ctxt, elem, NULL);
+       elem->computed = 1;
     }
     if (elem->value != NULL)
        return(xmlXPathObjectCopy(elem->value));
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
                     "variable not found %s\n", name);
 #endif
@@ -611,142 +1296,48 @@ xsltVariableLookup(xsltTransformContextPtr ctxt, const xmlChar *name,
  * parse an XSLT transformation param declaration, compute
  * its value but doesn't record it.
  *
- * It returns the new xsltStackElemPtr or NULL
+ * Returns the new xsltStackElemPtr or NULL
  */
 
 xsltStackElemPtr
 xsltParseStylesheetCallerParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
-    xmlChar *name, *ncname, *prefix;
-    xmlChar *select;
     xmlNodePtr tree = NULL;
     xsltStackElemPtr elem = NULL;
+    xsltStylePreCompPtr comp;
 
     if ((cur == NULL) || (ctxt == NULL))
        return(NULL);
-
-    name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
-    if (name == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
-           "xsl:param : missing name attribute\n");
+    comp = (xsltStylePreCompPtr) cur->_private;
+    if (comp == NULL) {
+       xsltTransformError(ctxt, NULL, cur,
+           "xsl:param : compilation error\n");
        return(NULL);
     }
 
-#ifdef DEBUG_VARIABLE
-    xsltGenericDebug(xsltGenericDebugContext,
-       "Parsing param %s\n", name);
-#endif
-
-    select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
-    if (select == NULL) {
-       tree = cur->children;
-    } else {
-#ifdef DEBUG_VARIABLE
-       xsltGenericDebug(xsltGenericDebugContext,
-           "        select %s\n", select);
-#endif
-       if (cur->children != NULL)
-           xsltGenericError(xsltGenericErrorContext,
-           "xsl:param : content shuld be empty since select is present \n");
-    }
-
-    ncname = xmlSplitQName2(name, &prefix);
-
-    if (ncname != NULL) {
-       if (prefix != NULL) {
-           xmlNsPtr ns;
-
-           ns = xmlSearchNs(cur->doc, cur, prefix);
-           if (ns == NULL) {
-               xsltGenericError(xsltGenericErrorContext,
-                   "xsl:param : no namespace bound to prefix %s\n", prefix);
-           } else {
-               elem = xsltBuildVariable(ctxt, ncname, ns->href, select,
-                                        tree, 1);
-           }
-           xmlFree(prefix);
-       } else {
-           elem = xsltBuildVariable(ctxt, ncname, NULL, select, tree, 1);
-       }
-       xmlFree(ncname);
-    } else {
-       elem = xsltBuildVariable(ctxt, name, NULL, select, tree, 1);
-    }
-
-    xmlFree(name);
-    if (select != NULL)
-       xmlFree(select);
-    return(elem);
-}
-
-/**
- * xsltParseStylesheetParam:
- * @ctxt:  the XSLT transformation context
- * @cur:  the "param" element
- *
- * parse an XSLT transformation param declaration and record
- * its value.
- */
-
-void
-xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
-    xmlChar *name, *ncname, *prefix;
-    xmlChar *select;
-    xmlNodePtr tree = NULL;
-
-    if ((cur == NULL) || (ctxt == NULL))
-       return;
-
-    name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
-    if (name == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
+    if (comp->name == NULL) {
+       xsltTransformError(ctxt, NULL, cur,
            "xsl:param : missing name attribute\n");
-       return;
+       return(NULL);
     }
 
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
-       "Parsing param %s\n", name);
+           "Handling param %s\n", comp->name);
 #endif
 
-    select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
-    if (select == NULL) {
+    if (comp->select == NULL) {
        tree = cur->children;
     } else {
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
        xsltGenericDebug(xsltGenericDebugContext,
-           "        select %s\n", select);
+           "        select %s\n", comp->select);
 #endif
-       if (cur->children != NULL)
-           xsltGenericError(xsltGenericErrorContext,
-           "xsl:param : content shuld be empty since select is present \n");
-    }
-
-    ncname = xmlSplitQName2(name, &prefix);
-
-    if (ncname != NULL) {
-       if (prefix != NULL) {
-           xmlNsPtr ns;
-
-           ns = xmlSearchNs(cur->doc, cur, prefix);
-           if (ns == NULL) {
-               xsltGenericError(xsltGenericErrorContext,
-                   "xsl:param : no namespace bound to prefix %s\n", prefix);
-           } else {
-               xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 1);
-           }
-           xmlFree(prefix);
-       } else {
-           xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 1);
-       }
-       xmlFree(ncname);
-    } else {
-       xsltRegisterVariable(ctxt, name, NULL, select, tree, 1);
+       tree = cur;
     }
 
-    xmlFree(name);
-    if (select != NULL)
-       xmlFree(select);
+    elem = xsltBuildVariable(ctxt, comp, tree);
 
+    return(elem);
 }
 
 /**
@@ -760,60 +1351,32 @@ xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
 
 void
 xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
-    xmlChar *name, *ncname, *prefix;
-    xmlChar *select;
-    xmlNodePtr tree = NULL;
+    xsltStylePreCompPtr comp;
 
     if ((cur == NULL) || (style == NULL))
        return;
 
-    name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
-    if (name == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
+    xsltStylePreCompute(style, cur);
+    comp = (xsltStylePreCompPtr) cur->_private;
+    if (comp == NULL) {
+       xsltTransformError(NULL, style, cur,
+            "xsl:variable : compilation failed\n");
+       return;
+    }
+
+    if (comp->name == NULL) {
+       xsltTransformError(NULL, style, cur,
            "xsl:variable : missing name attribute\n");
        return;
     }
 
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
-       "Parsing global variable %s\n", name);
+       "Registering global variable %s\n", comp->name);
 #endif
 
-    select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
-    if (select == NULL) {
-       tree = cur->children;
-    } else {
-       if (cur->children != NULL)
-           xsltGenericError(xsltGenericErrorContext,
-           "xsl:variable : content shuld be empty since select is present \n");
-    }
-
-    ncname = xmlSplitQName2(name, &prefix);
-
-    if (ncname != NULL) {
-       if (prefix != NULL) {
-           xmlNsPtr ns;
-
-           ns = xmlSearchNs(cur->doc, cur, prefix);
-           if (ns == NULL) {
-               xsltGenericError(xsltGenericErrorContext,
-                   "xsl:variable : no namespace bound to prefix %s\n", prefix);
-           } else {
-               xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 0);
-           }
-           xmlFree(prefix);
-       } else {
-           xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 0);
-       }
-       xmlFree(ncname);
-    } else {
-       xsltRegisterGlobalVariable(style, name, NULL, select, tree, 0);
-    }
-
-    xmlFree(name);
-    if (select != NULL)
-       xmlFree(select);
-
+    xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
+                              cur->children, comp, NULL);
 }
 
 /**
@@ -827,59 +1390,32 @@ xsltParseGlobalVariable(xsltStylesheetPtr style, xmlNodePtr cur) {
 
 void
 xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
-    xmlChar *name, *ncname, *prefix;
-    xmlChar *select;
-    xmlNodePtr tree = NULL;
+    xsltStylePreCompPtr comp;
 
     if ((cur == NULL) || (style == NULL))
        return;
 
-    name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
-    if (name == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
+    xsltStylePreCompute(style, cur);
+    comp = (xsltStylePreCompPtr) cur->_private;
+    if (comp == NULL) {
+       xsltTransformError(NULL, style, cur,
+            "xsl:param : compilation failed\n");
+       return;
+    }
+
+    if (comp->name == NULL) {
+       xsltTransformError(NULL, style, cur,
            "xsl:param : missing name attribute\n");
        return;
     }
 
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
-       "Parsing global param %s\n", name);
+       "Registering global param %s\n", comp->name);
 #endif
 
-    select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
-    if (select == NULL) {
-       tree = cur->children;
-    } else {
-       if (cur->children != NULL)
-           xsltGenericError(xsltGenericErrorContext,
-           "xsl:param : content shuld be empty since select is present \n");
-    }
-
-    ncname = xmlSplitQName2(name, &prefix);
-
-    if (ncname != NULL) {
-       if (prefix != NULL) {
-           xmlNsPtr ns;
-
-           ns = xmlSearchNs(cur->doc, cur, prefix);
-           if (ns == NULL) {
-               xsltGenericError(xsltGenericErrorContext,
-                   "xsl:param : no namespace bound to prefix %s\n", prefix);
-           } else {
-               xsltRegisterGlobalVariable(style, ncname, ns->href, select, tree, 1);
-           }
-           xmlFree(prefix);
-       } else {
-           xsltRegisterGlobalVariable(style, ncname, NULL, select, tree, 1);
-       }
-       xmlFree(ncname);
-    } else {
-       xsltRegisterGlobalVariable(style, name, NULL, select, tree, 1);
-    }
-
-    xmlFree(name);
-    if (select != NULL)
-       xmlFree(select);
+    xsltRegisterGlobalVariable(style, comp->name, comp->ns, comp->select,
+                              cur->children, comp, NULL);
 }
 
 /**
@@ -893,64 +1429,84 @@ xsltParseGlobalParam(xsltStylesheetPtr style, xmlNodePtr cur) {
 
 void
 xsltParseStylesheetVariable(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
-    xmlChar *name, *ncname, *prefix;
-    xmlChar *select;
-    xmlNodePtr tree = NULL;
+    xsltStylePreCompPtr comp;
 
     if ((cur == NULL) || (ctxt == NULL))
        return;
 
-    name = xmlGetNsProp(cur, (const xmlChar *)"name", XSLT_NAMESPACE);
-    if (name == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
+    comp = (xsltStylePreCompPtr) cur->_private;
+    if (comp == NULL) {
+       xsltTransformError(ctxt, NULL, cur,
+            "xsl:variable : compilation failed\n");
+       return;
+    }
+
+    if (comp->name == NULL) {
+       xsltTransformError(ctxt, NULL, cur,
            "xsl:variable : missing name attribute\n");
        return;
     }
 
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
-       "Parsing variable %s\n", name);
+       "Registering variable %s\n", comp->name);
 #endif
 
-    select = xmlGetNsProp(cur, (const xmlChar *)"select", XSLT_NAMESPACE);
-    if (select == NULL) {
-       tree = cur->children;
-    } else {
-       if (cur->children != NULL)
-           xsltGenericError(xsltGenericErrorContext,
-       "xsl:variable : content should be empty since select is present \n");
-    }
+    xsltRegisterVariable(ctxt, comp, cur->children, 0);
+}
 
-    ncname = xmlSplitQName2(name, &prefix);
+/**
+ * xsltParseStylesheetParam:
+ * @ctxt:  the XSLT transformation context
+ * @cur:  the "param" element
+ *
+ * parse an XSLT transformation param declaration and record
+ * its value.
+ */
 
-    if (ncname != NULL) {
-       if (prefix != NULL) {
-           xmlNsPtr ns;
+void
+xsltParseStylesheetParam(xsltTransformContextPtr ctxt, xmlNodePtr cur) {
+    xsltStylePreCompPtr comp;
 
-           ns = xmlSearchNs(cur->doc, cur, prefix);
-           if (ns == NULL) {
-               xsltGenericError(xsltGenericErrorContext,
-                   "xsl:variable : no namespace bound to prefix %s\n", prefix);
-           } else {
-               xsltRegisterVariable(ctxt, ncname, ns->href, select, tree, 0);
-           }
-           xmlFree(prefix);
-       } else {
-           xsltRegisterVariable(ctxt, ncname, NULL, select, tree, 0);
-       }
-       xmlFree(ncname);
-    } else {
-       xsltRegisterVariable(ctxt, name, NULL, select, tree, 0);
+    if ((cur == NULL) || (ctxt == NULL))
+       return;
+
+    comp = (xsltStylePreCompPtr) cur->_private;
+    if (comp == NULL) {
+       xsltTransformError(ctxt, NULL, cur,
+            "xsl:param : compilation failed\n");
+       return;
+    }
+
+    if (comp->name == NULL) {
+       xsltTransformError(ctxt, NULL, cur,
+           "xsl:param : missing name attribute\n");
+       return;
     }
 
-    xmlFree(name);
-    if (select != NULL)
-       xmlFree(select);
+#ifdef WITH_XSLT_DEBUG_VARIABLE
+    xsltGenericDebug(xsltGenericDebugContext,
+       "Registering param %s\n", comp->name);
+#endif
 
+    xsltRegisterVariable(ctxt, comp, cur->children, 1);
 }
 
 /**
- * xsltVariableLookup:
+ * xsltFreeGlobalVariables:
+ * @ctxt:  the XSLT transformation context
+ *
+ * Free up the data associated to the global variables
+ * its value.
+ */
+
+void
+xsltFreeGlobalVariables(xsltTransformContextPtr ctxt) {
+    xmlHashFree(ctxt->globalVars, (xmlHashDeallocator) xsltFreeStackElem);
+}
+
+/**
+ * xsltXPathVariableLookup:
  * @ctxt:  a void * but the the XSLT transformation context actually
  * @name:  the variable name
  * @ns_uri:  the variable namespace URI
@@ -969,17 +1525,17 @@ xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
     if ((ctxt == NULL) || (name == NULL))
        return(NULL);
 
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     xsltGenericDebug(xsltGenericDebugContext,
            "Lookup variable %s\n", name);
 #endif
     context = (xsltTransformContextPtr) ctxt;
     ret = xsltVariableLookup(context, name, ns_uri);
     if (ret == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(ctxt, NULL, NULL,
            "unregistered variable %s\n", name);
     }
-#ifdef DEBUG_VARIABLE
+#ifdef WITH_XSLT_DEBUG_VARIABLE
     if (ret != NULL)
        xsltGenericDebug(xsltGenericDebugContext,
            "found variable %s\n", name);
@@ -987,3 +1543,4 @@ xsltXPathVariableLookup(void *ctxt, const xmlChar *name,
     return(ret);
 }
 
+