adding the pattern node selection code. Inheried in part from libxslt but

* pattern.c include/libxml/pattern.h: adding the pattern node
  selection code. Inheried in part from libxslt but smaller.
* Makefile.am configure.in include/libxml/xmlversion.h.in:
  integrated the pattern module, made it a configure time option
* xmllint.c: added --pattern to test when doing --stream
Daniel
diff --git a/pattern.c b/pattern.c
new file mode 100644
index 0000000..e0f1566
--- /dev/null
+++ b/pattern.c
@@ -0,0 +1,918 @@
+/*
+ * pattern.c: Implemetation of selectors for nodes
+ *
+ * Reference:
+ *   http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
+ *   to some extent 
+ *   http://www.w3.org/TR/1999/REC-xml-19991116
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#define IN_LIBXML
+#include "libxml.h"
+
+#include <string.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+#include <libxml/hash.h>
+#include <libxml/dict.h>
+#include <libxml/xmlerror.h>
+#include <libxml/parserInternals.h>
+#include <libxml/pattern.h>
+
+#ifdef LIBXML_PATTERN_ENABLED
+
+#define ERROR(a, b, c, d)
+#define ERROR5(a, b, c, d, e)
+
+/*
+ * Types are private:
+ */
+
+typedef enum {
+    XML_OP_END=0,
+    XML_OP_ROOT,
+    XML_OP_ELEM,
+    XML_OP_CHILD,
+    XML_OP_ATTR,
+    XML_OP_PARENT,
+    XML_OP_ANCESTOR,
+    XML_OP_NS,
+    XML_OP_ALL
+} xmlPatOp;
+
+
+typedef struct _xmlStepOp xmlStepOp;
+typedef xmlStepOp *xmlStepOpPtr;
+struct _xmlStepOp {
+    xmlPatOp op;
+    const xmlChar *value;
+    const xmlChar *value2;
+};
+
+struct _xmlPattern {
+    void *data;    		/* the associated template */
+    struct _xmlPattern *next; /* siblings */
+    const xmlChar *pattern;	/* the pattern */
+
+    /* TODO fix the statically allocated size steps[] */
+    int nbStep;
+    int maxStep;
+    xmlStepOp steps[10];        /* ops for computation */
+};
+
+typedef struct _xmlPatParserContext xmlPatParserContext;
+typedef xmlPatParserContext *xmlPatParserContextPtr;
+struct _xmlPatParserContext {
+    const xmlChar *cur;			/* the current char being parsed */
+    const xmlChar *base;		/* the full expression */
+    int	           error;		/* error code */
+    xmlDictPtr     dict;		/* the dictionnary if any */
+    xmlPatternPtr  comp;		/* the result */
+    xmlNodePtr     elem;		/* the current node if any */    
+};
+
+/************************************************************************
+ * 									*
+ * 			Type functions 					*
+ * 									*
+ ************************************************************************/
+
+/**
+ * xmlNewPattern:
+ *
+ * Create a new XSLT Pattern
+ *
+ * Returns the newly allocated xmlPatternPtr or NULL in case of error
+ */
+static xmlPatternPtr
+xmlNewPattern(void) {
+    xmlPatternPtr cur;
+
+    cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
+    if (cur == NULL) {
+	ERROR(NULL, NULL, NULL,
+		"xmlNewPattern : malloc failed\n");
+	return(NULL);
+    }
+    memset(cur, 0, sizeof(xmlPattern));
+    cur->maxStep = 10;
+    return(cur);
+}
+
+/**
+ * xmlFreePattern:
+ * @comp:  an XSLT comp
+ *
+ * Free up the memory allocated by @comp
+ */
+void
+xmlFreePattern(xmlPatternPtr comp) {
+    xmlStepOpPtr op;
+    int i;
+
+    if (comp == NULL)
+	return;
+    if (comp->pattern != NULL)
+	xmlFree((xmlChar *)comp->pattern);
+    for (i = 0;i < comp->nbStep;i++) {
+	op = &comp->steps[i];
+	if (op->value != NULL)
+	    xmlFree((xmlChar *) op->value);
+	if (op->value2 != NULL)
+	    xmlFree((xmlChar *) op->value2);
+    }
+    memset(comp, -1, sizeof(xmlPattern));
+    xmlFree(comp);
+}
+
+/**
+ * xmlFreePatternList:
+ * @comp:  an XSLT comp list
+ *
+ * Free up the memory allocated by all the elements of @comp
+ */
+void
+xmlFreePatternList(xmlPatternPtr comp) {
+    xmlPatternPtr cur;
+
+    while (comp != NULL) {
+	cur = comp;
+	comp = comp->next;
+	xmlFreePattern(cur);
+    }
+}
+
+/**
+ * xmlNewPatParserContext:
+ * @pattern:  the pattern context
+ * @ctxt:  the transformation context, if done at run-time
+ *
+ * Create a new XML pattern parser context
+ *
+ * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
+ */
+static xmlPatParserContextPtr
+xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict) {
+    xmlPatParserContextPtr cur;
+
+    if (pattern == NULL)
+        return(NULL);
+
+    cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
+    if (cur == NULL) {
+	ERROR(NULL, NULL, NULL,
+		"xmlNewPatParserContext : malloc failed\n");
+	return(NULL);
+    }
+    memset(cur, 0, sizeof(xmlPatParserContext));
+    cur->dict = dict;
+    cur->cur = pattern;
+    cur->base = pattern;
+    return(cur);
+}
+
+/**
+ * xmlFreePatParserContext:
+ * @ctxt:  an XSLT parser context
+ *
+ * Free up the memory allocated by @ctxt
+ */
+static void
+xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
+    if (ctxt == NULL)
+	return;
+    memset(ctxt, -1, sizeof(xmlPatParserContext));
+    xmlFree(ctxt);
+}
+
+/**
+ * xmlPatternAdd:
+ * @comp:  the compiled match expression
+ * @op:  an op
+ * @value:  the first value
+ * @value2:  the second value
+ *
+ * Add an step to an XSLT Compiled Match
+ *
+ * Returns -1 in case of failure, 0 otherwise.
+ */
+static int
+xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
+                xmlPatternPtr comp,
+                xmlPatOp op, xmlChar * value, xmlChar * value2)
+{
+    if (comp->nbStep >= 10) {
+        ERROR(ctxt, NULL, NULL,
+                         "xmlPatternAdd: overflow\n");
+        return (-1);
+    }
+    comp->steps[comp->nbStep].op = op;
+    comp->steps[comp->nbStep].value = value;
+    comp->steps[comp->nbStep].value2 = value2;
+    comp->nbStep++;
+    return (0);
+}
+
+#if 0
+/**
+ * xsltSwapTopPattern:
+ * @comp:  the compiled match expression
+ *
+ * reverse the two top steps.
+ */
+static void
+xsltSwapTopPattern(xmlPatternPtr comp) {
+    int i;
+    int j = comp->nbStep - 1;
+
+    if (j > 0) {
+	register const xmlChar *tmp;
+	register xmlPatOp op;
+	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;
+    }
+}
+#endif
+
+/**
+ * xmlReversePattern:
+ * @comp:  the compiled match expression
+ *
+ * reverse all the stack of expressions
+ */
+static void
+xmlReversePattern(xmlPatternPtr comp) {
+    int i = 0;
+    int j = comp->nbStep - 1;
+
+    while (j > i) {
+	register const xmlChar *tmp;
+	register xmlPatOp op;
+	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;
+	j--;
+	i++;
+    }
+    comp->steps[comp->nbStep++].op = XML_OP_END;
+}
+
+/************************************************************************
+ * 									*
+ * 		The interpreter for the precompiled patterns		*
+ * 									*
+ ************************************************************************/
+
+/**
+ * xmlPatMatch:
+ * @comp: the precompiled pattern
+ * @node: a node
+ *
+ * Test wether the node matches the pattern
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+static int
+xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
+    int i;
+    xmlStepOpPtr step;
+
+    if ((comp == NULL) || (node == NULL)) return(-1);
+    for (i = 0;i < comp->nbStep;i++) {
+	step = &comp->steps[i];
+	switch (step->op) {
+            case XML_OP_END:
+		return(1);
+            case XML_OP_ROOT:
+		if ((node->type == XML_DOCUMENT_NODE) ||
+#ifdef LIBXML_DOCB_ENABLED
+		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
+#endif
+		    (node->type == XML_HTML_DOCUMENT_NODE))
+		    continue;
+		return(0);
+            case XML_OP_ELEM:
+		if (node->type != XML_ELEMENT_NODE)
+		    return(0);
+		if (step->value == NULL)
+		    continue;
+		if (step->value[0] != node->name[0])
+		    return(0);
+		if (!xmlStrEqual(step->value, node->name))
+		    return(0);
+
+		/* Namespace test */
+		if (node->ns == NULL) {
+		    if (step->value2 != NULL)
+			return(0);
+		} else if (node->ns->href != NULL) {
+		    if (step->value2 == NULL)
+			return(0);
+		    if (!xmlStrEqual(step->value2, node->ns->href))
+			return(0);
+		}
+		continue;
+            case XML_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))
+		    return(0);
+
+		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;
+		}
+		return(0);
+	    }
+            case XML_OP_ATTR:
+		if (node->type != XML_ATTRIBUTE_NODE)
+		    return(0);
+		if (step->value != NULL) {
+		    if (step->value[0] != node->name[0])
+			return(0);
+		    if (!xmlStrEqual(step->value, node->name))
+			return(0);
+		}
+		/* Namespace test */
+		if (node->ns == NULL) {
+		    if (step->value2 != NULL)
+			return(0);
+		} else if (step->value2 != NULL) {
+		    if (!xmlStrEqual(step->value2, node->ns->href))
+			return(0);
+		}
+		continue;
+            case XML_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))
+		    return(0);
+		node = node->parent;
+		if (node == NULL)
+		    return(0);
+		if (step->value == NULL)
+		    continue;
+		if (step->value[0] != node->name[0])
+		    return(0);
+		if (!xmlStrEqual(step->value, node->name))
+		    return(0);
+		/* Namespace test */
+		if (node->ns == NULL) {
+		    if (step->value2 != NULL)
+			return(0);
+		} else if (node->ns->href != NULL) {
+		    if (step->value2 == NULL)
+			return(0);
+		    if (!xmlStrEqual(step->value2, node->ns->href))
+			return(0);
+		}
+		continue;
+            case XML_OP_ANCESTOR:
+		/* TODO: implement coalescing of ANCESTOR/NODE ops */
+		if (step->value == NULL) {
+		    i++;
+		    step = &comp->steps[i];
+		    if (step->op == XML_OP_ROOT)
+			return(1);
+		    if (step->op != XML_OP_ELEM)
+			return(0);
+		    if (step->value == NULL)
+			return(-1);
+		}
+		if (node == NULL)
+		    return(0);
+		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);
+		node = node->parent;
+		while (node != NULL) {
+		    if (node == NULL)
+			return(0);
+		    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);
+		continue;
+            case XML_OP_NS:
+		if (node->type != XML_ELEMENT_NODE)
+		    return(0);
+		if (node->ns == NULL) {
+		    if (step->value != NULL)
+			return(0);
+		} else if (node->ns->href != NULL) {
+		    if (step->value == NULL)
+			return(0);
+		    if (!xmlStrEqual(step->value, node->ns->href))
+			return(0);
+		}
+		break;
+            case XML_OP_ALL:
+		if (node->type != XML_ELEMENT_NODE)
+		    return(0);
+		break;
+	}
+    }
+    return(1);
+}
+
+/************************************************************************
+ *									*
+ *			Dedicated parser for templates			*
+ *									*
+ ************************************************************************/
+
+#define TODO 								\
+    xmlGenericError(xmlGenericErrorContext,				\
+	    "Unimplemented block at %s:%d\n",				\
+            __FILE__, __LINE__);
+#define CUR (*ctxt->cur)
+#define SKIP(val) ctxt->cur += (val)
+#define NXT(val) ctxt->cur[(val)]
+#define CUR_PTR ctxt->cur
+
+#define SKIP_BLANKS 							\
+    while (IS_BLANK(CUR)) NEXT
+
+#define CURRENT (*ctxt->cur)
+#define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
+
+
+#define PUSH(op, val, val2) 						\
+    if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
+
+#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); }
+
+#if 0
+/**
+ * xmlPatScanLiteral:
+ * @ctxt:  the XPath Parser context
+ *
+ * Parse an XPath Litteral:
+ *
+ * [29] Literal ::= '"' [^"]* '"'
+ *                | "'" [^']* "'"
+ *
+ * Returns the Literal parsed or NULL
+ */
+
+static xmlChar *
+xmlPatScanLiteral(xmlPatParserContextPtr 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);
+}
+#endif
+
+/**
+ * xmlPatScanName:
+ * @ctxt:  the XPath Parser context
+ *
+ * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | 
+ *                  CombiningChar | Extender
+ *
+ * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+ *
+ * [6] Names ::= Name (S Name)*
+ *
+ * Returns the Name parsed or NULL
+ */
+
+static xmlChar *
+xmlPatScanName(xmlPatParserContextPtr ctxt) {
+    const xmlChar *q, *cur;
+    xmlChar *ret = NULL;
+    int val, len;
+
+    SKIP_BLANKS;
+
+    cur = q = CUR_PTR;
+    val = xmlStringCurrentChar(NULL, cur, &len);
+    if (!IS_LETTER(val) && (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);
+    }
+    ret = xmlStrndup(q, cur - q);
+    CUR_PTR = cur;
+    return(ret);
+}
+
+/**
+ * xmlPatScanNCName:
+ * @ctxt:  the XPath Parser context
+ *
+ * Parses a non qualified name
+ *
+ * Returns the Name parsed or NULL
+ */
+
+static xmlChar *
+xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
+    const xmlChar *q, *cur;
+    xmlChar *ret = NULL;
+    int val, len;
+
+    SKIP_BLANKS;
+
+    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);
+    }
+    ret = xmlStrndup(q, cur - q);
+    CUR_PTR = cur;
+    return(ret);
+}
+
+#if 0
+/**
+ * xmlPatScanQName:
+ * @ctxt:  the XPath Parser context
+ * @prefix:  the place to store the prefix
+ *
+ * Parse a qualified name
+ *
+ * Returns the Name parsed or NULL
+ */
+
+static xmlChar *
+xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
+    xmlChar *ret = NULL;
+
+    *prefix = NULL;
+    ret = xmlPatScanNCName(ctxt);
+    if (CUR == ':') {
+        *prefix = ret;
+	NEXT;
+	ret = xmlPatScanNCName(ctxt);
+    }
+    return(ret);
+}
+#endif
+
+/**
+ * xmlCompileStepPattern:
+ * @ctxt:  the compilation context
+ *
+ * Compile the Step Pattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [3]    Step    ::=    '.' | NameTest
+ * [4]    NameTest    ::=    QName | '*' | NCName ':' '*' 
+ */
+
+static void
+xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
+    xmlChar *token = NULL;
+    xmlChar *name = NULL;
+    const xmlChar *URI = NULL;
+    xmlChar *URL = NULL;
+
+    SKIP_BLANKS;
+    if (CUR == '.') {
+	NEXT;
+	PUSH(XML_OP_ELEM, NULL, NULL);
+	return;
+    }
+    name = xmlPatScanNCName(ctxt);
+    if (name == NULL) {
+	if (CUR == '*') {
+	    NEXT;
+	    PUSH(XML_OP_ALL, NULL, NULL);
+	    return;
+	} else {
+	    ERROR(NULL, NULL, NULL,
+		    "xmlCompileStepPattern : Name expected\n");
+	    ctxt->error = 1;
+	    return;
+	}
+    }
+    SKIP_BLANKS;
+    if (CUR == ':') {
+	NEXT;
+	if (CUR != ':') {
+	    xmlChar *prefix = name;
+	    xmlNsPtr ns;
+
+	    /*
+	     * This is a namespace match
+	     */
+	    token = xmlPatScanName(ctxt);
+	    ns = xmlSearchNs(NULL, ctxt->elem, prefix);
+	    if (ns == NULL) {
+		ERROR5(NULL, NULL, NULL,
+	    "xmlCompileStepPattern : 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(XML_OP_NS, URL, NULL);
+		} else {
+		    ERROR(NULL, NULL, NULL,
+			    "xmlCompileStepPattern : Name expected\n");
+		    ctxt->error = 1;
+		    goto error;
+		}
+	    } else {
+		PUSH(XML_OP_ELEM, token, URL);
+	    }
+	} else {
+	    NEXT;
+	    if (xmlStrEqual(token, (const xmlChar *) "child")) {
+		xmlFree(token);
+		token = xmlPatScanName(ctxt);
+		if (token == NULL) {
+	            if (CUR == '*') {
+            	        NEXT;
+	                PUSH(XML_OP_ALL, token, NULL);
+	                return;
+	            } else {
+		        ERROR(NULL, NULL, NULL,
+			    "xmlCompileStepPattern : QName expected\n");
+		        ctxt->error = 1;
+		        goto error;
+		    }
+		}
+		TODO
+		/* URI = xsltGetQNameURI(ctxt->elem, &token); */
+		if (token == NULL) {
+		    ctxt->error = 1;
+		    goto error;
+		} else {
+		    name = xmlStrdup(token);
+		    if (URI != NULL)
+			URL = xmlStrdup(URI);
+		}
+		PUSH(XML_OP_CHILD, name, URL);
+	    } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
+		xmlFree(token);
+		token = xmlPatScanName(ctxt);
+		if (token == NULL) {
+		    ERROR(NULL, NULL, NULL,
+			    "xmlCompileStepPattern : QName expected\n");
+		    ctxt->error = 1;
+		    goto error;
+		}
+		TODO
+		/* URI = xsltGetQNameURI(ctxt->elem, &token); */
+		if (token == NULL) {
+		    ctxt->error = 1;
+		    goto error;
+		} else {
+		    name = xmlStrdup(token);
+		    if (URI != NULL)
+			URL = xmlStrdup(URI);
+		}
+		PUSH(XML_OP_ATTR, name, URL);
+	    } else {
+		ERROR(NULL, NULL, NULL,
+		    "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
+		ctxt->error = 1;
+		goto error;
+	    }
+	    xmlFree(token);
+	}
+    } else if (CUR == '*') {
+	NEXT;
+	PUSH(XML_OP_ALL, token, NULL);
+    } else {
+	if (name == NULL) {
+	    ctxt->error = 1;
+	    goto error;
+	}
+	PUSH(XML_OP_ELEM, name, NULL);
+    }
+    return;
+error:
+    if (token != NULL)
+	xmlFree(token);
+    if (name != NULL)
+	xmlFree(name);
+}
+
+/**
+ * xmlCompilePathPattern:
+ * @ctxt:  the compilation context
+ *
+ * Compile the Path Pattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [5]    Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest ) 
+ */
+static void
+xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
+    SKIP_BLANKS;
+    if ((CUR == '/') && (NXT(1) == '/')) {
+	/*
+	 * since we reverse the query
+	 * a leading // can be safely ignored
+	 */
+	NEXT;
+	NEXT;
+    } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
+	/*
+	 * a leading .// can be safely ignored
+	 */
+	NEXT;
+	NEXT;
+	NEXT;
+    }
+    if (CUR == '@') {
+	TODO
+    } else {
+	xmlCompileStepPattern(ctxt);
+	SKIP_BLANKS;
+	while (CUR == '/') {
+	    if ((CUR == '/') && (NXT(1) == '/')) {
+	        PUSH(XML_OP_ANCESTOR, NULL, NULL);
+		NEXT;
+		NEXT;
+		SKIP_BLANKS;
+		xmlCompileStepPattern(ctxt);
+	    } else {
+	        PUSH(XML_OP_PARENT, NULL, NULL);
+		NEXT;
+		SKIP_BLANKS;
+		if ((CUR != 0) || (CUR == '|')) {
+		    xmlCompileStepPattern(ctxt);
+		}
+	    }
+	}
+    }
+error:
+    return;
+}
+/************************************************************************
+ *									*
+ *			The public interfaces				*
+ *									*
+ ************************************************************************/
+
+/**
+ * xmlPatterncompile:
+ * @pattern: the pattern to compile
+ * @dict: an optional dictionnary for interned strings
+ * @flags: compilation flags, undefined yet
+ *
+ * Compile a pattern
+ *
+ * Returns the compiled for of the pattern or NULL in case of error
+ */
+xmlPatternPtr
+xmlPatterncompile(const xmlChar *pattern, xmlDict *dictionnary, int flags) {
+    xmlPatternPtr ret = NULL;
+    xmlPatParserContextPtr ctxt = NULL;
+
+    ctxt = xmlNewPatParserContext(pattern, dictionnary);
+    if (ctxt == NULL) goto error;
+    ret = xmlNewPattern();
+    if (ret == NULL) goto error;
+    ctxt->comp = ret;
+
+    xmlCompilePathPattern(ctxt);
+    xmlFreePatParserContext(ctxt);
+
+    xmlReversePattern(ret);
+    return(ret);
+error:
+    if (ctxt != NULL) xmlFreePatParserContext(ctxt);
+    if (ret != NULL) xmlFreePattern(ret);
+    return(NULL);
+}
+
+/**
+ * xmlPatternMatch:
+ * @comp: the precompiled pattern
+ * @node: a node
+ *
+ * Test wether the node matches the pattern
+ *
+ * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
+ */
+int
+xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
+{
+    if ((comp == NULL) || (node == NULL))
+        return(-1);
+    return(xmlPatMatch(comp, node));
+}
+
+#endif /* LIBXML_PATTERN_ENABLED */