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/ChangeLog b/ChangeLog
index a995fef..4607de1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Tue Dec  2 23:29:56 CET 2003 Daniel Veillard <daniel@veillard.com>
+
+	* 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
+
 Tue Dec  2 11:25:25 CET 2003 Daniel Veillard <daniel@veillard.com>
 
 	* xmlreader.c: fixed a problem in xmlreader validation when
diff --git a/Makefile.am b/Makefile.am
index 11745e5..f10ed55 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,7 +27,7 @@
 		catalog.c globals.c threads.c c14n.c \
 		xmlregexp.c xmlschemas.c xmlschemastypes.c xmlunicode.c \
 		triostr.c trio.c xmlreader.c relaxng.c dict.c SAX2.c \
-		xmlwriter.c legacy.c chvalid.c
+		xmlwriter.c legacy.c chvalid.c pattern.c
 else
 libxml2_la_SOURCES = SAX.c entities.c encoding.c error.c parserInternals.c  \
 		parser.c tree.c hash.c list.c xmlIO.c xmlmemory.c uri.c  \
@@ -36,7 +36,7 @@
 		catalog.c globals.c threads.c c14n.c \
 		xmlregexp.c xmlschemas.c xmlschemastypes.c xmlunicode.c \
 		xmlreader.c relaxng.c dict.c SAX2.c \
-		xmlwriter.c legacy.c chvalid.c
+		xmlwriter.c legacy.c chvalid.c pattern.c
 endif
 
 DEPS = $(top_builddir)/libxml2.la
diff --git a/configure.in b/configure.in
index 141ee4c..edfd418 100644
--- a/configure.in
+++ b/configure.in
@@ -622,6 +622,23 @@
 AC_SUBST(WITH_READER)
 AC_SUBST(READER_TEST)
 
+AC_ARG_WITH(pattern,
+[  --with-pattern           add the xmlPattern selection interface (on)])
+if test "$with_minimum" = "yes" -a "$with_pattern" = ""
+then
+    with_pattern=no
+fi
+if test "$with_pattern" = "no" ; then
+    echo Disabling the xmlPattern parsing interface
+    WITH_PATTERN=0
+    PATTERN_TEST=
+else    
+    WITH_PATTERN=1
+    PATTERN_TEST=Patterntests
+fi
+AC_SUBST(WITH_PATTERN)
+AC_SUBST(PATTERN_TEST)
+
 AC_ARG_WITH(writer,
 [  --with-writer           add the xmlWriter saving interface (on)])
 if test "$with_minimum" = "yes" -a "$with_writer" = ""
diff --git a/include/libxml/pattern.h b/include/libxml/pattern.h
new file mode 100644
index 0000000..6aa7de1
--- /dev/null
+++ b/include/libxml/pattern.h
@@ -0,0 +1,47 @@
+/*
+ * Summary: pattern expression handling
+ * Description: allows to compile and test pattern expressions for nodes
+ *              either in a tree or based on a parser state.
+ *
+ * Copy: See Copyright for the status of this software.
+ *
+ * Author: Daniel Veillard
+ */
+
+#ifndef __XML_PATTERN_H__
+#define __XML_PATTERN_H__
+
+#include <libxml/xmlversion.h>
+#include <libxml/tree.h>
+#include <libxml/dict.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * xmlPattern:
+ *
+ * A compiled (XPath based) pattern to select nodes
+ */
+typedef struct _xmlPattern xmlPattern;
+typedef xmlPattern *xmlPatternPtr;
+
+XMLPUBFUN void XMLCALL
+			xmlFreePattern		(xmlPatternPtr comp);
+
+XMLPUBFUN void XMLCALL
+			xmlFreePatternList	(xmlPatternPtr comp);
+
+XMLPUBFUN xmlPatternPtr XMLCALL
+			xmlPatterncompile	(const xmlChar *pattern,
+						 xmlDict *dictionnary,
+						 int flags);
+XMLPUBFUN int XMLCALL
+			xmlPatternMatch		(xmlPatternPtr comp,
+						 xmlNodePtr node);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __XML_PATTERN_H__ */
diff --git a/include/libxml/xmlversion.h.in b/include/libxml/xmlversion.h.in
index dd78b88..fcef1cc 100644
--- a/include/libxml/xmlversion.h.in
+++ b/include/libxml/xmlversion.h.in
@@ -121,6 +121,15 @@
 #endif
 
 /**
+ * LIBXML_PATTERN_ENABLED:
+ *
+ * Whether the xmlPattern node selection interface is configured in
+ */
+#if @WITH_PATTERN@
+#define LIBXML_PATTERN_ENABLED
+#endif
+
+/**
  * LIBXML_WRITER_ENABLED:
  *
  * Whether the xmlWriter saving interface is configured in
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 */
diff --git a/xmllint.c b/xmllint.c
index 92b1c3e..8cc8e58 100644
--- a/xmllint.c
+++ b/xmllint.c
@@ -89,6 +89,9 @@
 #include <libxml/relaxng.h>
 #include <libxml/xmlschemas.h>
 #endif
+#ifdef LIBXML_PATTERN_ENABLED
+#include <libxml/pattern.h>
+#endif
 
 #ifndef XML_XML_DEFAULT_CATALOG
 #define XML_XML_DEFAULT_CATALOG "file:///etc/xml/catalog"
@@ -160,6 +163,10 @@
 #ifdef LIBXML_SAX1_ENABLED
 static int sax1 = 0;
 #endif /* LIBXML_SAX1_ENABLED */
+#ifdef LIBXML_PATTERN_ENABLED
+static const char *pattern = NULL;
+static xmlPatternPtr patternc = NULL;
+#endif
 static int options = 0;
 
 /*
@@ -620,6 +627,13 @@
     else {
 	printf(" %s\n", value);
     }
+#ifdef LIBXML_PATTERN_ENABLED
+    if (patternc) {
+        if (xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader)) == 1) {
+	    printf("Node matches pattern %s\n", pattern);
+	}
+    }
+#endif
 }
 
 static void streamFile(char *filename) {
@@ -680,7 +694,11 @@
 	}
 	ret = xmlTextReaderRead(reader);
 	while (ret == 1) {
-	    if (debug)
+	    if ((debug)
+#ifdef LIBXML_PATTERN_ENABLED
+	        || (patternc)
+#endif
+	       )
 		processNode(reader);
 	    ret = xmlTextReaderRead(reader);
 	}
@@ -748,7 +766,11 @@
 	}
 	ret = xmlTextReaderRead(reader);
 	while (ret == 1) {
-	    if (debug)
+	    if ((debug)
+#ifdef LIBXML_PATTERN_ENABLED
+	        || (patternc)
+#endif
+	       )
 		processNode(reader);
 	    ret = xmlTextReaderRead(reader);
 	}
@@ -1451,7 +1473,7 @@
 #ifdef LIBXML_CATALOG_ENABLED
     printf("\t--catalogs : use SGML catalogs from $SGML_CATALOG_FILES\n");
     printf("\t             otherwise XML Catalogs starting from \n");
-    printf("\t         " XML_XML_DEFAULT_CATALOG " are activated by default\n");
+    printf("\t         %s are activated by default\n", XML_XML_DEFAULT_CATALOG);
     printf("\t--nocatalogs: deactivate all catalogs\n");
 #endif
     printf("\t--auto : generate a small doc on the fly\n");
@@ -1464,6 +1486,9 @@
     printf("\t--stream : use the streaming interface to process very large files\n");
     printf("\t--walker : create a reader and walk though the resulting doc\n");
 #endif /* LIBXML_READER_ENABLED */
+#ifdef LIBXML_PATTERN_ENABLED
+    printf("\t--pattern pattern_value : test the pattern support\n");
+#endif
     printf("\t--chkregister : verify the node registration code\n");
 #ifdef LIBXML_SCHEMAS_ENABLED
     printf("\t--relaxng schema : do RelaxNG validation against the schema\n");
@@ -1732,6 +1757,12 @@
         } else if ((!strcmp(argv[i], "-nonet")) ||
                    (!strcmp(argv[i], "--nonet"))) {
 	    options |= XML_PARSE_NONET;
+#ifdef LIBXML_PATTERN_ENABLED
+        } else if ((!strcmp(argv[i], "-pattern")) ||
+                   (!strcmp(argv[i], "--pattern"))) {
+	    i++;
+	    pattern = argv[i];
+#endif
 	} else {
 	    fprintf(stderr, "Unknown option %s\n", argv[i]);
 	    usage(argv[0]);
@@ -1847,7 +1878,18 @@
 	    endTimer("Compiling the schemas");
 	}
     }
-#endif
+#endif /* LIBXML_SCHEMAS_ENABLED */
+#ifdef LIBXML_PATTERN_ENABLED
+    if (pattern != NULL) {
+        patternc = xmlPatterncompile((const xmlChar *) pattern, NULL, 0);
+	if (patternc == NULL) {
+	    xmlGenericError(xmlGenericErrorContext,
+		    "Pattern %s failed to compile\n", pattern);
+            progresult = 7;
+	    pattern = NULL;
+	}
+    }
+#endif /* LIBXML_PATTERN_ENABLED */
     for (i = 1; i < argc ; i++) {
 	if ((!strcmp(argv[i], "-encode")) ||
 	         (!strcmp(argv[i], "--encode"))) {
@@ -1881,6 +1923,13 @@
 	    i++;
 	    continue;
         }
+#ifdef LIBXML_PATTERN_ENABLED
+        if ((!strcmp(argv[i], "-pattern")) ||
+	    (!strcmp(argv[i], "--pattern"))) {
+	    i++;
+	    continue;
+	}
+#endif
 	if ((timing) && (repeat))
 	    startTimer();
 	/* Remember file names.  "-" means stdin.  <sven@zen.org> */
@@ -1932,6 +1981,10 @@
 	xmlSchemaFree(wxschemas);
     xmlRelaxNGCleanupTypes();
 #endif
+#ifdef LIBXML_PATTERN_ENABLED
+    if (patternc != NULL)
+        xmlFreePattern(patternc);
+#endif
     xmlCleanupParser();
     xmlMemoryDump();