preparing release 1.1.15 a bit more cleanup Daniel
[platform/upstream/libxslt.git] / libxslt / pattern.c
index 61c9b49..ed8e858 100644 (file)
@@ -14,6 +14,7 @@
  * TODO: detect [number] at compilation, optimize accordingly
  */
 
+#define IN_LIBXSLT
 #include "libxslt.h"
 
 #include <string.h>
@@ -59,6 +60,20 @@ typedef enum {
     XSLT_OP_PREDICATE
 } xsltOp;
 
+typedef struct _xsltStepState xsltStepState;
+typedef xsltStepState *xsltStepStatePtr;
+struct _xsltStepState {
+    int step;
+    xmlNodePtr node;
+};
+
+typedef struct _xsltStepStates xsltStepStates;
+typedef xsltStepStates *xsltStepStatesPtr;
+struct _xsltStepStates {
+    int nbstates;
+    int maxstates;
+    xsltStepStatePtr states;
+};
 
 typedef struct _xsltStepOp xsltStepOp;
 typedef xsltStepOp *xsltStepOpPtr;
@@ -84,6 +99,7 @@ struct _xsltCompMatch {
     const xmlChar *modeURI;      /* the mode URI */
     xsltTemplatePtr template;    /* the associated template */
 
+    int direct;
     /* TODO fix the statically allocated size steps[] */
     int nbStep;
     int maxStep;
@@ -124,8 +140,7 @@ xsltNewCompMatch(void) {
 
     cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
     if (cur == NULL) {
-       xsltPrintErrorContext(NULL, NULL, NULL);
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
                "xsltNewCompMatch : malloc failed\n");
        return(NULL);
     }
@@ -133,6 +148,7 @@ xsltNewCompMatch(void) {
     cur->maxStep = 40;
     cur->nsNr = 0;
     cur->nsList = NULL;
+    cur->direct = 0;
     return(cur);
 }
 
@@ -151,10 +167,6 @@ xsltFreeCompMatch(xsltCompMatchPtr comp) {
        return;
     if (comp->pattern != NULL)
        xmlFree((xmlChar *)comp->pattern);
-    if (comp->mode != NULL)
-       xmlFree((xmlChar *)comp->mode);
-    if (comp->modeURI != NULL)
-       xmlFree((xmlChar *)comp->modeURI);
     if (comp->nsList != NULL)
        xmlFree(comp->nsList);
     for (i = 0;i < comp->nbStep;i++) {
@@ -190,6 +202,28 @@ xsltFreeCompMatchList(xsltCompMatchPtr comp) {
 }
 
 /**
+ * xsltNormalizeCompSteps:
+ * @payload: pointer to template hash table entry
+ * @data: pointer to the stylesheet
+ * @name: template match name
+ *
+ * This is a hashtable scanner function to normalize the compiled
+ * steps of an imported stylesheet.
+ */
+void xsltNormalizeCompSteps(void *payload,
+        void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
+    xsltCompMatchPtr comp = payload;
+    xsltStylesheetPtr style = data;
+    int ix;
+
+    for (ix = 0; ix < comp->nbStep; ix++) {
+        comp->steps[ix].previousExtra += style->extrasNr;
+        comp->steps[ix].indexExtra += style->extrasNr;
+        comp->steps[ix].lenExtra += style->extrasNr;
+    }
+}
+
+/**
  * xsltNewParserContext:
  * @style:  the stylesheet
  * @ctxt:  the transformation context, if done at run-time
@@ -204,8 +238,7 @@ xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
 
     cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
     if (cur == NULL) {
-       xsltPrintErrorContext(NULL, NULL, NULL);
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
                "xsltNewParserContext : malloc failed\n");
        return(NULL);
     }
@@ -245,8 +278,7 @@ xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
                  xsltOp op, xmlChar * value, xmlChar * value2)
 {
     if (comp->nbStep >= 40) {
-        xsltPrintErrorContext(NULL, NULL, NULL);        /* TODO */
-        xsltGenericError(xsltGenericErrorContext,
+        xsltTransformError(NULL, NULL, NULL,
                          "xsltCompMatchAdd: overflow\n");
         return (-1);
     }
@@ -269,7 +301,24 @@ xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
            xsltAllocateExtra(ctxt->style);
     }
     if (op == XSLT_OP_PREDICATE) {
-       comp->steps[comp->nbStep].comp = xmlXPathCompile(value);
+       xmlXPathContextPtr xctxt;
+
+       if (ctxt->style != NULL)
+           xctxt = xmlXPathNewContext(ctxt->style->doc);
+       else
+           xctxt = xmlXPathNewContext(NULL);
+#ifdef XML_XPATH_NOVAR
+       xctxt->flags = XML_XPATH_NOVAR;
+#endif
+       if (ctxt->style != NULL)
+           xctxt->dict = ctxt->style->dict;
+       comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value);
+       xmlXPathFreeContext(xctxt);
+       if (comp->steps[comp->nbStep].comp == NULL) {
+           xsltTransformError(NULL, ctxt->style, ctxt->elem,
+                   "Failed to compile predicate\n");
+           ctxt->style->errors++;
+       }
     }
     comp->nbStep++;
     return (0);
@@ -337,6 +386,27 @@ xsltReverseCompMatch(xsltCompMatchPtr comp) {
        i++;
     }
     comp->steps[comp->nbStep++].op = XSLT_OP_END;
+    /*
+     * detect consecutive XSLT_OP_PREDICATE indicating a direct
+     * matching should be done.
+     */
+    for (i = 0;i < comp->nbStep - 1;i++) {
+        if ((comp->steps[i].op == XSLT_OP_PREDICATE) &&
+           (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
+
+           comp->direct = 1;
+           if (comp->pattern[0] != '/') {
+               xmlChar *query;
+
+               query = xmlStrdup((const xmlChar *)"//");
+               query = xmlStrcat(query, comp->pattern);
+
+               xmlFree((xmlChar *) comp->pattern);
+               comp->pattern = query;
+           }
+           break;
+       }
+    }
 }
 
 /************************************************************************
@@ -345,6 +415,132 @@ xsltReverseCompMatch(xsltCompMatchPtr comp) {
  *                                                                     *
  ************************************************************************/
 
+static int
+xsltPatPushState(xsltStepStates *states, int step, xmlNodePtr node) {
+    if ((states->states == NULL) || (states->maxstates <= 0)) {
+        states->maxstates = 4;
+       states->nbstates = 0;
+       states->states = xmlMalloc(4 * sizeof(xsltStepState));
+    }
+    else if (states->maxstates <= states->nbstates) {
+        xsltStepState *tmp;
+
+       tmp = (xsltStepStatePtr) xmlRealloc(states->states,
+                              2 * states->maxstates * sizeof(xsltStepState));
+       if (tmp == NULL)
+           return(-1);
+       states->states = tmp;
+       states->maxstates *= 2;
+    }
+    states->states[states->nbstates].step = step;
+    states->states[states->nbstates++].node = node;
+#if 0
+    fprintf(stderr, "Push: %d, %s\n", step, node->name);
+#endif
+    return(0);
+}
+
+/**
+ * xsltTestCompMatchDirect:
+ * @ctxt:  a XSLT process context
+ * @comp: the precompiled pattern
+ * @node: a node
+ *
+ * Test whether the node matches the pattern, do a direct evalutation
+ * and not a step by step evaluation.
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+static int
+xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
+                       xmlNodePtr node) {
+    xsltStepOpPtr sel = NULL;
+    xmlDocPtr prevdoc;
+    xmlDocPtr doc;
+    xmlXPathObjectPtr list;
+    int ix, j;
+    int nocache = 0;
+    int isRVT;
+
+    doc = node->doc;
+    if ((doc != NULL) &&
+       (doc->name != NULL) &&
+       (doc->name[0] == ' ') &&
+       (xmlStrEqual(BAD_CAST doc->name,
+                    BAD_CAST " fake node libxslt")))
+       isRVT = 1;
+    else
+       isRVT = 0;
+    sel = &comp->steps[0]; /* store extra in first step arbitrarily */
+
+    prevdoc = (xmlDocPtr)
+       XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+    ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
+    list = (xmlXPathObjectPtr)
+       XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
+    
+    if ((list == NULL) || (prevdoc != doc)) {
+       xmlXPathObjectPtr newlist;
+       xmlNodePtr parent = node->parent;
+       xmlDocPtr olddoc;
+       xmlNodePtr oldnode;
+
+       oldnode = ctxt->xpathCtxt->node;
+       olddoc = ctxt->xpathCtxt->doc;
+       ctxt->xpathCtxt->node = node;
+       ctxt->xpathCtxt->doc = doc;
+       newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
+       ctxt->xpathCtxt->node = oldnode;
+       ctxt->xpathCtxt->doc = olddoc;
+       if (newlist == NULL)
+           return(-1);
+       if (newlist->type != XPATH_NODESET) {
+           xmlXPathFreeObject(newlist);
+           return(-1);
+       }
+       ix = 0;
+
+       if ((parent == NULL) || (node->doc == NULL) || isRVT)
+           nocache = 1;
+       
+       if (nocache == 0) {
+           if (list != NULL)
+               xmlXPathFreeObject(list);
+           list = newlist;
+
+           XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
+               (void *) list;
+           XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
+               (void *) doc;
+           XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
+               0;
+           XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
+               (xmlFreeFunc) xmlXPathFreeObject;
+       } else
+           list = newlist;
+    }
+    if ((list->nodesetval == NULL) ||
+       (list->nodesetval->nodeNr <= 0)) {
+       if (nocache == 1)
+           xmlXPathFreeObject(list);
+       return(0);
+    }
+    /* TODO: store the index and use it for the scan */
+    if (ix == 0) {
+       for (j = 0;j < list->nodesetval->nodeNr;j++) {
+           if (list->nodesetval->nodeTab[j] == node) {
+               if (nocache == 1)
+                   xmlXPathFreeObject(list);
+               return(1);
+           }
+       }
+    } else {
+    }
+    if (nocache == 1)
+       xmlXPathFreeObject(list);
+    return(0);
+}
+
 /**
  * xsltTestCompMatch:
  * @ctxt:  a XSLT process context
@@ -353,7 +549,7 @@ xsltReverseCompMatch(xsltCompMatchPtr comp) {
  * @mode:  the mode name or NULL
  * @modeURI:  the mode URI or NULL
  *
- * Test wether the node matches the pattern
+ * Test whether the node matches the pattern
  *
  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
  */
@@ -362,18 +558,21 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                  xmlNodePtr node, const xmlChar *mode,
                  const xmlChar *modeURI) {
     int i;
-    xsltStepOpPtr step, select = NULL;
+    xsltStepOpPtr step, sel = NULL;
+    xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
 
     if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
-       xsltPrintErrorContext(ctxt, NULL, node);
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(ctxt, NULL, node,
                "xsltTestCompMatch: null arg\n");
         return(-1);
     }
     if (mode != NULL) {
        if (comp->mode == NULL)
            return(0);
-       if ((comp->mode != mode) && (!xmlStrEqual(comp->mode, mode)))
+       /*
+        * both mode strings must be interned on the stylesheet dictionary
+        */
+       if (comp->mode != mode)
            return(0);
     } else {
        if (comp->mode != NULL)
@@ -382,20 +581,25 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
     if (modeURI != NULL) {
        if (comp->modeURI == NULL)
            return(0);
-       if ((comp->modeURI != modeURI) &&
-           (!xmlStrEqual(comp->modeURI, modeURI)))
+       /*
+        * both modeURI strings must be interned on the stylesheet dictionary
+        */
+       if (comp->modeURI != modeURI)
            return(0);
     } else {
        if (comp->modeURI != NULL)
            return(0);
     }
-    for (i = 0;i < comp->nbStep;i++) {
+
+    i = 0;
+restart:
+    for (;i < comp->nbStep;i++) {
        step = &comp->steps[i];
        if (step->op != XSLT_OP_PREDICATE)
-           select = step;
+           sel = step;
        switch (step->op) {
             case XSLT_OP_END:
-               return(1);
+               goto found;
             case XSLT_OP_ROOT:
                if ((node->type == XML_DOCUMENT_NODE) ||
 #ifdef LIBXML_DOCB_ENABLED
@@ -403,24 +607,28 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
 #endif
                    (node->type == XML_HTML_DOCUMENT_NODE))
                    continue;
-               return(0);
+               if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
+                   continue;
+               goto rollback;
             case XSLT_OP_ELEM:
                if (node->type != XML_ELEMENT_NODE)
-                   return(0);
+                   goto rollback;
                if (step->value == NULL)
                    continue;
+               if (step->value[0] != node->name[0])
+                   goto rollback;
                if (!xmlStrEqual(step->value, node->name))
-                   return(0);
+                   goto rollback;
 
                /* Namespace test */
                if (node->ns == NULL) {
                    if (step->value2 != NULL)
-                       return(0);
+                       goto rollback;
                } else if (node->ns->href != NULL) {
                    if (step->value2 == NULL)
-                       return(0);
+                       goto rollback;
                    if (!xmlStrEqual(step->value2, node->ns->href))
-                       return(0);
+                       goto rollback;
                }
                continue;
             case XSLT_OP_CHILD: {
@@ -432,13 +640,14 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                    (node->type != XML_DOCB_DOCUMENT_NODE) &&
 #endif
                    (node->type != XML_HTML_DOCUMENT_NODE))
-                   return(0);
+                   goto rollback;
 
                lst = node->children;
 
                if (step->value != NULL) {
                    while (lst != NULL) {
                        if ((lst->type == XML_ELEMENT_NODE) &&
+                           (step->value[0] == lst->name[0]) &&
                            (xmlStrEqual(step->value, lst->name)))
                            break;
                        lst = lst->next;
@@ -446,25 +655,24 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                    if (lst != NULL)
                        continue;
                }
-               return(0);
+               goto rollback;
            }
             case XSLT_OP_ATTR:
                if (node->type != XML_ATTRIBUTE_NODE)
-                   return(0);
-               if (step->value == NULL)
-                   continue;
-               if (!xmlStrEqual(step->value, node->name))
-                   return(0);
-
+                   goto rollback;
+               if (step->value != NULL) {
+                   if (step->value[0] != node->name[0])
+                       goto rollback;
+                   if (!xmlStrEqual(step->value, node->name))
+                       goto rollback;
+               }
                /* Namespace test */
                if (node->ns == NULL) {
                    if (step->value2 != NULL)
-                       return(0);
-               } else if (node->ns->href != NULL) {
-                   if (step->value2 == NULL)
-                       return(0);
+                       goto rollback;
+               } else if (step->value2 != NULL) {
                    if (!xmlStrEqual(step->value2, node->ns->href))
-                       return(0);
+                       goto rollback;
                }
                continue;
             case XSLT_OP_PARENT:
@@ -474,51 +682,66 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                    (node->type == XML_DOCB_DOCUMENT_NODE) ||
 #endif
                    (node->type == XML_NAMESPACE_DECL))
-                   return(0);
+                   goto rollback;
                node = node->parent;
                if (node == NULL)
-                   return(0);
+                   goto rollback;
                if (step->value == NULL)
                    continue;
+               if (step->value[0] != node->name[0])
+                   goto rollback;
                if (!xmlStrEqual(step->value, node->name))
-                   return(0);
+                   goto rollback;
                /* Namespace test */
                if (node->ns == NULL) {
                    if (step->value2 != NULL)
-                       return(0);
+                       goto rollback;
                } else if (node->ns->href != NULL) {
                    if (step->value2 == NULL)
-                       return(0);
+                       goto rollback;
                    if (!xmlStrEqual(step->value2, node->ns->href))
-                       return(0);
+                       goto rollback;
                }
                continue;
             case XSLT_OP_ANCESTOR:
                /* TODO: implement coalescing of ANCESTOR/NODE ops */
                if (step->value == NULL) {
-                   i++;
-                   step = &comp->steps[i];
+                   step = &comp->steps[i+1];
                    if (step->op == XSLT_OP_ROOT)
-                       return(1);
-                   if (step->op != XSLT_OP_ELEM)
-                       return(0);
-                   if (step->value == NULL)
-                       return(-1);
+                       goto found;
+                   /* added NS, ID and KEY as a result of bug 168208 */
+                   if ((step->op != XSLT_OP_ELEM) && 
+                       (step->op != XSLT_OP_ALL) && 
+                       (step->op != XSLT_OP_NS) &&
+                       (step->op != XSLT_OP_ID) &&
+                       (step->op != XSLT_OP_KEY))
+                       goto rollback;
                }
                if (node == NULL)
-                   return(0);
+                   goto rollback;
                if ((node->type == XML_DOCUMENT_NODE) ||
                    (node->type == XML_HTML_DOCUMENT_NODE) ||
 #ifdef LIBXML_DOCB_ENABLED
                    (node->type == XML_DOCB_DOCUMENT_NODE) ||
 #endif
                    (node->type == XML_NAMESPACE_DECL))
-                   return(0);
+                   goto rollback;
                node = node->parent;
+               if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
+                   xsltPatPushState(&states, i, node);
+                   continue;
+               }
+               i++;
+               if (step->value == NULL) {
+                   xsltPatPushState(&states, i - 1, node);
+                   continue;
+               }
                while (node != NULL) {
                    if (node == NULL)
-                       return(0);
-                   if (xmlStrEqual(step->value, node->name)) {
+                       goto rollback;
+                   if ((node->type == XML_ELEMENT_NODE) &&
+                       (step->value[0] == node->name[0]) &&
+                       (xmlStrEqual(step->value, node->name))) {
                        /* Namespace test */
                        if (node->ns == NULL) {
                            if (step->value2 == NULL)
@@ -532,18 +755,19 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                    node = node->parent;
                }
                if (node == NULL)
-                   return(0);
+                   goto rollback;
+               xsltPatPushState(&states, i - 1, node);
                continue;
             case XSLT_OP_ID: {
                /* TODO Handle IDs decently, must be done differently */
                xmlAttrPtr id;
 
                if (node->type != XML_ELEMENT_NODE)
-                   return(0);
+                   goto rollback;
 
                id = xmlGetID(node->doc, step->value);
                if ((id == NULL) || (id->parent != node))
-                   return(0);
+                   goto rollback;
                break;
            }
             case XSLT_OP_KEY: {
@@ -553,107 +777,79 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                list = xsltGetKey(ctxt, step->value,
                                  step->value3, step->value2);
                if (list == NULL)
-                   return(0);
+                   goto rollback;
                for (indx = 0;indx < list->nodeNr;indx++)
                    if (list->nodeTab[indx] == node)
                        break;
                if (indx >= list->nodeNr)
-                   return(0);
+                   goto rollback;
                break;
            }
             case XSLT_OP_NS:
                if (node->type != XML_ELEMENT_NODE)
-                   return(0);
+                   goto rollback;
                if (node->ns == NULL) {
                    if (step->value != NULL)
-                       return(0);
+                       goto rollback;
                } else if (node->ns->href != NULL) {
                    if (step->value == NULL)
-                       return(0);
+                       goto rollback;
                    if (!xmlStrEqual(step->value, node->ns->href))
-                       return(0);
+                       goto rollback;
                }
                break;
             case XSLT_OP_ALL:
                if (node->type != XML_ELEMENT_NODE)
-                   return(0);
+                   goto rollback;
                break;
            case XSLT_OP_PREDICATE: {
                xmlNodePtr oldNode;
+               xmlDocPtr doc;
                int oldCS, oldCP;
                int pos = 0, len = 0;
+               int isRVT;
+
                /*
-                * The simple existing predicate code cannot handle
-                * properly cascaded predicates. If in this situation
-                * compute directly the full node list once and check
-                * if the node is in the result list.
+                * when there is cascading XSLT_OP_PREDICATE, then use a
+                * direct computation approach. It's not done directly
+                * at the beginning of the routine to filter out as much
+                * as possible this costly computation.
                 */
-               if (comp->steps[i + 1].op == XSLT_OP_PREDICATE) {
-                   xmlNodePtr previous;
-                   xmlXPathObjectPtr list;
-                   int index, j;
-
-                   previous = (xmlNodePtr)
-                       XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra);
-                   index = (int)
-                       XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra);
-                   list = (xmlXPathObjectPtr)
-                       XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra);
-                   if (list == NULL) {
-                       xmlChar *query;
-
-                       if (comp->pattern[0] == '/')
-                           query = xmlStrdup(comp->pattern);
-                       else {
-                           query = xmlStrdup((const xmlChar *)"//");
-                           query = xmlStrcat(query, comp->pattern);
-                       }
-                       list = xmlXPathEval(query, ctxt->xpathCtxt);
-                       xmlFree(query);
-                       if (list == NULL)
-                           return(-1);
-                       if (list->type != XPATH_NODESET) {
-                           xmlXPathFreeObject(list);
-                           return(-1);
-                       }
-                       XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra) =
-                           (void *) list;
-                       XSLT_RUNTIME_EXTRA_FREE(ctxt, select->lenExtra) =
-                           (xmlFreeFunc) xmlXPathFreeObject;
-                   }
-                   if ((list->nodesetval == NULL) ||
-                       (list->nodesetval->nodeNr <= 0))
-                       return(0);
-                   if (index == 0) {
-                       for (j = 0;j < list->nodesetval->nodeNr;j++) {
-                           if (list->nodesetval->nodeTab[j] == node) {
-                               return(1);
-                           }
-                       }
-                   } else {
+               if (comp->direct) {
+                   if (states.states != NULL) {
+                       /* Free the rollback states */
+                       xmlFree(states.states);
                    }
-                   return(0);
+                   return(xsltTestCompMatchDirect(ctxt, comp, node));
                }
+
+               doc = node->doc;
+               if ((doc != NULL) &&
+                   (doc->name != NULL) &&
+                   (doc->name[0] == ' ') &&
+                   (xmlStrEqual(BAD_CAST doc->name,
+                                BAD_CAST " fake node libxslt")))
+                   isRVT = 1;
+               else
+                   isRVT = 0;
+
                /*
                 * Depending on the last selection, one may need to
                 * recompute contextSize and proximityPosition.
-                *
-                * TODO: make this thread safe !
                 */
                oldCS = ctxt->xpathCtxt->contextSize;
                oldCP = ctxt->xpathCtxt->proximityPosition;
-               if ((select != NULL) &&
-                   (select->op == XSLT_OP_ELEM) &&
-                   (select->value != NULL) &&
+               if ((sel != NULL) &&
+                   (sel->op == XSLT_OP_ELEM) &&
+                   (sel->value != NULL) &&
                    (node->type == XML_ELEMENT_NODE) &&
                    (node->parent != NULL)) {
                    xmlNodePtr previous;
-                   int index;
+                   int ix, nocache = 0;
 
                    previous = (xmlNodePtr)
-                       XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra);
-                   index = (int)
-                       XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra);
+                       XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+                   ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
                    if ((previous != NULL) &&
                        (previous->parent == node->parent)) {
                        /*
@@ -665,10 +861,14 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                        while (sibling != NULL) {
                            if (sibling == previous)
                                break;
-                           if (xmlStrEqual(node->name, sibling->name)) {
-                               if ((select->value2 == NULL) ||
+                           if ((previous->type == XML_ELEMENT_NODE) &&
+                               (previous->name != NULL) &&
+                               (sibling->name != NULL) &&
+                               (previous->name[0] == sibling->name[0]) &&
+                               (xmlStrEqual(previous->name, sibling->name))) {
+                               if ((sel->value2 == NULL) ||
                                    ((sibling->ns != NULL) &&
-                                    (xmlStrEqual(select->value2,
+                                    (xmlStrEqual(sel->value2,
                                                  sibling->ns->href))))
                                    indx++;
                            }
@@ -681,23 +881,32 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                            while (sibling != NULL) {
                                if (sibling == previous)
                                    break;
-                               if ((select->value2 == NULL) ||
+                               if ((sel->value2 == NULL) ||
                                    ((sibling->ns != NULL) &&
-                                    (xmlStrEqual(select->value2,
+                                    (xmlStrEqual(sel->value2,
                                                  sibling->ns->href))))
                                    indx--;
                                sibling = sibling->next;
                            }
                        }
                        if (sibling != NULL) {
-                           pos = index + indx;
-                           len = (int)
-                               XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra);
-                           XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra) =
-                               node;
-                           XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
-                               (void *) pos;
-                           index = pos;
+                           pos = ix + indx;
+                           /*
+                            * If the node is in a Value Tree we need to
+                            * save len, but cannot cache the node!
+                            * (bugs 153137 and 158840)
+                            */
+                           if (node->doc != NULL) {
+                               len = XSLT_RUNTIME_EXTRA(ctxt,
+                                       sel->lenExtra, ival);
+                               if (!isRVT) {
+                                   XSLT_RUNTIME_EXTRA(ctxt,
+                                       sel->previousExtra, ptr) = node;
+                                   XSLT_RUNTIME_EXTRA(ctxt,
+                                       sel->indexExtra, ival) = pos;
+                               }
+                           }
+                           ix = pos;
                        } else
                            pos = 0;
                    } else {
@@ -705,43 +914,62 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                         * recompute the index
                         */
                        xmlNodePtr siblings = node->parent->children;
+                       xmlNodePtr parent = node->parent;
 
                        while (siblings != NULL) {
                            if (siblings->type == XML_ELEMENT_NODE) {
                                if (siblings == node) {
                                    len++;
                                    pos = len;
-                               } else if (xmlStrEqual(node->name,
-                                          siblings->name)) {
-                                   if ((select->value2 == NULL) ||
+                               } else if ((node->name != NULL) &&
+                                          (siblings->name != NULL) &&
+                                   (node->name[0] == siblings->name[0]) &&
+                                   (xmlStrEqual(node->name, siblings->name))) {
+                                   if ((sel->value2 == NULL) ||
                                        ((siblings->ns != NULL) &&
-                                        (xmlStrEqual(select->value2,
+                                        (xmlStrEqual(sel->value2,
                                                      siblings->ns->href))))
                                        len++;
                                }
                            }
                            siblings = siblings->next;
                        }
+                       if ((parent == NULL) || (node->doc == NULL))
+                           nocache = 1;
+                       else {
+                           while (parent->parent != NULL)
+                               parent = parent->parent;
+                           if (((parent->type != XML_DOCUMENT_NODE) &&
+                                (parent->type != XML_HTML_DOCUMENT_NODE)) ||
+                                (parent != (xmlNodePtr) node->doc))
+                               nocache = 1;
+                       }
                    }
                    if (pos != 0) {
                        ctxt->xpathCtxt->contextSize = len;
                        ctxt->xpathCtxt->proximityPosition = pos;
-                       XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra) =
-                           node;
-                       XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
-                           (void *) pos;
-                       XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra) =
-                           (void *) len;
+                       /*
+                        * If the node is in a Value Tree we cannot
+                        * cache it !
+                        */
+                       if ((!isRVT) && (node->doc != NULL) &&
+                           (nocache == 0)) {
+                           XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
+                               node;
+                           XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
+                               pos;
+                           XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
+                               len;
+                       }
                    }
-               } else if ((select != NULL) && (select->op == XSLT_OP_ALL) &&
+               } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
                           (node->type == XML_ELEMENT_NODE)) {
                    xmlNodePtr previous;
-                   int index;
+                   int ix, nocache = 0;
 
                    previous = (xmlNodePtr)
-                       XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra);
-                   index = (int)
-                       XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra);
+                       XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+                   ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
                    if ((previous != NULL) &&
                        (previous->parent == node->parent)) {
                        /*
@@ -770,13 +998,19 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                            }
                        }
                        if (sibling != NULL) {
-                           pos = index + indx;
-                           len = (int)
-                               XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra);
-                           XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra) =
-                               node;
-                           XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
-                               (void *) pos;
+                           pos = ix + indx;
+                           /*
+                            * If the node is in a Value Tree we cannot
+                            * cache it !
+                            */
+                           if ((node->doc != NULL) && !isRVT) {
+                               len = XSLT_RUNTIME_EXTRA(ctxt,
+                                       sel->lenExtra, ival);
+                               XSLT_RUNTIME_EXTRA(ctxt,
+                                       sel->previousExtra, ptr) = node;
+                               XSLT_RUNTIME_EXTRA(ctxt,
+                                       sel->indexExtra, ival) = pos;
+                           }
                        } else
                            pos = 0;
                    } else {
@@ -784,6 +1018,7 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                         * recompute the index
                         */
                        xmlNodePtr siblings = node->parent->children;
+                       xmlNodePtr parent = node->parent;
 
                        while (siblings != NULL) {
                            if (siblings->type == XML_ELEMENT_NODE) {
@@ -794,16 +1029,32 @@ xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
                            }
                            siblings = siblings->next;
                        }
+                       if ((parent == NULL) || (node->doc == NULL))
+                           nocache = 1;
+                       else {
+                           while (parent->parent != NULL)
+                               parent = parent->parent;
+                           if (((parent->type != XML_DOCUMENT_NODE) &&
+                                (parent->type != XML_HTML_DOCUMENT_NODE)) ||
+                                (parent != (xmlNodePtr) node->doc))
+                               nocache = 1;
+                       }
                    }
                    if (pos != 0) {
                        ctxt->xpathCtxt->contextSize = len;
                        ctxt->xpathCtxt->proximityPosition = pos;
-                       XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra) =
-                           node;
-                       XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
-                           (void *) pos;
-                       XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra) =
-                           (void *) len;
+                       /*
+                        * If the node is in a Value Tree we cannot
+                        * cache it !
+                        */
+                       if ((node->doc != NULL) && (nocache == 0) && !isRVT) {
+                           XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
+                               node;
+                           XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
+                               pos;
+                           XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
+                               len;
+                       }
                    }
                }
                oldNode = ctxt->node;
@@ -830,46 +1081,60 @@ wrong_index:
                    ctxt->xpathCtxt->proximityPosition = oldCP;
                }
                ctxt->node = oldNode;
-               return(0);
+               goto rollback;
            }
             case XSLT_OP_PI:
                if (node->type != XML_PI_NODE)
-                   return(0);
+                   goto rollback;
                if (step->value != NULL) {
                    if (!xmlStrEqual(step->value, node->name))
-                       return(0);
+                       goto rollback;
                }
                break;
             case XSLT_OP_COMMENT:
                if (node->type != XML_COMMENT_NODE)
-                   return(0);
+                   goto rollback;
                break;
             case XSLT_OP_TEXT:
                if ((node->type != XML_TEXT_NODE) &&
                    (node->type != XML_CDATA_SECTION_NODE))
-                   return(0);
+                   goto rollback;
                break;
             case XSLT_OP_NODE:
                switch (node->type) {
-                   case XML_DOCUMENT_NODE:
-                   case XML_HTML_DOCUMENT_NODE:
-#ifdef LIBXML_DOCB_ENABLED
-                   case XML_DOCB_DOCUMENT_NODE:
-#endif
                    case XML_ELEMENT_NODE:
                    case XML_CDATA_SECTION_NODE:
                    case XML_PI_NODE:
                    case XML_COMMENT_NODE:
                    case XML_TEXT_NODE:
-                   case XML_ATTRIBUTE_NODE:
                        break;
                    default:
-                       return(0);
+                       goto rollback;
                }
                break;
        }
     }
+found:
+    if (states.states != NULL) {
+        /* Free the rollback states */
+       xmlFree(states.states);
+    }
     return(1);
+rollback:
+    /* got an error try to rollback */
+    if (states.states == NULL)
+       return(0);
+    if (states.nbstates <= 0) {
+       xmlFree(states.states);
+       return(0);
+    }
+    states.nbstates--;
+    i = states.states[states.nbstates].step;
+    node = states.states[states.nbstates].node;
+#if 0
+    fprintf(stderr, "Pop: %d, %s\n", i, node->name);
+#endif
+    goto restart;
 }
 
 /**
@@ -878,7 +1143,7 @@ wrong_index:
  * @node: a node
  * @comp: the precompiled pattern list
  *
- * Test wether the node matches one of the patterns in the list
+ * Test whether the node matches one of the patterns in the list
  *
  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
  */
@@ -910,7 +1175,7 @@ xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
 #define CUR_PTR ctxt->cur
 
 #define SKIP_BLANKS                                                    \
-    while (IS_BLANK(CUR)) NEXT
+    while (IS_BLANK_CH(CUR)) NEXT
 
 #define CURRENT (*ctxt->cur)
 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
@@ -1110,8 +1375,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
     xmlChar *lit2 = NULL;
 
     if (CUR != '(') {
-       xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-       xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
                "xsltCompileIdKeyPattern : ( expected\n");
        ctxt->error = 1;
        return;
@@ -1124,8 +1388,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
            return;
        SKIP_BLANKS;
        if (CUR != ')') {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileIdKeyPattern : ) expected\n");
            ctxt->error = 1;
            return;
@@ -1140,8 +1403,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
            return;
        SKIP_BLANKS;
        if (CUR != ',') {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileIdKeyPattern : , expected\n");
            ctxt->error = 1;
            return;
@@ -1153,8 +1415,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
            return;
        SKIP_BLANKS;
        if (CUR != ')') {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileIdKeyPattern : ) expected\n");
            ctxt->error = 1;
            return;
@@ -1171,8 +1432,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
                return;
            SKIP_BLANKS;
            if (CUR != ')') {
-               xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-               xsltGenericError(xsltGenericErrorContext,
+               xsltTransformError(NULL, NULL, NULL,
                        "xsltCompileIdKeyPattern : ) expected\n");
                ctxt->error = 1;
                return;
@@ -1184,8 +1444,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
        NEXT;
        SKIP_BLANKS;
        if (CUR != ')') {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileIdKeyPattern : ) expected\n");
            ctxt->error = 1;
            return;
@@ -1196,8 +1455,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
        NEXT;
        SKIP_BLANKS;
        if (CUR != ')') {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileIdKeyPattern : ) expected\n");
            ctxt->error = 1;
            return;
@@ -1208,8 +1466,7 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
        NEXT;
        SKIP_BLANKS;
        if (CUR != ')') {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileIdKeyPattern : ) expected\n");
            ctxt->error = 1;
            return;
@@ -1217,14 +1474,12 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
        NEXT;
        PUSH(XSLT_OP_NODE, NULL, NULL);
     } else if (aid) {
-       xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-       xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
            "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
        ctxt->error = 1;
        return;
     } else {
-       xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-       xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
            "xsltCompileIdKeyPattern : node type\n");
        ctxt->error = 1;
        return;
@@ -1270,7 +1525,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
        if (CUR == '*') {
            NEXT;
            PUSH(XSLT_OP_ATTR, NULL, NULL);
-           return;
+           goto parse_predicate;
        }
        token = xsltScanQName(ctxt, &prefix);
        if (prefix != NULL) {
@@ -1278,8 +1533,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
 
            ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
            if (ns == NULL) {
-               xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-               xsltGenericError(xsltGenericErrorContext,
+               xsltTransformError(NULL, NULL, NULL,
                "xsltCompileStepPattern : no namespace bound to prefix %s\n",
                                 prefix);
            } else {
@@ -1293,14 +1547,13 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
                PUSH(XSLT_OP_ATTR, NULL, URL);
                return;
            }
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileStepPattern : Name expected\n");
            ctxt->error = 1;
            goto error;
        }
        PUSH(XSLT_OP_ATTR, token, URL);
-       return;
+       goto parse_predicate;
     }
     if (token == NULL)
        token = xsltScanName(ctxt);
@@ -1310,8 +1563,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
            PUSH(XSLT_OP_ALL, token, NULL);
            goto parse_predicate;
        } else {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileStepPattern : Name expected\n");
            ctxt->error = 1;
            goto error;
@@ -1336,8 +1588,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
            token = xsltScanName(ctxt);
            ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
            if (ns == NULL) {
-               xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-               xsltGenericError(xsltGenericErrorContext,
+               xsltTransformError(NULL, NULL, NULL,
            "xsltCompileStepPattern : no namespace bound to prefix %s\n",
                                 prefix);
                ctxt->error = 1;
@@ -1351,8 +1602,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
                    NEXT;
                    PUSH(XSLT_OP_NS, URL, NULL);
                } else {
-                   xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-                   xsltGenericError(xsltGenericErrorContext,
+                   xsltTransformError(NULL, NULL, NULL,
                            "xsltCompileStepPattern : Name expected\n");
                    ctxt->error = 1;
                    goto error;
@@ -1371,8 +1621,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
                        PUSH(XSLT_OP_ALL, token, NULL);
                        goto parse_predicate;
                    } else {
-                       xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-                       xsltGenericError(xsltGenericErrorContext,
+                       xsltTransformError(NULL, NULL, NULL,
                            "xsltCompileStepPattern : QName expected\n");
                        ctxt->error = 1;
                        goto error;
@@ -1392,8 +1641,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
                xmlFree(token);
                token = xsltScanName(ctxt);
                if (token == NULL) {
-                   xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-                   xsltGenericError(xsltGenericErrorContext,
+                   xsltTransformError(NULL, NULL, NULL,
                            "xsltCompileStepPattern : QName expected\n");
                    ctxt->error = 1;
                    goto error;
@@ -1409,8 +1657,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
                }
                PUSH(XSLT_OP_ATTR, name, URL);
            } else {
-               xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-               xsltGenericError(xsltGenericErrorContext,
+               xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
                ctxt->error = 1;
                goto error;
@@ -1460,11 +1707,10 @@ parse_predicate:
            NEXT;
        }
        if (CUR == 0) {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileStepPattern : ']' expected\n");
            ctxt->error = 1;
-           goto error;
+           return;
         }
        ret = xmlStrndup(q, CUR_PTR - q);
        PUSH(XSLT_OP_PREDICATE, ret, NULL);
@@ -1510,7 +1756,7 @@ xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
            PUSH(XSLT_OP_PARENT, NULL, NULL);
            NEXT;
            SKIP_BLANKS;
-           if ((CUR != 0) || (CUR == '|')) {
+           if ((CUR != 0) && (CUR != '|')) {
                xsltCompileRelativePathPattern(ctxt, NULL);
            }
        } else {
@@ -1545,6 +1791,7 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
         */
        NEXT;
        NEXT;
+       ctxt->comp->priority = 0.5;     /* '//' means not 0 priority */
        xsltCompileRelativePathPattern(ctxt, NULL);
     } else if (CUR == '/') {
        /*
@@ -1553,7 +1800,7 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
        NEXT;
        SKIP_BLANKS;
        PUSH(XSLT_OP_ROOT, NULL, NULL);
-       if ((CUR != 0) || (CUR == '|')) {
+       if ((CUR != 0) && (CUR != '|')) {
            PUSH(XSLT_OP_PARENT, NULL, NULL);
            xsltCompileRelativePathPattern(ctxt, NULL);
        }
@@ -1565,8 +1812,7 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
        xmlChar *name;
        name = xsltScanName(ctxt);
        if (name == NULL) {
-           xsltPrintErrorContext(NULL, NULL, NULL); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, NULL,
                    "xsltCompileLocationPathPattern : Name expected\n");
            ctxt->error = 1;
            return;
@@ -1619,8 +1865,7 @@ xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
     int current, start, end, level, j;
 
     if (pattern == NULL) {
-       xsltPrintErrorContext(NULL, NULL, node); /* TODO */
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, node,
                         "xsltCompilePattern : NULL pattern\n");
        return(NULL);
     }
@@ -1633,7 +1878,7 @@ xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
     current = end = 0;
     while (pattern[current] != 0) {
        start = current;
-       while (IS_BLANK(pattern[current]))
+       while (IS_BLANK_CH(pattern[current]))
            current++;
        end = current;
        level = 0;
@@ -1654,8 +1899,7 @@ xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
            end++;
        }
        if (current == end) {
-           xsltPrintErrorContext(NULL, NULL, node); /* TODO */
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, NULL, node,
                             "xsltCompilePattern : NULL pattern\n");
            goto error;
        }
@@ -1689,9 +1933,19 @@ xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
                         "xsltCompilePattern : parsing '%s'\n",
                         element->pattern);
 #endif
+       /*
+        Preset default priority to be zero.
+        This may be changed by xsltCompileLocationPathPattern.
+        */
+       element->priority = 0;
        xsltCompileLocationPathPattern(ctxt);
-       if (ctxt->error)
+       if (ctxt->error) {
+           xsltTransformError(NULL, style, node,
+                            "xsltCompilePattern : failed to compile '%s'\n",
+                            element->pattern);
+           if (style != NULL) style->errors++;
            goto error;
+       }
 
        /*
         * Reverse for faster interpretation.
@@ -1701,42 +1955,36 @@ xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
        /*
         * Set-up the priority
         */
-       if (((element->steps[0].op == XSLT_OP_ELEM) ||
-            (element->steps[0].op == XSLT_OP_ATTR)) &&
-           (element->steps[0].value != NULL) &&
-           (element->steps[1].op == XSLT_OP_END)) {
-           element->priority = 0;
-#if 0
-       } else if ((element->steps[0].op == XSLT_OP_ROOT) &&
-                  (element->steps[1].op == XSLT_OP_END)) {
-           element->priority = 0;
-#endif
-       } else if ((element->steps[0].op == XSLT_OP_PI) &&
-                  (element->steps[0].value != NULL) &&
-                  (element->steps[1].op == XSLT_OP_END)) {
-           element->priority = 0;
-       } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
-                  (element->steps[0].value2 != NULL) &&
-                  (element->steps[1].op == XSLT_OP_END)) {
-           element->priority = -0.25;
-       } else if ((element->steps[0].op == XSLT_OP_NS) &&
-                  (element->steps[0].value != NULL) &&
-                  (element->steps[1].op == XSLT_OP_END)) {
-           element->priority = -0.25;
-       } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
-                  (element->steps[0].value == NULL) &&
-                  (element->steps[0].value2 == NULL) &&
-                  (element->steps[1].op == XSLT_OP_END)) {
-           element->priority = -0.5;
-       } else if (((element->steps[0].op == XSLT_OP_PI) ||
-                   (element->steps[0].op == XSLT_OP_TEXT) ||
-                   (element->steps[0].op == XSLT_OP_ALL) ||
-                   (element->steps[0].op == XSLT_OP_NODE) ||
-                   (element->steps[0].op == XSLT_OP_COMMENT)) &&
-                  (element->steps[1].op == XSLT_OP_END)) {
-           element->priority = -0.5;
-       } else {
-           element->priority = 0.5;
+       if (element->priority == 0) {   /* if not yet determined */
+           if (((element->steps[0].op == XSLT_OP_ELEM) ||
+                (element->steps[0].op == XSLT_OP_ATTR) ||
+                (element->steps[0].op == XSLT_OP_PI)) &&
+               (element->steps[0].value != NULL) &&
+               (element->steps[1].op == XSLT_OP_END)) {
+               ;       /* previously preset */
+           } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
+                      (element->steps[0].value2 != NULL) &&
+                      (element->steps[1].op == XSLT_OP_END)) {
+                       element->priority = -0.25;
+           } else if ((element->steps[0].op == XSLT_OP_NS) &&
+                      (element->steps[0].value != NULL) &&
+                      (element->steps[1].op == XSLT_OP_END)) {
+                       element->priority = -0.25;
+           } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
+                      (element->steps[0].value == NULL) &&
+                      (element->steps[0].value2 == NULL) &&
+                      (element->steps[1].op == XSLT_OP_END)) {
+                       element->priority = -0.5;
+           } else if (((element->steps[0].op == XSLT_OP_PI) ||
+                      (element->steps[0].op == XSLT_OP_TEXT) ||
+                      (element->steps[0].op == XSLT_OP_ALL) ||
+                      (element->steps[0].op == XSLT_OP_NODE) ||
+                      (element->steps[0].op == XSLT_OP_COMMENT)) &&
+                      (element->steps[1].op == XSLT_OP_END)) {
+                       element->priority = -0.5;
+           } else {
+               element->priority = 0.5;
+           }
        }
 #ifdef WITH_XSLT_DEBUG_PATTERN
        xsltGenericDebug(xsltGenericDebugContext,
@@ -1748,9 +1996,9 @@ xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
        current = end;
     }
     if (end == 0) {
-       xsltPrintErrorContext(NULL, NULL, node); /* TODO */
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, style, node,
                         "xsltCompilePattern : NULL pattern\n");
+       if (style != NULL) style->errors++;
        goto error;
     }
 
@@ -1801,9 +2049,9 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
        
        pat->template = cur;
        if (mode != NULL)
-           pat->mode = xmlStrdup(mode);
+           pat->mode = xmlDictLookup(style->dict, mode, -1);
        if (modeURI != NULL)
-           pat->modeURI = xmlStrdup(modeURI);
+           pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
        if (priority != XSLT_PAT_NO_PRIORITY)
            pat->priority = priority;
 
@@ -1817,7 +2065,6 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
            else
                top = (xsltCompMatchPtr *) &(style->attrMatch);
            break;
-        case XSLT_OP_ELEM:
         case XSLT_OP_CHILD:
         case XSLT_OP_PARENT:
         case XSLT_OP_ANCESTOR:
@@ -1837,8 +2084,7 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
            break;
         case XSLT_OP_END:
        case XSLT_OP_PREDICATE:
-           xsltPrintErrorContext(NULL, style, NULL);
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, style, NULL,
                             "xsltAddTemplate: invalid compiled pattern\n");
            xsltFreeCompMatch(pat);
            return(-1);
@@ -1858,12 +2104,12 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
        case XSLT_OP_TEXT:
            top = (xsltCompMatchPtr *) &(style->textMatch);
            break;
+        case XSLT_OP_ELEM:
        case XSLT_OP_NODE:
            if (pat->steps[0].value != NULL)
                name = pat->steps[0].value;
            else
                top = (xsltCompMatchPtr *) &(style->elemMatch);
-           
            break;
        }
        if (name != NULL) {
@@ -1919,8 +2165,7 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
                list->next = pat;
            }
        } else {
-           xsltPrintErrorContext(NULL, style, NULL);
-           xsltGenericError(xsltGenericErrorContext,
+           xsltTransformError(NULL, style, NULL,
                             "xsltAddTemplate: invalid compiled pattern\n");
            xsltFreeCompMatch(pat);
            return(-1);
@@ -1960,6 +2205,7 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
     const xmlChar *name = NULL;
     xsltCompMatchPtr list = NULL;
     float priority;
+    int keyed = 0;
 
     if ((ctxt == NULL) || (node == NULL))
        return(NULL);
@@ -1979,6 +2225,8 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
             */
            switch (node->type) {
                case XML_ELEMENT_NODE:
+                   if (node->name[0] == ' ')
+                       break;
                case XML_ATTRIBUTE_NODE:
                case XML_PI_NODE:
                    name = node->name;
@@ -2008,7 +2256,7 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
        }
        if (name != NULL) {
            /*
-            * find the list of appliable expressions based on the name
+            * find the list of applicable expressions based on the name
             */
            list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
                                             name, ctxt->mode, ctxt->modeURI);
@@ -2030,24 +2278,41 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
         */
        switch (node->type) {
            case XML_ELEMENT_NODE:
-               list = curstyle->elemMatch;
+               if (node->name[0] == ' ')
+                   list = curstyle->rootMatch;
+               else
+                   list = curstyle->elemMatch;
+               if (node->psvi != NULL) keyed = 1;
                break;
-           case XML_ATTRIBUTE_NODE:
+           case XML_ATTRIBUTE_NODE: {
+               xmlAttrPtr attr;
+
                list = curstyle->attrMatch;
+               attr = (xmlAttrPtr) node;
+               if (attr->psvi != NULL) keyed = 1;
                break;
+           }
            case XML_PI_NODE:
                list = curstyle->piMatch;
+               if (node->psvi != NULL) keyed = 1;
                break;
            case XML_DOCUMENT_NODE:
-           case XML_HTML_DOCUMENT_NODE:
+           case XML_HTML_DOCUMENT_NODE: {
+               xmlDocPtr doc;
+
                list = curstyle->rootMatch;
+               doc = (xmlDocPtr) node;
+               if (doc->psvi != NULL) keyed = 1;
                break;
+           }
            case XML_TEXT_NODE:
            case XML_CDATA_SECTION_NODE:
                list = curstyle->textMatch;
+               if (node->psvi != NULL) keyed = 1;
                break;
            case XML_COMMENT_NODE:
                list = curstyle->commentMatch;
+               if (node->psvi != NULL) keyed = 1;
                break;
            case XML_ENTITY_REF_NODE:
            case XML_ENTITY_NODE:
@@ -2064,7 +2329,6 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
                break;
            default:
                break;
-
        }
        while ((list != NULL) &&
               ((ret == NULL)  || (list->priority > priority))) {
@@ -2080,7 +2344,21 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
         * Some of the tests for elements can also apply to documents
         */
        if ((node->type == XML_DOCUMENT_NODE) ||
-           (node->type == XML_HTML_DOCUMENT_NODE)) {
+           (node->type == XML_HTML_DOCUMENT_NODE) ||
+           (node->type == XML_TEXT_NODE)) {
+           list = curstyle->elemMatch;
+           while ((list != NULL) &&
+                  ((ret == NULL)  || (list->priority > priority))) {
+               if (xsltTestCompMatch(ctxt, list, node,
+                                     ctxt->mode, ctxt->modeURI)) {
+                   ret = list->template;
+                   priority = list->priority;
+                   break;
+               }
+               list = list->next;
+           }
+       } else if ((node->type == XML_PI_NODE) ||
+                  (node->type == XML_COMMENT_NODE)) {
            list = curstyle->elemMatch;
            while ((list != NULL) &&
                   ((ret == NULL)  || (list->priority > priority))) {
@@ -2094,7 +2372,7 @@ xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
            }
        }
 
-       if (node->_private != NULL) {
+       if (keyed) {
            list = curstyle->keyMatch;
            while ((list != NULL) &&
                   ((ret == NULL)  || (list->priority > priority))) {
@@ -2158,36 +2436,3 @@ xsltFreeTemplateHashes(xsltStylesheetPtr style) {
         xsltFreeCompMatchList(style->commentMatch);
 }
 
-#if 0
-/**
- * xsltMatchPattern
- * @node: a node in the source tree
- * @pattern: an XSLT pattern
- * @ctxtdoc:  context document (for namespaces)
- * @ctxtnode:  context node (for namespaces)
- *
- * Determine if a node matches a pattern.
- */
-int
-xsltMatchPattern(xsltTransformContextPtr context,
-                xmlNodePtr node,
-                const xmlChar *pattern,
-                xmlDocPtr ctxtdoc,
-                xmlNodePtr ctxtnode)
-{
-    int match = 0;
-    xsltCompMatchPtr first, comp;
-
-    if ((context != NULL) && (pattern != NULL)) {
-       first = xsltCompilePattern(pattern, ctxtdoc, ctxtnode);
-       for (comp = first; comp != NULL; comp = comp->next) {
-           match = xsltTestCompMatch(context, comp, node, NULL, NULL);
-           if (match)
-               break; /* for */
-       }
-       if (first)
-           xsltFreeCompMatchList(first);
-    }
-    return match;
-}
-#endif