preparing release 1.1.15 a bit more cleanup Daniel
[platform/upstream/libxslt.git] / libxslt / pattern.c
index e1510d1..ed8e858 100644 (file)
@@ -6,10 +6,16 @@
  *
  * See Copyright for the status of this software.
  *
- * Daniel.Veillard@imag.fr
+ * daniel@veillard.com
  */
 
-#include "xsltconfig.h"
+/*
+ * TODO: handle pathological cases like *[*[@a="b"]]
+ * TODO: detect [number] at compilation, optimize accordingly
+ */
+
+#define IN_LIBXSLT
+#include "libxslt.h"
 
 #include <string.h>
 
 #include <libxml/parserInternals.h>
 #include "xslt.h"
 #include "xsltInternals.h"
-
-/* #define DEBUG_PARSING */
-
-#define TODO                                                           \
-    xsltGenericError(xsltGenericErrorContext,                          \
-           "Unimplemented block at %s:%d\n",                           \
-            __FILE__, __LINE__);
-
-/*
- * To cleanup
- */
-xmlChar *xmlSplitQName2(const xmlChar *name, xmlChar **prefix);
-
-/*
- * There is no XSLT specific error reporting module yet
- */
-#define xsltGenericError xmlGenericError
-#define xsltGenericErrorContext xmlGenericErrorContext
+#include "xsltutils.h"
+#include "imports.h"
+#include "templates.h"
+#include "keys.h"
+#include "pattern.h"
+
+#ifdef WITH_XSLT_DEBUG
+#define WITH_XSLT_DEBUG_PATTERN
+#endif
 
 /*
  * Types are private:
@@ -56,9 +53,27 @@ typedef enum {
     XSLT_OP_KEY,
     XSLT_OP_NS,
     XSLT_OP_ALL,
+    XSLT_OP_PI,
+    XSLT_OP_COMMENT,
+    XSLT_OP_TEXT,
+    XSLT_OP_NODE,
     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;
@@ -66,26 +81,42 @@ struct _xsltStepOp {
     xsltOp op;
     xmlChar *value;
     xmlChar *value2;
+    xmlChar *value3;
+    xmlXPathCompExprPtr comp;
+    /*
+     * Optimisations for count
+     */
+    int        previousExtra;
+    int        indexExtra;
+    int        lenExtra;
 };
 
-typedef struct _xsltCompMatch xsltCompMatch;
-typedef xsltCompMatch *xsltCompMatchPtr;
 struct _xsltCompMatch {
     struct _xsltCompMatch *next; /* siblings in the name hash */
-    int priority;                /* the priority */
+    float priority;              /* the priority */
+    const xmlChar *pattern;       /* the pattern */
+    const xmlChar *mode;         /* the mode */
+    const xmlChar *modeURI;      /* the mode URI */
     xsltTemplatePtr template;    /* the associated template */
 
-    /* TODO fix the statically allocated size */
+    int direct;
+    /* TODO fix the statically allocated size steps[] */
     int nbStep;
     int maxStep;
-    xsltStepOp steps[20];        /* ops for computation */
+    xmlNsPtr *nsList;          /* the namespaces in scope */
+    int nsNr;                  /* the number of namespaces in scope */
+    xsltStepOp steps[40];        /* ops for computation */
 };
 
 typedef struct _xsltParserContext xsltParserContext;
 typedef xsltParserContext *xsltParserContextPtr;
 struct _xsltParserContext {
+    xsltStylesheetPtr style;           /* the stylesheet */
+    xsltTransformContextPtr ctxt;      /* the transformation or NULL */
     const xmlChar *cur;                        /* the current char being parsed */
     const xmlChar *base;               /* the full expression */
+    xmlDocPtr      doc;                        /* the source document */
+    xmlNodePtr    elem;                        /* the source element */
     int error;                         /* error code */
     xsltCompMatchPtr comp;             /* the result */
 };
@@ -103,18 +134,21 @@ struct _xsltParserContext {
  *
  * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
  */
-xsltCompMatchPtr
+static xsltCompMatchPtr
 xsltNewCompMatch(void) {
     xsltCompMatchPtr cur;
 
     cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
     if (cur == NULL) {
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
                "xsltNewCompMatch : malloc failed\n");
        return(NULL);
     }
     memset(cur, 0, sizeof(xsltCompMatch));
-    cur->maxStep = 20;
+    cur->maxStep = 40;
+    cur->nsNr = 0;
+    cur->nsList = NULL;
+    cur->direct = 0;
     return(cur);
 }
 
@@ -124,19 +158,27 @@ xsltNewCompMatch(void) {
  *
  * Free up the memory allocated by @comp
  */
-void
+static void
 xsltFreeCompMatch(xsltCompMatchPtr comp) {
     xsltStepOpPtr op;
     int i;
 
     if (comp == NULL)
        return;
+    if (comp->pattern != NULL)
+       xmlFree((xmlChar *)comp->pattern);
+    if (comp->nsList != NULL)
+       xmlFree(comp->nsList);
     for (i = 0;i < comp->nbStep;i++) {
        op = &comp->steps[i];
        if (op->value != NULL)
            xmlFree(op->value);
        if (op->value2 != NULL)
            xmlFree(op->value2);
+       if (op->value3 != NULL)
+           xmlFree(op->value3);
+       if (op->comp != NULL)
+           xmlXPathFreeCompExpr(op->comp);
     }
     memset(comp, -1, sizeof(xsltCompMatch));
     xmlFree(comp);
@@ -160,23 +202,49 @@ 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
  *
  * Create a new XSLT ParserContext
  *
  * Returns the newly allocated xsltParserContextPtr or NULL in case of error
  */
-xsltParserContextPtr
-xsltNewParserContext(void) {
+static xsltParserContextPtr
+xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
     xsltParserContextPtr cur;
 
     cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
     if (cur == NULL) {
-        xsltGenericError(xsltGenericErrorContext,
+       xsltTransformError(NULL, NULL, NULL,
                "xsltNewParserContext : malloc failed\n");
        return(NULL);
     }
     memset(cur, 0, sizeof(xsltParserContext));
+    cur->style = style;
+    cur->ctxt = ctxt;
     return(cur);
 }
 
@@ -186,7 +254,7 @@ xsltNewParserContext(void) {
  *
  * Free up the memory allocated by @ctxt
  */
-void
+static void
 xsltFreeParserContext(xsltParserContextPtr ctxt) {
     if (ctxt == NULL)
        return;
@@ -205,19 +273,86 @@ xsltFreeParserContext(xsltParserContextPtr ctxt) {
  *
  * Returns -1 in case of failure, 0 otherwise.
  */
-int
-xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
-                  xmlChar *value2) {
-    if (comp->nbStep >= 20) {
-        xsltGenericError(xsltGenericErrorContext,
-               "xsltCompMatchAddOp: overflow\n");
-        return(-1);
+static int
+xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
+                 xsltOp op, xmlChar * value, xmlChar * value2)
+{
+    if (comp->nbStep >= 40) {
+        xsltTransformError(NULL, NULL, NULL,
+                         "xsltCompMatchAdd: overflow\n");
+        return (-1);
     }
     comp->steps[comp->nbStep].op = op;
     comp->steps[comp->nbStep].value = value;
     comp->steps[comp->nbStep].value2 = value2;
+    if (ctxt->ctxt != NULL) {
+       comp->steps[comp->nbStep].previousExtra =
+           xsltAllocateExtraCtxt(ctxt->ctxt);
+       comp->steps[comp->nbStep].indexExtra =
+           xsltAllocateExtraCtxt(ctxt->ctxt);
+       comp->steps[comp->nbStep].lenExtra =
+           xsltAllocateExtraCtxt(ctxt->ctxt);
+    } else {
+       comp->steps[comp->nbStep].previousExtra =
+           xsltAllocateExtra(ctxt->style);
+       comp->steps[comp->nbStep].indexExtra =
+           xsltAllocateExtra(ctxt->style);
+       comp->steps[comp->nbStep].lenExtra =
+           xsltAllocateExtra(ctxt->style);
+    }
+    if (op == XSLT_OP_PREDICATE) {
+       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);
+    return (0);
+}
+
+/**
+ * xsltSwapTopCompMatch:
+ * @comp:  the compiled match expression
+ *
+ * reverse the two top steps.
+ */
+static void
+xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
+    int i;
+    int j = comp->nbStep - 1;
+
+    if (j > 0) {
+       register xmlChar *tmp;
+       register xsltOp op;
+       register xmlXPathCompExprPtr expr; 
+       i = j - 1;
+       tmp = comp->steps[i].value;
+       comp->steps[i].value = comp->steps[j].value;
+       comp->steps[j].value = tmp;
+       tmp = comp->steps[i].value2;
+       comp->steps[i].value2 = comp->steps[j].value2;
+       comp->steps[j].value2 = tmp;
+       op = comp->steps[i].op;
+       comp->steps[i].op = comp->steps[j].op;
+       comp->steps[j].op = op;
+       expr = comp->steps[i].comp;
+       comp->steps[i].comp = comp->steps[j].comp;
+       comp->steps[j].comp = expr;
+    }
 }
 
 /**
@@ -226,7 +361,7 @@ xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
  *
  * reverse all the stack of expressions
  */
-void
+static void
 xsltReverseCompMatch(xsltCompMatchPtr comp) {
     int i = 0;
     int j = comp->nbStep - 1;
@@ -234,6 +369,7 @@ xsltReverseCompMatch(xsltCompMatchPtr comp) {
     while (j > i) {
        register xmlChar *tmp;
        register xsltOp op;
+       register xmlXPathCompExprPtr expr; 
        tmp = comp->steps[i].value;
        comp->steps[i].value = comp->steps[j].value;
        comp->steps[j].value = tmp;
@@ -243,10 +379,34 @@ xsltReverseCompMatch(xsltCompMatchPtr comp) {
        op = comp->steps[i].op;
        comp->steps[i].op = comp->steps[j].op;
        comp->steps[j].op = op;
+       expr = comp->steps[i].comp;
+       comp->steps[i].comp = comp->steps[j].comp;
+       comp->steps[j].comp = expr;
        j--;
        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;
+       }
+    }
 }
 
 /************************************************************************
@@ -255,110 +415,752 @@ 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
  * @comp: the precompiled pattern
  * @node: a node
+ * @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
  */
-int
-xsltTestCompMatch(xsltCompMatchPtr comp, xmlNodePtr node) {
+static int
+xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
+                 xmlNodePtr node, const xmlChar *mode,
+                 const xmlChar *modeURI) {
     int i;
-    xsltStepOpPtr step;
+    xsltStepOpPtr step, sel = NULL;
+    xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
 
-    if ((comp == NULL) || (node == NULL)) {
-        xsltGenericError(xsltGenericErrorContext,
+    if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
+       xsltTransformError(ctxt, NULL, node,
                "xsltTestCompMatch: null arg\n");
         return(-1);
     }
-    for (i = 0;i < comp->nbStep;i++) {
+    if (mode != NULL) {
+       if (comp->mode == NULL)
+           return(0);
+       /*
+        * both mode strings must be interned on the stylesheet dictionary
+        */
+       if (comp->mode != mode)
+           return(0);
+    } else {
+       if (comp->mode != NULL)
+           return(0);
+    }
+    if (modeURI != NULL) {
+       if (comp->modeURI == NULL)
+           return(0);
+       /*
+        * both modeURI strings must be interned on the stylesheet dictionary
+        */
+       if (comp->modeURI != modeURI)
+           return(0);
+    } else {
+       if (comp->modeURI != NULL)
+           return(0);
+    }
+
+    i = 0;
+restart:
+    for (;i < comp->nbStep;i++) {
        step = &comp->steps[i];
+       if (step->op != XSLT_OP_PREDICATE)
+           sel = step;
        switch (step->op) {
             case XSLT_OP_END:
-               return(1);
+               goto found;
             case XSLT_OP_ROOT:
-               if ((node->type != XML_DOCUMENT_NODE) &&
-                   (node->type != XML_HTML_DOCUMENT_NODE))
-                   return(0);
-               continue;
+               if ((node->type == XML_DOCUMENT_NODE) ||
+#ifdef LIBXML_DOCB_ENABLED
+                   (node->type == XML_DOCB_DOCUMENT_NODE) ||
+#endif
+                   (node->type == XML_HTML_DOCUMENT_NODE))
+                   continue;
+               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);
-               /* TODO: Handle namespace ... */
+                   goto rollback;
+
+               /* Namespace test */
+               if (node->ns == NULL) {
+                   if (step->value2 != NULL)
+                       goto rollback;
+               } else if (node->ns->href != NULL) {
+                   if (step->value2 == NULL)
+                       goto rollback;
+                   if (!xmlStrEqual(step->value2, node->ns->href))
+                       goto rollback;
+               }
                continue;
-            case XSLT_OP_CHILD:
-               TODO /* Hummm !!! */
-               return(0);
+            case XSLT_OP_CHILD: {
+               xmlNodePtr lst;
+
+               if ((node->type != XML_ELEMENT_NODE) &&
+                   (node->type != XML_DOCUMENT_NODE) &&
+#ifdef LIBXML_DOCB_ENABLED
+                   (node->type != XML_DOCB_DOCUMENT_NODE) &&
+#endif
+                   (node->type != XML_HTML_DOCUMENT_NODE))
+                   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;
+                   }
+                   if (lst != NULL)
+                       continue;
+               }
+               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);
-               /* TODO: Handle namespace ... */
+                   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)
+                       goto rollback;
+               } else if (step->value2 != NULL) {
+                   if (!xmlStrEqual(step->value2, node->ns->href))
+                       goto rollback;
+               }
                continue;
             case XSLT_OP_PARENT:
+               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))
+                   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);
-               /* TODO: Handle namespace ... */
+                   goto rollback;
+               /* Namespace test */
+               if (node->ns == NULL) {
+                   if (step->value2 != NULL)
+                       goto rollback;
+               } else if (node->ns->href != NULL) {
+                   if (step->value2 == NULL)
+                       goto rollback;
+                   if (!xmlStrEqual(step->value2, node->ns->href))
+                       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))
+                   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)) {
-                       /* TODO: Handle namespace ... */
-                       break;
+                       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)
+                               break;
+                       } else if (node->ns->href != NULL) {
+                           if ((step->value2 != NULL) &&
+                               (xmlStrEqual(step->value2, node->ns->href)))
+                               break;
+                       }
                    }
+                   node = node->parent;
                }
                if (node == NULL)
-                   return(0);
+                   goto rollback;
+               xsltPatPushState(&states, i - 1, node);
                continue;
-            case XSLT_OP_ID:
-               TODO /* Handle IDs, might be done differently */
+            case XSLT_OP_ID: {
+               /* TODO Handle IDs decently, must be done differently */
+               xmlAttrPtr id;
+
+               if (node->type != XML_ELEMENT_NODE)
+                   goto rollback;
+
+               id = xmlGetID(node->doc, step->value);
+               if ((id == NULL) || (id->parent != node))
+                   goto rollback;
                break;
-            case XSLT_OP_KEY:
-               TODO /* Handle Keys, might be done differently */
+           }
+            case XSLT_OP_KEY: {
+               xmlNodeSetPtr list;
+               int indx;
+
+               list = xsltGetKey(ctxt, step->value,
+                                 step->value3, step->value2);
+               if (list == NULL)
+                   goto rollback;
+               for (indx = 0;indx < list->nodeNr;indx++)
+                   if (list->nodeTab[indx] == node)
+                       break;
+               if (indx >= list->nodeNr)
+                   goto rollback;
                break;
+           }
             case XSLT_OP_NS:
-               TODO /* Handle Namespace */
+               if (node->type != XML_ELEMENT_NODE)
+                   goto rollback;
+               if (node->ns == NULL) {
+                   if (step->value != NULL)
+                       goto rollback;
+               } else if (node->ns->href != NULL) {
+                   if (step->value == NULL)
+                       goto rollback;
+                   if (!xmlStrEqual(step->value, node->ns->href))
+                       goto rollback;
+               }
                break;
             case XSLT_OP_ALL:
-               TODO /* Handle Namespace */
+               if (node->type != XML_ELEMENT_NODE)
+                   goto rollback;
+               break;
+           case XSLT_OP_PREDICATE: {
+               xmlNodePtr oldNode;
+               xmlDocPtr doc;
+               int oldCS, oldCP;
+               int pos = 0, len = 0;
+               int isRVT;
+
+               /*
+                * 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->direct) {
+                   if (states.states != NULL) {
+                       /* Free the rollback states */
+                       xmlFree(states.states);
+                   }
+                   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.
+                */
+               oldCS = ctxt->xpathCtxt->contextSize;
+               oldCP = ctxt->xpathCtxt->proximityPosition;
+               if ((sel != NULL) &&
+                   (sel->op == XSLT_OP_ELEM) &&
+                   (sel->value != NULL) &&
+                   (node->type == XML_ELEMENT_NODE) &&
+                   (node->parent != NULL)) {
+                   xmlNodePtr previous;
+                   int ix, nocache = 0;
+
+                   previous = (xmlNodePtr)
+                       XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+                   ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
+                   if ((previous != NULL) &&
+                       (previous->parent == node->parent)) {
+                       /*
+                        * just walk back to adjust the index
+                        */
+                       int indx = 0;
+                       xmlNodePtr sibling = node;
+
+                       while (sibling != NULL) {
+                           if (sibling == previous)
+                               break;
+                           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(sel->value2,
+                                                 sibling->ns->href))))
+                                   indx++;
+                           }
+                           sibling = sibling->prev;
+                       }
+                       if (sibling == NULL) {
+                           /* hum going backward in document order ... */
+                           indx = 0;
+                           sibling = node;
+                           while (sibling != NULL) {
+                               if (sibling == previous)
+                                   break;
+                               if ((sel->value2 == NULL) ||
+                                   ((sibling->ns != NULL) &&
+                                    (xmlStrEqual(sel->value2,
+                                                 sibling->ns->href))))
+                                   indx--;
+                               sibling = sibling->next;
+                           }
+                       }
+                       if (sibling != NULL) {
+                           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 {
+                       /*
+                        * 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 ((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(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;
+                       /*
+                        * 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 ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
+                          (node->type == XML_ELEMENT_NODE)) {
+                   xmlNodePtr previous;
+                   int ix, nocache = 0;
+
+                   previous = (xmlNodePtr)
+                       XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
+                   ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
+                   if ((previous != NULL) &&
+                       (previous->parent == node->parent)) {
+                       /*
+                        * just walk back to adjust the index
+                        */
+                       int indx = 0;
+                       xmlNodePtr sibling = node;
+
+                       while (sibling != NULL) {
+                           if (sibling == previous)
+                               break;
+                           if (sibling->type == XML_ELEMENT_NODE)
+                               indx++;
+                           sibling = sibling->prev;
+                       }
+                       if (sibling == NULL) {
+                           /* hum going backward in document order ... */
+                           indx = 0;
+                           sibling = node;
+                           while (sibling != NULL) {
+                               if (sibling == previous)
+                                   break;
+                               if (sibling->type == XML_ELEMENT_NODE)
+                                   indx--;
+                               sibling = sibling->next;
+                           }
+                       }
+                       if (sibling != NULL) {
+                           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 {
+                       /*
+                        * recompute the index
+                        */
+                       xmlNodePtr siblings = node->parent->children;
+                       xmlNodePtr parent = node->parent;
+
+                       while (siblings != NULL) {
+                           if (siblings->type == XML_ELEMENT_NODE) {
+                               len++;
+                               if (siblings == node) {
+                                   pos = 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;
+                       /*
+                        * 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;
+               ctxt->node = node;
+
+               if (step->value == NULL)
+                   goto wrong_index;
+               if (step->comp == NULL)
+                   goto wrong_index;
+
+               if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList,
+                                           comp->nsNr))
+                   goto wrong_index;
+
+               if (pos != 0) {
+                   ctxt->xpathCtxt->contextSize = oldCS;
+                   ctxt->xpathCtxt->proximityPosition = oldCP;
+               }
+               ctxt->node = oldNode;
+               break;
+wrong_index:
+               if (pos != 0) {
+                   ctxt->xpathCtxt->contextSize = oldCS;
+                   ctxt->xpathCtxt->proximityPosition = oldCP;
+               }
+               ctxt->node = oldNode;
+               goto rollback;
+           }
+            case XSLT_OP_PI:
+               if (node->type != XML_PI_NODE)
+                   goto rollback;
+               if (step->value != NULL) {
+                   if (!xmlStrEqual(step->value, node->name))
+                       goto rollback;
+               }
+               break;
+            case XSLT_OP_COMMENT:
+               if (node->type != XML_COMMENT_NODE)
+                   goto rollback;
+               break;
+            case XSLT_OP_TEXT:
+               if ((node->type != XML_TEXT_NODE) &&
+                   (node->type != XML_CDATA_SECTION_NODE))
+                   goto rollback;
                break;
-           case XSLT_OP_PREDICATE:
-               TODO /* Handle Namespace */
+            case XSLT_OP_NODE:
+               switch (node->type) {
+                   case XML_ELEMENT_NODE:
+                   case XML_CDATA_SECTION_NODE:
+                   case XML_PI_NODE:
+                   case XML_COMMENT_NODE:
+                   case XML_TEXT_NODE:
+                       break;
+                   default:
+                       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;
+}
+
+/**
+ * xsltTestCompMatchList:
+ * @ctxt:  a XSLT process context
+ * @node: a node
+ * @comp: the precompiled pattern 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
+ */
+int
+xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
+                     xsltCompMatchPtr comp) {
+    int ret;
+
+    if ((ctxt == NULL) || (node == NULL))
+       return(-1);
+    while (comp != NULL) {
+       ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
+       if (ret == 1)
+           return(1);
+       comp = comp->next;
+    }
+    return(0);
 }
 
 /************************************************************************
@@ -373,23 +1175,90 @@ xsltTestCompMatch(xsltCompMatchPtr comp, 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)
 
 
 #define PUSH(op, val, val2)                                            \
-    if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
+    if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
+
+#define SWAP()                                                 \
+    xsltSwapTopCompMatch(ctxt->comp);
+
+#define XSLT_ERROR(X)                                                  \
+    { xsltError(ctxt, __FILE__, __LINE__, X);                  \
+      ctxt->error = (X); return; }
+
+#define XSLT_ERROR0(X)                                                 \
+    { xsltError(ctxt, __FILE__, __LINE__, X);                  \
+      ctxt->error = (X); return(0); }
 
 /**
- * xsltScanName:
+ * xsltScanLiteral:
  * @ctxt:  the XPath Parser context
  *
- * Trickery: parse an XML name but without consuming the input flow
- * Needed to avoid insanity in the parser state.
+ * Parse an XPath Litteral:
+ *
+ * [29] Literal ::= '"' [^"]* '"'
+ *                | "'" [^']* "'"
+ *
+ * Returns the Literal parsed or NULL
+ */
+
+static xmlChar *
+xsltScanLiteral(xsltParserContextPtr ctxt) {
+    const xmlChar *q, *cur;
+    xmlChar *ret = NULL;
+    int val, len;
+
+    SKIP_BLANKS;
+    if (CUR == '"') {
+        NEXT;
+       cur = q = CUR_PTR;
+       val = xmlStringCurrentChar(NULL, cur, &len);
+       while ((IS_CHAR(val)) && (val != '"')) {
+           cur += len;
+           val = xmlStringCurrentChar(NULL, cur, &len);
+       }
+       if (!IS_CHAR(val)) {
+           ctxt->error = 1;
+           return(NULL);
+       } else {
+           ret = xmlStrndup(q, cur - q);
+        }
+       cur += len;
+       CUR_PTR = cur;
+    } else if (CUR == '\'') {
+        NEXT;
+       cur = q = CUR_PTR;
+       val = xmlStringCurrentChar(NULL, cur, &len);
+       while ((IS_CHAR(val)) && (val != '\'')) {
+           cur += len;
+           val = xmlStringCurrentChar(NULL, cur, &len);
+       }
+       if (!IS_CHAR(val)) {
+           ctxt->error = 1;
+           return(NULL);
+       } else {
+           ret = xmlStrndup(q, cur - q);
+        }
+       cur += len;
+       CUR_PTR = cur;
+    } else {
+       /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
+       ctxt->error = 1;
+       return(NULL);
+    }
+    return(ret);
+}
+
+/**
+ * xsltScanName:
+ * @ctxt:  the XPath Parser context
  *
- * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
+ * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | 
  *                  CombiningChar | Extender
  *
  * [5] Name ::= (Letter | '_' | ':') (NameChar)*
@@ -399,104 +1268,463 @@ xsltTestCompMatch(xsltCompMatchPtr comp, xmlNodePtr node) {
  * Returns the Name parsed or NULL
  */
 
-xmlChar *
+static xmlChar *
 xsltScanName(xsltParserContextPtr ctxt) {
-    xmlChar buf[XML_MAX_NAMELEN];
-    int len = 0;
+    const xmlChar *q, *cur;
+    xmlChar *ret = NULL;
+    int val, len;
 
     SKIP_BLANKS;
-    if (!IS_LETTER(CUR) && (CUR != '_') &&
-        (CUR != ':')) {
+
+    cur = q = CUR_PTR;
+    val = xmlStringCurrentChar(NULL, cur, &len);
+    if (!IS_LETTER(val) && (val != '_') && (val != ':'))
        return(NULL);
-    }
 
-    while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
-           (NXT(len) == '.') || (NXT(len) == '-') ||
-          (NXT(len) == '_') || (NXT(len) == ':') || 
-          (IS_COMBINING(NXT(len))) ||
-          (IS_EXTENDER(NXT(len)))) {
-       buf[len] = NXT(len);
-       len++;
-       if (len >= XML_MAX_NAMELEN) {
-           xmlGenericError(xmlGenericErrorContext, 
-              "xmlScanName: reached XML_MAX_NAMELEN limit\n");
-           while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
-                  (NXT(len) == '.') || (NXT(len) == '-') ||
-                  (NXT(len) == '_') || (NXT(len) == ':') || 
-                  (IS_COMBINING(NXT(len))) ||
-                  (IS_EXTENDER(NXT(len))))
-                len++;
-           break;
-       }
+    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
+           (val == '.') || (val == '-') ||
+          (val == '_') || 
+          (IS_COMBINING(val)) ||
+          (IS_EXTENDER(val))) {
+       cur += len;
+       val = xmlStringCurrentChar(NULL, cur, &len);
     }
-    SKIP(len);
-    return(xmlStrndup(buf, len));
+    ret = xmlStrndup(q, cur - q);
+    CUR_PTR = cur;
+    return(ret);
 }
-/*
- * Compile the XSLT LocationPathPattern
- * [3] IdKeyPattern ::= 'id' '(' Literal ')'
- *                    | 'key' '(' Literal ',' Literal ')'
- */
 
 /**
- * xsltCompileStepPattern:
- * @comp:  the compilation context
- * @token:  a posible precompiled name
+ * xsltScanNCName:
+ * @ctxt:  the XPath Parser context
  *
- * Compile the XSLT StepPattern and generates a precompiled
- * form suitable for fast matching.
+ * Parses a non qualified name
  *
- * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* 
- * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
- *                                     | ('child' | 'attribute') '::'
- * from XPath
- * [7]  NodeTest ::= NameTest
- *                 | NodeType '(' ')'
- *                 | 'processing-instruction' '(' Literal ')'
- * [8] Predicate ::= '[' PredicateExpr ']'
- * [9] PredicateExpr ::= Expr
- * [13] AbbreviatedAxisSpecifier ::= '@'?
- * [37] NameTest ::= '*' | NCName ':' '*' | QName
+ * Returns the Name parsed or NULL
  */
 
-void
-xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
+static xmlChar *
+xsltScanNCName(xsltParserContextPtr ctxt) {
+    const xmlChar *q, *cur;
+    xmlChar *ret = NULL;
+    int val, len;
+
     SKIP_BLANKS;
-    if ((token == NULL) && (CUR == '@')) {
-       token = xsltScanName(ctxt);
-       if (token == NULL) {
-           xsltGenericError(xsltGenericErrorContext,
-                   "xsltCompilePattern : Name expected\n");
-           ctxt->error = 1;
-           return;
-       }
-    }
-    if (token == NULL)
-       token = xsltScanName(ctxt);
-    if (token == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
-               "xsltCompilePattern : Name expected\n");
-        ctxt->error = 1;
-       return;
+
+    cur = q = CUR_PTR;
+    val = xmlStringCurrentChar(NULL, cur, &len);
+    if (!IS_LETTER(val) && (val != '_'))
+       return(NULL);
+
+    while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
+           (val == '.') || (val == '-') ||
+          (val == '_') ||
+          (IS_COMBINING(val)) ||
+          (IS_EXTENDER(val))) {
+       cur += len;
+       val = xmlStringCurrentChar(NULL, cur, &len);
     }
-    SKIP_BLANKS;
+    ret = xmlStrndup(q, cur - q);
+    CUR_PTR = cur;
+    return(ret);
+}
+
+/**
+ * xsltScanQName:
+ * @ctxt:  the XPath Parser context
+ * @prefix:  the place to store the prefix
+ *
+ * Parse a qualified name
+ *
+ * Returns the Name parsed or NULL
+ */
+
+static xmlChar *
+xsltScanQName(xsltParserContextPtr ctxt, xmlChar **prefix) {
+    xmlChar *ret = NULL;
+
+    *prefix = NULL;
+    ret = xsltScanNCName(ctxt);
+    if (CUR == ':') {
+        *prefix = ret;
+       NEXT;
+       ret = xsltScanNCName(ctxt);
+    }
+    return(ret);
+}
+
+/*
+ * xsltCompileIdKeyPattern:
+ * @ctxt:  the compilation context
+ * @name:  a preparsed name
+ * @aid:  whether id/key are allowed there
+ *
+ * Compile the XSLT LocationIdKeyPattern
+ * [3] IdKeyPattern ::= 'id' '(' Literal ')'
+ *                    | 'key' '(' Literal ',' Literal ')'
+ *
+ * also handle NodeType and PI from:
+ *
+ * [7]  NodeTest ::= NameTest
+ *                 | NodeType '(' ')'
+ *                 | 'processing-instruction' '(' Literal ')'
+ */
+static void
+xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
+    xmlChar *lit = NULL;
+    xmlChar *lit2 = NULL;
+
+    if (CUR != '(') {
+       xsltTransformError(NULL, NULL, NULL,
+               "xsltCompileIdKeyPattern : ( expected\n");
+       ctxt->error = 1;
+       return;
+    }
+    if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
+       NEXT;
+       SKIP_BLANKS;
+        lit = xsltScanLiteral(ctxt);
+       if (ctxt->error)
+           return;
+       SKIP_BLANKS;
+       if (CUR != ')') {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileIdKeyPattern : ) expected\n");
+           ctxt->error = 1;
+           return;
+       }
+       NEXT;
+       PUSH(XSLT_OP_ID, lit, NULL);
+    } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
+       NEXT;
+       SKIP_BLANKS;
+        lit = xsltScanLiteral(ctxt);
+       if (ctxt->error)
+           return;
+       SKIP_BLANKS;
+       if (CUR != ',') {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileIdKeyPattern : , expected\n");
+           ctxt->error = 1;
+           return;
+       }
+       NEXT;
+       SKIP_BLANKS;
+        lit2 = xsltScanLiteral(ctxt);
+       if (ctxt->error)
+           return;
+       SKIP_BLANKS;
+       if (CUR != ')') {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileIdKeyPattern : ) expected\n");
+           ctxt->error = 1;
+           return;
+       }
+       NEXT;
+       /* TODO: support namespace in keys */
+       PUSH(XSLT_OP_KEY, lit, lit2);
+    } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
+       NEXT;
+       SKIP_BLANKS;
+       if (CUR != ')') {
+           lit = xsltScanLiteral(ctxt);
+           if (ctxt->error)
+               return;
+           SKIP_BLANKS;
+           if (CUR != ')') {
+               xsltTransformError(NULL, NULL, NULL,
+                       "xsltCompileIdKeyPattern : ) expected\n");
+               ctxt->error = 1;
+               return;
+           }
+       }
+       NEXT;
+       PUSH(XSLT_OP_PI, lit, NULL);
+    } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
+       NEXT;
+       SKIP_BLANKS;
+       if (CUR != ')') {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileIdKeyPattern : ) expected\n");
+           ctxt->error = 1;
+           return;
+       }
+       NEXT;
+       PUSH(XSLT_OP_TEXT, NULL, NULL);
+    } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
+       NEXT;
+       SKIP_BLANKS;
+       if (CUR != ')') {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileIdKeyPattern : ) expected\n");
+           ctxt->error = 1;
+           return;
+       }
+       NEXT;
+       PUSH(XSLT_OP_COMMENT, NULL, NULL);
+    } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
+       NEXT;
+       SKIP_BLANKS;
+       if (CUR != ')') {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileIdKeyPattern : ) expected\n");
+           ctxt->error = 1;
+           return;
+       }
+       NEXT;
+       PUSH(XSLT_OP_NODE, NULL, NULL);
+    } else if (aid) {
+       xsltTransformError(NULL, NULL, NULL,
+           "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
+       ctxt->error = 1;
+       return;
+    } else {
+       xsltTransformError(NULL, NULL, NULL,
+           "xsltCompileIdKeyPattern : node type\n");
+       ctxt->error = 1;
+       return;
+    }
+error:
+    if (name != NULL)
+       xmlFree(name);
+}
+
+/**
+ * xsltCompileStepPattern:
+ * @ctxt:  the compilation context
+ * @token:  a posible precompiled name
+ *
+ * Compile the XSLT StepPattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* 
+ * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
+ *                                     | ('child' | 'attribute') '::'
+ * from XPath
+ * [7]  NodeTest ::= NameTest
+ *                 | NodeType '(' ')'
+ *                 | 'processing-instruction' '(' Literal ')'
+ * [8] Predicate ::= '[' PredicateExpr ']'
+ * [9] PredicateExpr ::= Expr
+ * [13] AbbreviatedAxisSpecifier ::= '@'?
+ * [37] NameTest ::= '*' | NCName ':' '*' | QName
+ */
+
+static void
+xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
+    xmlChar *name = NULL;
+    const xmlChar *URI = NULL;
+    xmlChar *URL = NULL;
+    int level;
+
+    SKIP_BLANKS;
+    if ((token == NULL) && (CUR == '@')) {
+       xmlChar *prefix = NULL;
+
+       NEXT;
+       if (CUR == '*') {
+           NEXT;
+           PUSH(XSLT_OP_ATTR, NULL, NULL);
+           goto parse_predicate;
+       }
+       token = xsltScanQName(ctxt, &prefix);
+       if (prefix != NULL) {
+           xmlNsPtr ns;
+
+           ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
+           if (ns == NULL) {
+               xsltTransformError(NULL, NULL, NULL,
+               "xsltCompileStepPattern : no namespace bound to prefix %s\n",
+                                prefix);
+           } else {
+               URL = xmlStrdup(ns->href);
+           }
+           xmlFree(prefix);
+       }
+       if (token == NULL) {
+           if (CUR == '*') {
+               NEXT;
+               PUSH(XSLT_OP_ATTR, NULL, URL);
+               return;
+           }
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileStepPattern : Name expected\n");
+           ctxt->error = 1;
+           goto error;
+       }
+       PUSH(XSLT_OP_ATTR, token, URL);
+       goto parse_predicate;
+    }
+    if (token == NULL)
+       token = xsltScanName(ctxt);
+    if (token == NULL) {
+       if (CUR == '*') {
+           NEXT;
+           PUSH(XSLT_OP_ALL, token, NULL);
+           goto parse_predicate;
+       } else {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileStepPattern : Name expected\n");
+           ctxt->error = 1;
+           goto error;
+       }
+    }
+
+
+    SKIP_BLANKS;
     if (CUR == '(') {
-       TODO;
-       /* if (xmlStrEqual(token, "processing-instruction")) */
+       xsltCompileIdKeyPattern(ctxt, token, 0);
+       if (ctxt->error)
+           goto error;
     } else if (CUR == ':') {
-       TODO;
+       NEXT;
+       if (CUR != ':') {
+           xmlChar *prefix = token;
+           xmlNsPtr ns;
+
+           /*
+            * This is a namespace match
+            */
+           token = xsltScanName(ctxt);
+           ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
+           if (ns == NULL) {
+               xsltTransformError(NULL, NULL, NULL,
+           "xsltCompileStepPattern : no namespace bound to prefix %s\n",
+                                prefix);
+               ctxt->error = 1;
+               goto error;
+           } else {
+               URL = xmlStrdup(ns->href);
+           }
+           xmlFree(prefix);
+           if (token == NULL) {
+               if (CUR == '*') {
+                   NEXT;
+                   PUSH(XSLT_OP_NS, URL, NULL);
+               } else {
+                   xsltTransformError(NULL, NULL, NULL,
+                           "xsltCompileStepPattern : Name expected\n");
+                   ctxt->error = 1;
+                   goto error;
+               }
+           } else {
+               PUSH(XSLT_OP_ELEM, token, URL);
+           }
+       } else {
+           NEXT;
+           if (xmlStrEqual(token, (const xmlChar *) "child")) {
+               xmlFree(token);
+               token = xsltScanName(ctxt);
+               if (token == NULL) {
+                   if (CUR == '*') {
+                       NEXT;
+                       PUSH(XSLT_OP_ALL, token, NULL);
+                       goto parse_predicate;
+                   } else {
+                       xsltTransformError(NULL, NULL, NULL,
+                           "xsltCompileStepPattern : QName expected\n");
+                       ctxt->error = 1;
+                       goto error;
+                   }
+               }
+               URI = xsltGetQNameURI(ctxt->elem, &token);
+               if (token == NULL) {
+                   ctxt->error = 1;
+                   goto error;
+               } else {
+                   name = xmlStrdup(token);
+                   if (URI != NULL)
+                       URL = xmlStrdup(URI);
+               }
+               PUSH(XSLT_OP_CHILD, name, URL);
+           } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
+               xmlFree(token);
+               token = xsltScanName(ctxt);
+               if (token == NULL) {
+                   xsltTransformError(NULL, NULL, NULL,
+                           "xsltCompileStepPattern : QName expected\n");
+                   ctxt->error = 1;
+                   goto error;
+               }
+               URI = xsltGetQNameURI(ctxt->elem, &token);
+               if (token == NULL) {
+                   ctxt->error = 1;
+                   goto error;
+               } else {
+                   name = xmlStrdup(token);
+                   if (URI != NULL)
+                       URL = xmlStrdup(URI);
+               }
+               PUSH(XSLT_OP_ATTR, name, URL);
+           } else {
+               xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
+               ctxt->error = 1;
+               goto error;
+           }
+           xmlFree(token);
+       }
     } else if (CUR == '*') {
-       TODO;
+       NEXT;
+       PUSH(XSLT_OP_ALL, token, NULL);
     } else {
-       PUSH(XSLT_OP_ELEM, token, NULL);
+       URI = xsltGetQNameURI(ctxt->elem, &token);
+       if (token == NULL) {
+           ctxt->error = 1;
+           goto error;
+       }
+       if (URI != NULL)
+           URL = xmlStrdup(URI);
+       PUSH(XSLT_OP_ELEM, token, URL);
     }
+parse_predicate:
     SKIP_BLANKS;
+    level = 0;
     while (CUR == '[') {
-       TODO;
+       const xmlChar *q;
+       xmlChar *ret = NULL;
+
+       level++;
+       NEXT;
+       q = CUR_PTR;
+       while (CUR != 0) {
+           /* Skip over nested predicates */
+           if (CUR == '[')
+               level++;
+           else if (CUR == ']') {
+               level--;
+               if (level == 0)
+                   break;
+           } else if (CUR == '"') {
+               NEXT;
+               while ((CUR != 0) && (CUR != '"'))
+                   NEXT;
+           } else if (CUR == '\'') {
+               NEXT;
+               while ((CUR != 0) && (CUR != '\''))
+                   NEXT;
+           }
+           NEXT;
+       }
+       if (CUR == 0) {
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileStepPattern : ']' expected\n");
+           ctxt->error = 1;
+           return;
+        }
+       ret = xmlStrndup(q, CUR_PTR - q);
+       PUSH(XSLT_OP_PREDICATE, ret, NULL);
+       /* push the predicate lower than local test */
+       SWAP();
+       NEXT;
+       SKIP_BLANKS;
     }
     return;
 error:
-    ctxt->error = 1;
+    if (token != NULL)
+       xmlFree(token);
+    if (name != NULL)
+       xmlFree(name);
 }
 
 /**
@@ -511,7 +1739,7 @@ error:
  *                           | RelativePathPattern '/' StepPattern
  *                           | RelativePathPattern '//' StepPattern
  */
-void
+static void
 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
     xsltCompileStepPattern(ctxt, token);
     if (ctxt->error)
@@ -528,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 {
@@ -544,7 +1772,7 @@ error:
 
 /**
  * xsltCompileLocationPathPattern:
- * @comp:  the compilation context
+ * @ctxt:  the compilation context
  *
  * Compile the XSLT LocationPathPattern and generates a precompiled
  * form suitable for fast matching.
@@ -553,7 +1781,7 @@ error:
  *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
  *                           | '//'? RelativePathPattern
  */
-void
+static void
 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
     SKIP_BLANKS;
     if ((CUR == '/') && (NXT(1) == '/')) {
@@ -563,6 +1791,7 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
         */
        NEXT;
        NEXT;
+       ctxt->comp->priority = 0.5;     /* '//' means not 0 priority */
        xsltCompileRelativePathPattern(ctxt, NULL);
     } else if (CUR == '/') {
        /*
@@ -571,22 +1800,39 @@ 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);
        }
+    } else if (CUR == '*') {
+       xsltCompileRelativePathPattern(ctxt, NULL);
+    } else if (CUR == '@') {
+       xsltCompileRelativePathPattern(ctxt, NULL);
     } else {
        xmlChar *name;
        name = xsltScanName(ctxt);
        if (name == NULL) {
-           xsltGenericError(xsltGenericErrorContext,
-                   "xsltCompilePattern : Name expected\n");
+           xsltTransformError(NULL, NULL, NULL,
+                   "xsltCompileLocationPathPattern : Name expected\n");
            ctxt->error = 1;
            return;
        }
        SKIP_BLANKS;
-       if (CUR == '(') {
-           TODO
+       if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
+           xsltCompileIdKeyPattern(ctxt, name, 1);
+           if ((CUR == '/') && (NXT(1) == '/')) {
+               PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
+               NEXT;
+               NEXT;
+               SKIP_BLANKS;
+               xsltCompileRelativePathPattern(ctxt, NULL);
+           } else if (CUR == '/') {
+               PUSH(XSLT_OP_PARENT, NULL, NULL);
+               NEXT;
+               SKIP_BLANKS;
+               xsltCompileRelativePathPattern(ctxt, NULL);
+           }
+           return;
        }
        xsltCompileRelativePathPattern(ctxt, name);
     }
@@ -596,74 +1842,177 @@ error:
 
 /**
  * xsltCompilePattern:
- * @pattern an XSLT pattern
+ * @pattern: an XSLT pattern
+ * @doc:  the containing document
+ * @node:  the containing element
+ * @style:  the stylesheet
+ * @runtime:  the transformation context, if done at run-time
  *
- * Compile the XSLT pattern and generates a precompiled form suitable
+ * Compile the XSLT pattern and generates a list of precompiled form suitable
  * for fast matching.
- * Note that the splitting as union of patterns is expected to be handled
- * by the caller
  *
  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
  *
- * Returns the generated xsltCompMatchPtr or NULL in case of failure
+ * Returns the generated pattern list or NULL in case of failure
  */
 
 xsltCompMatchPtr
-xsltCompilePattern(const xmlChar *pattern) {
-    xsltParserContextPtr ctxt;
-    xsltCompMatchPtr ret;
-    const xmlChar *cur;
+xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
+                  xmlNodePtr node, xsltStylesheetPtr style,
+                  xsltTransformContextPtr runtime) {
+    xsltParserContextPtr ctxt = NULL;
+    xsltCompMatchPtr element, first = NULL, previous = NULL;
+    int current, start, end, level, j;
 
     if (pattern == NULL) {
-        xsltGenericError(xsltGenericErrorContext,
-               "xsltCompilePattern : NULL pattern\n");
+       xsltTransformError(NULL, NULL, node,
+                        "xsltCompilePattern : NULL pattern\n");
        return(NULL);
     }
 
-#ifdef DEBUG_PARSING
-    xsltGenericError(xsltGenericErrorContext,
-           "xsltCompilePattern : parsing '%s'\n", pattern);
-#endif
-
-    cur = pattern;
-    while (IS_BLANK(*cur)) cur++;
-    if (*cur == 0) {
-        xsltGenericError(xsltGenericErrorContext,
-               "xsltCompilePattern : NULL pattern\n");
-       return(NULL);
-    }
-    ctxt = xsltNewParserContext();
+    ctxt = xsltNewParserContext(style, runtime);
     if (ctxt == NULL)
        return(NULL);
-    ret = xsltNewCompMatch();
-    if (ret == NULL) {
-       xsltFreeParserContext(ctxt);
-       return(NULL);
-    }
+    ctxt->doc = doc;
+    ctxt->elem = node;
+    current = end = 0;
+    while (pattern[current] != 0) {
+       start = current;
+       while (IS_BLANK_CH(pattern[current]))
+           current++;
+       end = current;
+       level = 0;
+       while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
+           if (pattern[end] == '[')
+               level++;
+           else if (pattern[end] == ']')
+               level--;
+           else if (pattern[end] == '\'') {
+               end++;
+               while ((pattern[end] != 0) && (pattern[end] != '\''))
+                   end++;
+           } else if (pattern[end] == '"') {
+               end++;
+               while ((pattern[end] != 0) && (pattern[end] != '"'))
+                   end++;
+           }
+           end++;
+       }
+       if (current == end) {
+           xsltTransformError(NULL, NULL, node,
+                            "xsltCompilePattern : NULL pattern\n");
+           goto error;
+       }
+       element = xsltNewCompMatch();
+       if (element == NULL) {
+           goto error;
+       }
+       if (first == NULL)
+           first = element;
+       else if (previous != NULL)
+           previous->next = element;
+       previous = element;
+
+       ctxt->comp = element;
+       ctxt->base = xmlStrndup(&pattern[start], end - start);
+       if (ctxt->base == NULL)
+           goto error;
+       ctxt->cur = &(ctxt->base)[current - start];
+       element->pattern = ctxt->base;
+       element->nsList = xmlGetNsList(doc, node);
+       j = 0;
+       if (element->nsList != NULL) {
+           while (element->nsList[j] != NULL)
+               j++;
+       }
+       element->nsNr = j;
 
-    ctxt->comp = ret;
-    ctxt->base = pattern;
-    ctxt->cur = cur;
-    xsltCompileLocationPathPattern(ctxt);
-    if (ctxt->error)
-       goto error;
 
-    /*
-     * Reverse for faster interpretation.
-     */
-    xsltReverseCompMatch(ret);
+#ifdef WITH_XSLT_DEBUG_PATTERN
+       xsltGenericDebug(xsltGenericDebugContext,
+                        "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) {
+           xsltTransformError(NULL, style, node,
+                            "xsltCompilePattern : failed to compile '%s'\n",
+                            element->pattern);
+           if (style != NULL) style->errors++;
+           goto error;
+       }
+
+       /*
+        * Reverse for faster interpretation.
+        */
+       xsltReverseCompMatch(element);
+
+       /*
+        * Set-up the priority
+        */
+       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,
+                    "xsltCompilePattern : parsed %s, default priority %f\n",
+                        element->pattern, element->priority);
+#endif
+       if (pattern[end] == '|')
+           end++;
+       current = end;
+    }
+    if (end == 0) {
+       xsltTransformError(NULL, style, node,
+                        "xsltCompilePattern : NULL pattern\n");
+       if (style != NULL) style->errors++;
+       goto error;
+    }
 
     xsltFreeParserContext(ctxt);
-    return(ret);
+    return(first);
 
 error:
-    xsltFreeParserContext(ctxt);
-    xsltFreeCompMatch(ret);
+    if (ctxt != NULL)
+       xsltFreeParserContext(ctxt);
+    if (first != NULL)
+       xsltFreeCompMatchList(first);
     return(NULL);
-
 }
 
-
 /************************************************************************
  *                                                                     *
  *                     Module interfaces                               *
@@ -674,185 +2023,389 @@ error:
  * xsltAddTemplate:
  * @style: an XSLT stylesheet
  * @cur: an XSLT template
+ * @mode:  the mode name or NULL
+ * @modeURI:  the mode URI or NULL
  *
  * Register the XSLT pattern associated to @cur
  *
  * Returns -1 in case of error, 0 otherwise
  */
 int
-xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur) {
-    xsltCompMatchPtr pat, list;
-    const xmlChar *name;
+xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
+               const xmlChar *mode, const xmlChar *modeURI) {
+    xsltCompMatchPtr pat, list, *top = NULL, next;
+    const xmlChar *name = NULL;
+    float priority;              /* the priority */
 
-    /*
-     * get a compiled form of the pattern
-     */
-    /* TODO : handle | in patterns as multple pat !!! */
-    pat = xsltCompilePattern(cur->match);
-    if (pat == NULL)
+    if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
        return(-1);
-    pat->template = cur;
-    if (cur->priority != XSLT_PAT_NO_PRIORITY)
-       pat->priority = cur->priority;
 
-    /*
-     * insert it in the hash table list corresponding to its lookup name
-     */
-    switch (pat->steps[0].op) {
-        case XSLT_OP_ELEM:
-        case XSLT_OP_CHILD:
+    priority = cur->priority;
+    pat = xsltCompilePattern(cur->match, style->doc, cur->elem, style, NULL);
+    while (pat) {
+       next = pat->next;
+       pat->next = NULL;
+       name = NULL;
+       
+       pat->template = cur;
+       if (mode != NULL)
+           pat->mode = xmlDictLookup(style->dict, mode, -1);
+       if (modeURI != NULL)
+           pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
+       if (priority != XSLT_PAT_NO_PRIORITY)
+           pat->priority = priority;
+
+       /*
+        * insert it in the hash table list corresponding to its lookup name
+        */
+       switch (pat->steps[0].op) {
         case XSLT_OP_ATTR:
+           if (pat->steps[0].value != NULL)
+               name = pat->steps[0].value;
+           else
+               top = (xsltCompMatchPtr *) &(style->attrMatch);
+           break;
+        case XSLT_OP_CHILD:
         case XSLT_OP_PARENT:
         case XSLT_OP_ANCESTOR:
-        case XSLT_OP_ID:
+           top = (xsltCompMatchPtr *) &(style->elemMatch);
+           break;
+        case XSLT_OP_ROOT:
+           top = (xsltCompMatchPtr *) &(style->rootMatch);
+           break;
         case XSLT_OP_KEY:
+           top = (xsltCompMatchPtr *) &(style->keyMatch);
+           break;
+        case XSLT_OP_ID:
+           /* TODO optimize ID !!! */
         case XSLT_OP_NS:
-             name = pat->steps[0].value;
-            break;
-        case XSLT_OP_ROOT:
-             name = (const xmlChar *) "/";
-            break;
         case XSLT_OP_ALL:
-             name = (const xmlChar *) "*";
-            break;
+           top = (xsltCompMatchPtr *) &(style->elemMatch);
+           break;
         case XSLT_OP_END:
        case XSLT_OP_PREDICATE:
-           xsltGenericError(xsltGenericErrorContext,
-                   "xsltAddTemplate: invalid compiled pattern\n");
+           xsltTransformError(NULL, style, NULL,
+                            "xsltAddTemplate: invalid compiled pattern\n");
            xsltFreeCompMatch(pat);
            return(-1);
-    }
-    if (name == NULL) {
-       xsltGenericError(xsltGenericErrorContext,
-               "xsltAddTemplate: invalid compiled pattern\n");
-       xsltFreeCompMatch(pat);
-       return(-1);
-    }
-    if (style->templatesHash == NULL) {
-       style->templatesHash = xmlHashCreate(0);
-        if (style->templatesHash == NULL) {
-           xsltFreeCompMatch(pat);
-           return(-1);
-       }
-#ifdef DEBUG_PARSING
-       xsltGenericError(xsltGenericErrorContext,
-               "xsltAddTemplate: created template hash\n");
-#endif
-       xmlHashAddEntry(style->templatesHash, name, pat);
-#ifdef DEBUG_PARSING
-       xsltGenericError(xsltGenericErrorContext,
-               "xsltAddTemplate: added new hash %s\n", name);
-#endif
-    } else {
-       list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
-       if (list == NULL) {
-           xmlHashAddEntry(style->templatesHash, name, pat);
-#ifdef DEBUG_PARSING
-           xsltGenericError(xsltGenericErrorContext,
-                   "xsltAddTemplate: added new hash %s\n", name);
-#endif
-       } else {
            /*
-            * Note '<=' since one must choose among the matching template
-            * rules that are left, the one that occurs last in the stylesheet
+            * TODO: some flags at the top level about type based patterns
+            *       would be faster than inclusion in the hash table.
             */
-           if (list->priority <= pat->priority) {
+       case XSLT_OP_PI:
+           if (pat->steps[0].value != NULL)
+               name = pat->steps[0].value;
+           else
+               top = (xsltCompMatchPtr *) &(style->piMatch);
+           break;
+       case XSLT_OP_COMMENT:
+           top = (xsltCompMatchPtr *) &(style->commentMatch);
+           break;
+       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) {
+           if (style->templatesHash == NULL) {
+               style->templatesHash = xmlHashCreate(1024);
+               if (style->templatesHash == NULL) {
+                   xsltFreeCompMatch(pat);
+                   return(-1);
+               }
+               xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
+           } else {
+               list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
+                                                        name, mode, modeURI);
+               if (list == NULL) {
+                   xmlHashAddEntry3(style->templatesHash, name,
+                                    mode, modeURI, pat);
+               } else {
+                   /*
+                    * Note '<=' since one must choose among the matching
+                    * template rules that are left, the one that occurs
+                    * last in the stylesheet
+                    */
+                   if (list->priority <= pat->priority) {
+                       pat->next = list;
+                       xmlHashUpdateEntry3(style->templatesHash, name,
+                                           mode, modeURI, pat, NULL);
+                   } else {
+                       while (list->next != NULL) {
+                           if (list->next->priority <= pat->priority)
+                               break;
+                           list = list->next;
+                       }
+                       pat->next = list->next;
+                       list->next = pat;
+                   }
+               }
+           }
+       } else if (top != NULL) {
+           list = *top;
+           if (list == NULL) {
+               *top = pat;
+               pat->next = NULL;
+           } else if (list->priority <= pat->priority) {
                pat->next = list;
-               xmlHashUpdateEntry(style->templatesHash, name, pat, NULL);
-#ifdef DEBUG_PARSING
-               xsltGenericError(xsltGenericErrorContext,
-                       "xsltAddTemplate: added head hash for %s\n", name);
-#endif
+               *top = pat;
            } else {
                while (list->next != NULL) {
                    if (list->next->priority <= pat->priority)
                        break;
+                   list = list->next;
                }
                pat->next = list->next;
                list->next = pat;
            }
+       } else {
+           xsltTransformError(NULL, style, NULL,
+                            "xsltAddTemplate: invalid compiled pattern\n");
+           xsltFreeCompMatch(pat);
+           return(-1);
        }
+#ifdef WITH_XSLT_DEBUG_PATTERN
+       if (mode)
+           xsltGenericDebug(xsltGenericDebugContext,
+                        "added pattern : '%s' mode '%s' priority %f\n",
+                            pat->pattern, pat->mode, pat->priority);
+       else
+           xsltGenericDebug(xsltGenericDebugContext,
+                        "added pattern : '%s' priority %f\n",
+                            pat->pattern, pat->priority);
+#endif
+
+       pat = next;
     }
     return(0);
 }
 
 /**
  * xsltGetTemplate:
- * @style: an XSLT stylesheet
- * @node: an XML Node
+ * @ctxt:  a XSLT process context
+ * @node:  the node being processed
+ * @style:  the current style
  *
- * Finds the template applying to this node
+ * Finds the template applying to this node, if @style is non-NULL
+ * it means one needs to look for the next imported template in scope.
  *
  * Returns the xsltTemplatePtr or NULL if not found
  */
 xsltTemplatePtr
-xsltGetTemplate(xsltStylesheetPtr style, xmlNodePtr node) {
-    const xmlChar *name;
-    xsltCompMatchPtr list;
-
-    if ((style == NULL) || (node == NULL))
+xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
+               xsltStylesheetPtr style) {
+    xsltStylesheetPtr curstyle;
+    xsltTemplatePtr ret = NULL;
+    const xmlChar *name = NULL;
+    xsltCompMatchPtr list = NULL;
+    float priority;
+    int keyed = 0;
+
+    if ((ctxt == NULL) || (node == NULL))
        return(NULL);
 
-    /* TODO : handle IDs/keys here ! */
-    if (style->templatesHash == NULL)
-       return(NULL);
+    if (style == NULL) {
+       curstyle = ctxt->style;
+    } else {
+       curstyle = xsltNextImport(style);
+    }
 
-    /*
-     * Use a name as selector
-     */
-    switch (node->type) {
-        case XML_ELEMENT_NODE:
-        case XML_ATTRIBUTE_NODE:
-        case XML_PI_NODE:
-           name = node->name;
-           break;
-        case XML_DOCUMENT_NODE:
-        case XML_HTML_DOCUMENT_NODE:
-           name = (const xmlChar *)"/";
-           break;
-        case XML_TEXT_NODE:
-        case XML_CDATA_SECTION_NODE:
-        case XML_ENTITY_REF_NODE:
-        case XML_ENTITY_NODE:
-        case XML_COMMENT_NODE:
-        case XML_DOCUMENT_TYPE_NODE:
-        case XML_DOCUMENT_FRAG_NODE:
-        case XML_NOTATION_NODE:
-        case XML_DTD_NODE:
-        case XML_ELEMENT_DECL:
-        case XML_ATTRIBUTE_DECL:
-        case XML_ENTITY_DECL:
-        case XML_NAMESPACE_DECL:
-        case XML_XINCLUDE_START:
-        case XML_XINCLUDE_END:
-           return(NULL);
-       default:
-           return(NULL);
+    while ((curstyle != NULL) && (curstyle != style)) {
+       priority = XSLT_PAT_NO_PRIORITY;
+       /* TODO : handle IDs/keys here ! */
+       if (curstyle->templatesHash != NULL) {
+           /*
+            * Use the top name as selector
+            */
+           switch (node->type) {
+               case XML_ELEMENT_NODE:
+                   if (node->name[0] == ' ')
+                       break;
+               case XML_ATTRIBUTE_NODE:
+               case XML_PI_NODE:
+                   name = node->name;
+                   break;
+               case XML_DOCUMENT_NODE:
+               case XML_HTML_DOCUMENT_NODE:
+               case XML_TEXT_NODE:
+               case XML_CDATA_SECTION_NODE:
+               case XML_COMMENT_NODE:
+               case XML_ENTITY_REF_NODE:
+               case XML_ENTITY_NODE:
+               case XML_DOCUMENT_TYPE_NODE:
+               case XML_DOCUMENT_FRAG_NODE:
+               case XML_NOTATION_NODE:
+               case XML_DTD_NODE:
+               case XML_ELEMENT_DECL:
+               case XML_ATTRIBUTE_DECL:
+               case XML_ENTITY_DECL:
+               case XML_NAMESPACE_DECL:
+               case XML_XINCLUDE_START:
+               case XML_XINCLUDE_END:
+                   break;
+               default:
+                   return(NULL);
 
-    }
-    if (name == NULL)
-       return(NULL);
+           }
+       }
+       if (name != NULL) {
+           /*
+            * find the list of applicable expressions based on the name
+            */
+           list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
+                                            name, ctxt->mode, ctxt->modeURI);
+       } else
+           list = NULL;
+       while (list != NULL) {
+           if (xsltTestCompMatch(ctxt, list, node,
+                                 ctxt->mode, ctxt->modeURI)) {
+               ret = list->template;
+               priority = list->priority;
+               break;
+           }
+           list = list->next;
+       }
+       list = NULL;
 
-    /*
-     * find the list of appliable expressions based on the name
-     */
-    list = (xsltCompMatchPtr) xmlHashLookup(style->templatesHash, name);
-    if (list == NULL) {
-#ifdef DEBUG_MATCHING
-       xsltGenericError(xsltGenericErrorContext,
-               "xsltGetTemplate: empty set for %s\n", name);
-#endif
-       return(NULL);
-    }
-    while (list != NULL) {
-       if (xsltTestCompMatch(list, node))
-           return(list->template);
-       list = list->next;
-    }
+       /*
+        * find alternate generic matches
+        */
+       switch (node->type) {
+           case XML_ELEMENT_NODE:
+               if (node->name[0] == ' ')
+                   list = curstyle->rootMatch;
+               else
+                   list = curstyle->elemMatch;
+               if (node->psvi != NULL) keyed = 1;
+               break;
+           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: {
+               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:
+           case XML_DOCUMENT_TYPE_NODE:
+           case XML_DOCUMENT_FRAG_NODE:
+           case XML_NOTATION_NODE:
+           case XML_DTD_NODE:
+           case XML_ELEMENT_DECL:
+           case XML_ATTRIBUTE_DECL:
+           case XML_ENTITY_DECL:
+           case XML_NAMESPACE_DECL:
+           case XML_XINCLUDE_START:
+           case XML_XINCLUDE_END:
+               break;
+           default:
+               break;
+       }
+       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;
+       }
+       /*
+        * 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_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))) {
+               if (xsltTestCompMatch(ctxt, list, node,
+                                     ctxt->mode, ctxt->modeURI)) {
+                   ret = list->template;
+                   priority = list->priority;
+                   break;
+               }
+               list = list->next;
+           }
+       }
+
+       if (keyed) {
+           list = curstyle->keyMatch;
+           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;
+           }
+       }
+       if (ret != NULL)
+           return(ret);
+
+       /*
+        * Cycle on next curstylesheet import.
+        */
+       curstyle = xsltNextImport(curstyle);
+    }
     return(NULL);
 }
 
+/**
+ * xsltCleanupTemplates:
+ * @style: an XSLT stylesheet
+ *
+ * Cleanup the state of the templates used by the stylesheet and
+ * the ones it imports.
+ */
+void
+xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
+}
 
 /**
  * xsltFreeTemplateHashes:
@@ -865,5 +2418,21 @@ xsltFreeTemplateHashes(xsltStylesheetPtr style) {
     if (style->templatesHash != NULL)
        xmlHashFree((xmlHashTablePtr) style->templatesHash,
                    (xmlHashDeallocator) xsltFreeCompMatchList);
+    if (style->rootMatch != NULL)
+        xsltFreeCompMatchList(style->rootMatch);
+    if (style->keyMatch != NULL)
+        xsltFreeCompMatchList(style->keyMatch);
+    if (style->elemMatch != NULL)
+        xsltFreeCompMatchList(style->elemMatch);
+    if (style->attrMatch != NULL)
+        xsltFreeCompMatchList(style->attrMatch);
+    if (style->parentMatch != NULL)
+        xsltFreeCompMatchList(style->parentMatch);
+    if (style->textMatch != NULL)
+        xsltFreeCompMatchList(style->textMatch);
+    if (style->piMatch != NULL)
+        xsltFreeCompMatchList(style->piMatch);
+    if (style->commentMatch != NULL)
+        xsltFreeCompMatchList(style->commentMatch);
 }