added a more convenient extension API for value and context managing Now


	* include/libxml/xpath{,Internals}.h xpath.c: added a more
	  convenient extension API for value and context managing
	  Now handles external objects through xmlXPathPopExternal,
	  xmlXPathWrapExternal and xmlXPathReturnExternal.
	  Added functions for sets operations (intersection, etc.)
diff --git a/ChangeLog b/ChangeLog
index b416401..23cf3c2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Mon Jul 16 06:32:44 CEST 2001 Thomas Broyer <tbroyer@ltgt.net>
+
+	* include/libxml/xpath{,Internals}.h xpath.c: added a more
+	  convenient extension API for value and context managing
+	  Now handles external objects through xmlXPathPopExternal,
+	  xmlXPathWrapExternal and xmlXPathReturnExternal.
+	  Added functions for sets operations (intersection, etc.)
+
 Mon Jul 16 20:05:27 CEST 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
 
 	* include/libxml/parserInternals.h include/libxml/HTMLparser.h
@@ -5,7 +13,7 @@
 	  HTMLparser.c: cleanup of global variables, marking some
 	  const or private.
 
-Sun Jul 16 00:17:15 CEST 2001 Thomas Broyer <tbroyer@ltgt.net>
+Mon Jul 16 00:17:15 CEST 2001 Thomas Broyer <tbroyer@ltgt.net>
 
 	* include/libxml/xpath.h: exported xmlXPath{NAN,PINF,NINF}
 	  fixed xmlXPathNodeSetItem when passing index=0
diff --git a/include/libxml/xpath.h b/include/libxml/xpath.h
index 87a6118..8724f1c 100644
--- a/include/libxml/xpath.h
+++ b/include/libxml/xpath.h
@@ -288,12 +288,40 @@
 LIBXML_DLL_IMPORT extern double xmlXPathNINF;
 
 /* These macros may later turn into functions */
+/**
+ * xmlXPathNodeSetGetLength:
+ * @ns:  a node-set
+ *
+ * Implement a functionnality similar to the DOM NodeList.length
+ *
+ * Returns the number of nodes in the node-set.
+ */
 #define xmlXPathNodeSetGetLength(ns) ((ns) ? (ns)->nodeNr : 0)
+/**
+ * xmlXPathNodeSetItem:
+ * @ns:  a node-set
+ * @index:  index of a node in the set
+ *
+ * Implements a functionnality similar to the DOM NodeList.item()
+ *
+ * Returns the xmlNodePtr at the given @index in @ns or NULL if
+ *         @index is out of range (0 to length-1)
+ */
 #define xmlXPathNodeSetItem(ns, index)				\
 		((((ns) != NULL) && 				\
 		  ((index) >= 0) && ((index) < (ns)->nodeNr)) ?	\
 		 (ns)->nodeTab[(index)]				\
 		 : NULL)
+/**
+ * xmlXPathNodeSetIsEmpty:
+ * @ns: a node-set
+ *
+ * Checks whether @ns is empty or not
+ *
+ * Returns %TRUE if @ns is an empty node-set
+ */
+#define xmlXPathNodeSetIsEmpty(ns)                                      \
+    (((ns) == NULL) || ((ns)->nodeNr == 0) || ((ns)->nodeTab == NULL))
 
 
 void		   xmlXPathFreeObject		(xmlXPathObjectPtr obj);
diff --git a/include/libxml/xpathInternals.h b/include/libxml/xpathInternals.h
index 40c3bce..05f843c 100644
--- a/include/libxml/xpathInternals.h
+++ b/include/libxml/xpathInternals.h
@@ -27,6 +27,173 @@
  *									*
  ************************************************************************/
 
+    /**
+     * Many of these macros may later turn into functions. They
+     * shouldn't be used in #ifdef's preprocessor instructions.
+     */
+/**
+ * xmlXPathSetError:
+ * @ctxt:  an XPath parser context
+ * @err:  an xmlXPathError code
+ *
+ * Raises an error.
+ */
+#define xmlXPathSetError(ctxt, err)					\
+    { xmlXPatherror((ctxt), __FILE__, __LINE__, (err));			\
+      (ctxt)->error = (err); }
+/**
+ * xmlXPathSetArityError:
+ * @ctxt:  an XPath parser context
+ *
+ * Raises an XPATH_INVALID_ARITY error
+ */
+#define xmlXPathSetArityError(ctxt)					\
+    xmlXPathSetError((ctxt), XPATH_INVALID_ARITY)
+/**
+ * xmlXPathSetTypeError:
+ * @ctxt:  an XPath parser context
+ *
+ * Raises an XPATH_INVALID_TYPE error
+ */
+#define xmlXPathSetTypeError(ctxt)					\
+    xmlXPathSetError((ctxt), XPATH_INVALID_TYPE)
+/**
+ * xmlXPathGetError:
+ * @ctxt:  an XPath parser context
+ *
+ * Returns the context error
+ */
+#define xmlXPathGetError(ctxt)	  ((ctxt)->error)
+/**
+ * xmlXPathCheckError:
+ * @ctxt:  an XPath parser context
+ *
+ * Returns true if an error has been raised, false otherwise.
+ */
+#define xmlXPathCheckError(ctxt)  ((ctxt)->error != XPATH_EXPRESSION_OK)
+
+/**
+ * xmlXPathGetDocument:
+ * @ctxt:  an XPath parser context
+ *
+ * Returns the context document
+ */
+#define xmlXPathGetDocument(ctxt)	((ctxt)->context->doc)
+/**
+ * xmlXPathGetContextNode:
+ * @ctxt: an XPath parser context
+ *
+ * Returns the context node
+ */
+#define xmlXPathGetContextNode(ctxt)	((ctxt)->context->node)
+
+int		xmlXPathPopBoolean	(xmlXPathParserContextPtr ctxt);
+double		xmlXPathPopNumber	(xmlXPathParserContextPtr ctxt);
+xmlChar *	xmlXPathPopString	(xmlXPathParserContextPtr ctxt);
+xmlNodeSetPtr	xmlXPathPopNodeSet	(xmlXPathParserContextPtr ctxt);
+void *		xmlXPathPopExternal	(xmlXPathParserContextPtr ctxt);
+
+/**
+ * xmlXPathReturnBoolean:
+ * @ctxt:  an XPath parser context
+ * @val:  a boolean
+ *
+ * Pushes the boolean @val on the context stack
+ */
+#define xmlXPathReturnBoolean(ctxt, val)				\
+    valuePush((ctxt), xmlXPathNewBoolean(val))
+/**
+ * xmlXPathReturnTrue:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes true on the context stack
+ */
+#define xmlXPathReturnTrue(ctxt)   xmlXPathReturnBoolean((ctxt), 1)
+/**
+ * xmlXPathReturnFalse:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes false on the context stack
+ */
+#define xmlXPathReturnFalse(ctxt)  xmlXPathReturnBoolean((ctxt), 0)
+/**
+ * xmlXPathReturnNumber:
+ * @ctxt:  an XPath parser context
+ * @val:  a double
+ *
+ * Pushes the double @val on the context stack
+ */
+#define xmlXPathReturnNumber(ctxt, val)					\
+    valuePush((ctxt), xmlXPathNewFloat(val))
+/**
+ * xmlXPathReturnString:
+ * @ctxt:  an XPath parser context
+ * @str:  a string
+ *
+ * Pushes the string @str on the context stack
+ */
+#define xmlXPathReturnString(ctxt, str)					\
+    valuePush((ctxt), xmlXPathWrapString(str))
+/**
+ * xmlXPathReturnEmptyString:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes an empty string on the stack
+ */
+#define xmlXPathReturnEmptyString(ctxt)					\
+    valuePush((ctxt), xmlXPathNewCString(""))
+/**
+ * xmlXPathReturnNodeSet:
+ * @ctxt:  an XPath parser context
+ * @ns:  a node-set
+ *
+ * Pushes the node-set @ns on the context stack
+ */
+#define xmlXPathReturnNodeSet(ctxt, ns)					\
+    valuePush((ctxt), xmlXPathWrapNodeSet(ns))
+/**
+ * xmlXPathReturnEmptyNodeSet:
+ * @ctxt:  an XPath parser context
+ *
+ * Pushes an empty node-set on the context stack
+ */
+#define xmlXPathReturnEmptyNodeSet(ctxt, ns)				\
+    valuePush((ctxt), xmlXPathNewNodeSet(NULL))
+/**
+ * xmlXPathReturnExternal:
+ * @ctxt:  an XPath parser context
+ * @val:  user data
+ *
+ * Pushes user data on the context stack
+ */
+#define xmlXPathReturnExternal(ctxt, val)				\
+    valuePush((ctxt), xmlXPathWrapExternal(val))
+
+/**
+ * xmlXPathStackIsNodeSet:
+ * @ctxt: an XPath parser context
+ *
+ * Returns true if the current object on the stack is a node-set
+ */
+#define xmlXPathStackIsNodeSet(ctxt)					\
+    (((ctxt)->value != NULL)						\
+     && (((ctxt)->value->type == XPATH_NODESET)				\
+         || ((ctxt)->value->type == XPATH_XSLT_TREE)))
+
+/**
+ * xmlXPathEmptyNodeSet:
+ * @ns:  a node-set
+ *
+ * Empties a node-set
+ */
+#define xmlXPathEmptyNodeSet(ns)					\
+    { while ((ns)->nodeNr > 0) (ns)->nodeTab[(ns)->nodeNr--] = NULL; }
+
+    /**
+     * These macros shouldn't be used anymore. Prefer above functions
+     * and macros.
+     */
+
 /**
  * CHECK_ERROR:
  *
@@ -123,7 +290,7 @@
         xmlXPathBooleanFunction(ctxt, 1);
 
 /*
- * Varibale Lookup forwarding
+ * Variable Lookup forwarding
  */
 typedef xmlXPathObjectPtr
 	(*xmlXPathVariableLookupFunc)	(void *ctxt,
@@ -148,6 +315,40 @@
 void		xmlXPathDebugDumpCompExpr(FILE *output,
 					 xmlXPathCompExprPtr comp,
 					 int depth);
+
+/**
+ * NodeSet handling
+ */
+xmlNodeSetPtr	xmlXPathDifference		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+xmlNodeSetPtr	xmlXPathIntersection		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+xmlNodeSetPtr	xmlXPathDistinctSorted		(xmlNodeSetPtr nodes);
+xmlNodeSetPtr	xmlXPathDistinct		(xmlNodeSetPtr nodes);
+
+int		xmlXPathHasSameNodes		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+xmlNodeSetPtr	xmlXPathNodeLeadingSorted	(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+xmlNodeSetPtr	xmlXPathLeadingSorted		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+xmlNodeSetPtr	xmlXPathNodeLeading		(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+xmlNodeSetPtr	xmlXPathLeading			(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+xmlNodeSetPtr	xmlXPathNodeTrailingSorted	(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+xmlNodeSetPtr	xmlXPathTrailingSorted		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+xmlNodeSetPtr	xmlXPathNodeTrailing		(xmlNodeSetPtr nodes,
+						 xmlNodePtr node);
+xmlNodeSetPtr	xmlXPathTrailing		(xmlNodeSetPtr nodes1,
+						 xmlNodeSetPtr nodes2);
+
+
 /**
  * Extending a context
  */
@@ -244,6 +445,7 @@
 xmlXPathObjectPtr xmlXPathNewNodeSet(xmlNodePtr val);
 xmlXPathObjectPtr xmlXPathNewNodeSetList(xmlNodeSetPtr val);
 xmlXPathObjectPtr xmlXPathWrapNodeSet(xmlNodeSetPtr val);
+    xmlXPathObjectPtr xmlXPathWrapExternal(void *val);
 void xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj);
 
 
diff --git a/xpath.c b/xpath.c
index 1fb3eea..b98ebac 100644
--- a/xpath.c
+++ b/xpath.c
@@ -941,6 +941,137 @@
 
 PUSH_AND_POP(xmlXPathObjectPtr, value)
 
+/**
+ * xmlXPathPopBoolean:
+ * @ctxt:  an XPath parser context
+ *
+ * Pops a boolean from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the boolean
+ */
+int
+xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
+    xmlXPathObjectPtr obj;
+    int ret;
+
+    obj = valuePop(ctxt);
+    if (obj == NULL) {
+	xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+	return(0);
+    }
+    ret = xmlXPathCastToBoolean(obj);
+    xmlXPathFreeObject(obj);
+    return(ret);
+}
+
+/**
+ * xmlXPathPopNumber:
+ * @ctxt:  an XPath parser context
+ *
+ * Pops a number from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the number
+ */
+double
+xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
+    xmlXPathObjectPtr obj;
+    double ret;
+
+    obj = valuePop(ctxt);
+    if (obj == NULL) {
+	xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+	return(0);
+    }
+    ret = xmlXPathCastToNumber(obj);
+    xmlXPathFreeObject(obj);
+    return(ret);
+}
+
+/**
+ * xmlXPathPopString:
+ * @ctxt:  an XPath parser context
+ *
+ * Pops a string from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the string
+ */
+xmlChar *
+xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
+    xmlXPathObjectPtr obj;
+    xmlChar * ret;
+
+    obj = valuePop(ctxt);
+    if (obj == NULL) {
+	xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+	return(NULL);
+    }
+    ret = xmlXPathCastToString(obj);
+    /* TODO: needs refactoring somewhere else */
+    if (obj->stringval == ret)
+	obj->stringval = NULL;
+    xmlXPathFreeObject(obj);
+    return(ret);
+}
+
+/**
+ * xmlXPathPopNodeSet:
+ * @ctxt:  an XPath parser context
+ *
+ * Pops a node-set from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the node-set
+ */
+xmlNodeSetPtr
+xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
+    xmlXPathObjectPtr obj;
+    xmlNodeSetPtr ret;
+
+    if (ctxt->value == NULL) {
+	xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+	return(NULL);
+    }
+    if (!xmlXPathStackIsNodeSet(ctxt)) {
+	xmlXPathSetTypeError(ctxt);
+	return(NULL);
+    }
+    obj = valuePop(ctxt);
+    ret = obj->nodesetval;
+    xmlXPathFreeNodeSetList(obj);
+    return(ret);
+}
+
+/**
+ * xmlXPathPopExternal:
+ * @ctxt:  an XPath parser context
+ *
+ * Pops an external oject from the stack, handling conversion if needed.
+ * Check error with #xmlXPathCheckError.
+ *
+ * Returns the object
+ */
+void *
+xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
+    xmlXPathObjectPtr obj;
+    void * ret;
+
+    if (ctxt->value == NULL) {
+	xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
+	return(NULL);
+    }
+    if (ctxt->value->type != XPATH_USERS) {
+	xmlXPathSetTypeError(ctxt);
+	return(NULL);
+    }
+    obj = valuePop(ctxt);
+    ret = obj->user;
+    xmlXPathFreeObject(obj);
+    return(ret);
+}
+
 /*
  * Macros for accessing the content. Those should be used only by the parser,
  * and not exported.
@@ -1120,9 +1251,7 @@
  * @line:  the line number
  * @no:  the error number
  *
- * Create a new xmlNodeSetPtr of type double and of value @val
- *
- * Returns the newly created object.
+ * Formats an error message.
  */
 void
 xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
@@ -1321,6 +1450,26 @@
 }
 
 /**
+ * xmlXPathNodeSetContains:
+ * @cur:  the node-set
+ * @val:  the node
+ *
+ * checks whether @cur contains @val
+ *
+ * Returns true (1) if @cur contains @val, false (0) otherwise
+ */
+int
+xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
+    int i;
+
+    for (i = 0; i < cur->nodeNr; i++) {
+        if (cur->nodeTab[i] == val)
+	    return(1);
+    }
+    return(0);
+}
+
+/**
  * xmlXPathNodeSetAdd:
  * @cur:  the initial node set
  * @val:  a new xmlNodePtr
@@ -1724,6 +1873,364 @@
     xmlFree(obj);
 }
 
+/**
+ * xmlXPathDifference:
+ * @nodes1:  a node-set
+ * @nodes2:  a node-set
+ *
+ * Implements the EXSLT - Sets difference() function:
+ *    node-set set:difference (node-set, node-set)
+ *
+ * Returns the difference between the two node sets, or nodes1 if
+ *         nodes2 is empty
+ */
+xmlNodeSetPtr
+xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+    xmlNodeSetPtr ret;
+    int i, l1;
+    xmlNodePtr cur;
+
+    if (xmlXPathNodeSetIsEmpty(nodes2))
+	return(nodes1);
+
+    ret = xmlXPathNodeSetCreate(NULL);
+    if (xmlXPathNodeSetIsEmpty(nodes1))
+	return(ret);
+
+    l1 = xmlXPathNodeSetGetLength(nodes1);
+
+    for (i = 0; i < l1; i++) {
+	cur = xmlXPathNodeSetItem(nodes1, i);
+	if (!xmlXPathNodeSetContains(nodes2, cur))
+	    xmlXPathNodeSetAddUnique(ret, cur);
+    }
+    return(ret);
+}
+
+/**
+ * xmlXPathIntersection:
+ * @nodes1:  a node-set
+ * @nodes2:  a node-set
+ *
+ * Implements the EXSLT - Sets intersection() function:
+ *    node-set set:intersection (node-set, node-set)
+ *
+ * Returns a node set comprising the nodes that are within both the
+ *         node sets passed as arguments
+ */
+xmlNodeSetPtr
+xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+    xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
+    int i, l1;
+    xmlNodePtr cur;
+
+    if (xmlXPathNodeSetIsEmpty(nodes1))
+	return(ret);
+    if (xmlXPathNodeSetIsEmpty(nodes2))
+	return(ret);
+
+    l1 = xmlXPathNodeSetGetLength(nodes1);
+
+    for (i = 0; i < l1; i++) {
+	cur = xmlXPathNodeSetItem(nodes1, i);
+	if (xmlXPathNodeSetContains(nodes2, cur))
+	    xmlXPathNodeSetAddUnique(ret, cur);
+    }
+    return(ret);
+}
+
+/**
+ * xmlXPathDistinctSorted:
+ * @nodes:  a node-set, sorted by document order
+ *
+ * Implements the EXSLT - Sets distinct() function:
+ *    node-set set:distinct (node-set)
+ * 
+ * Returns a subset of the nodes contained in @nodes, or @nodes if
+ *         it is empty
+ */
+xmlNodeSetPtr
+xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
+    xmlNodeSetPtr ret;
+    xmlHashTablePtr hash;
+    int i, l;
+    xmlChar * strval;
+    xmlNodePtr cur;
+
+    if (xmlXPathNodeSetIsEmpty(nodes))
+	return(nodes);
+
+    ret = xmlXPathNodeSetCreate(NULL);
+    l = xmlXPathNodeSetGetLength(nodes);
+    hash = xmlHashCreate (l);
+    for (i = 0; i < l; i++) {
+	cur = xmlXPathNodeSetItem(nodes, i);
+	strval = xmlXPathCastNodeToString(cur);
+	if (xmlHashLookup(hash, strval) == NULL) {
+	    xmlHashAddEntry(hash, strval, strval);
+	    xmlXPathNodeSetAddUnique(ret, cur);
+	} else {
+	    xmlFree(strval);
+	}
+    }
+    xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
+    return(ret);
+}
+
+/**
+ * xmlXPathDistinct:
+ * @nodes:  a node-set
+ *
+ * Implements the EXSLT - Sets distinct() function:
+ *    node-set set:distinct (node-set)
+ * @nodes is sorted by document order, then #exslSetsDistinctSorted
+ * is called with the sorted node-set
+ *
+ * Returns a subset of the nodes contained in @nodes, or @nodes if
+ *         it is empty
+ */
+xmlNodeSetPtr
+xmlXPathDistinct (xmlNodeSetPtr nodes) {
+    if (xmlXPathNodeSetIsEmpty(nodes))
+	return(nodes);
+
+    xmlXPathNodeSetSort(nodes);
+    return(xmlXPathDistinctSorted(nodes));
+}
+
+/**
+ * xmlXPathHasSameNodes:
+ * @nodes1:  a node-set
+ * @nodes2:  a node-set
+ *
+ * Implements the EXSLT - Sets has-same-nodes function:
+ *    boolean set:has-same-node(node-set, node-set)
+ *
+ * Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
+ *         otherwise
+ */
+int
+xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+    int i, l;
+    xmlNodePtr cur;
+
+    if (xmlXPathNodeSetIsEmpty(nodes1) ||
+	xmlXPathNodeSetIsEmpty(nodes2))
+	return(0);
+
+    l = xmlXPathNodeSetGetLength(nodes1);
+    for (i = 0; i < l; i++) {
+	cur = xmlXPathNodeSetItem(nodes1, i);
+	if (xmlXPathNodeSetContains(nodes2, cur))
+	    return(1);
+    }
+    return(0);
+}
+
+/**
+ * xmlXPathNodeLeadingSorted:
+ * @nodes: a node-set, sorted by document order
+ * @node: a node
+ *
+ * Implements the EXSLT - Sets leading() function:
+ *    node-set set:leading (node-set, node-set)
+ *
+ * Returns the nodes in @nodes that precede @node in document order,
+ *         @nodes if @node is NULL or an empty node-set if @nodes
+ *         doesn't contain @node
+ */
+xmlNodeSetPtr
+xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
+    int i, l;
+    xmlNodePtr cur;
+    xmlNodeSetPtr ret;
+
+    if (node == NULL)
+	return(nodes);
+
+    ret = xmlXPathNodeSetCreate(NULL);
+    if (xmlXPathNodeSetIsEmpty(nodes) ||
+	(!xmlXPathNodeSetContains(nodes, node)))
+	return(ret);
+
+    l = xmlXPathNodeSetGetLength(nodes);
+    for (i = 0; i < l; i++) {
+	cur = xmlXPathNodeSetItem(nodes, i);
+	if (cur == node)
+	    break;
+	xmlXPathNodeSetAddUnique(ret, cur);
+    }
+    return(ret);
+}
+
+/**
+ * xmlXPathNodeLeading:
+ * @nodes:  a node-set
+ * @node:  a node
+ *
+ * Implements the EXSLT - Sets leading() function:
+ *    node-set set:leading (node-set, node-set)
+ * @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
+ * is called.
+ *
+ * Returns the nodes in @nodes that precede @node in document order,
+ *         @nodes if @node is NULL or an empty node-set if @nodes
+ *         doesn't contain @node
+ */
+xmlNodeSetPtr
+xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
+    xmlXPathNodeSetSort(nodes);
+    return(xmlXPathNodeLeadingSorted(nodes, node));
+}
+
+/**
+ * xmlXPathLeadingSorted:
+ * @nodes1:  a node-set, sorted by document order
+ * @nodes2:  a node-set, sorted by document order
+ *
+ * Implements the EXSLT - Sets leading() function:
+ *    node-set set:leading (node-set, node-set)
+ *
+ * Returns the nodes in @nodes1 that precede the first node in @nodes2
+ *         in document order, @nodes1 if @nodes2 is NULL or empty or
+ *         an empty node-set if @nodes1 doesn't contain @nodes2
+ */
+xmlNodeSetPtr
+xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+    if (xmlXPathNodeSetIsEmpty(nodes2))
+	return(nodes1);
+    return(xmlXPathNodeLeadingSorted(nodes1,
+				     xmlXPathNodeSetItem(nodes2, 1)));
+}
+
+/**
+ * xmlXPathLeading:
+ * @nodes1:  a node-set
+ * @nodes2:  a node-set
+ *
+ * Implements the EXSLT - Sets leading() function:
+ *    node-set set:leading (node-set, node-set)
+ * @nodes1 and @nodes2 are sorted by document order, then
+ * #exslSetsLeadingSorted is called.
+ *
+ * Returns the nodes in @nodes1 that precede the first node in @nodes2
+ *         in document order, @nodes1 if @nodes2 is NULL or empty or
+ *         an empty node-set if @nodes1 doesn't contain @nodes2
+ */
+xmlNodeSetPtr
+xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+    if (xmlXPathNodeSetIsEmpty(nodes2))
+	return(nodes1);
+    if (xmlXPathNodeSetIsEmpty(nodes1))
+	return(xmlXPathNodeSetCreate(NULL));
+    xmlXPathNodeSetSort(nodes1);
+    xmlXPathNodeSetSort(nodes2);
+    return(xmlXPathNodeLeadingSorted(nodes1,
+				     xmlXPathNodeSetItem(nodes2, 1)));
+}
+
+/**
+ * xmlXPathNodeTrailingSorted:
+ * @nodes: a node-set, sorted by document order
+ * @node: a node
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ *    node-set set:trailing (node-set, node-set)
+ *
+ * Returns the nodes in @nodes that follow @node in document order,
+ *         @nodes if @node is NULL or an empty node-set if @nodes
+ *         doesn't contain @node
+ */
+xmlNodeSetPtr
+xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
+    int i, l;
+    xmlNodePtr cur;
+    xmlNodeSetPtr ret;
+
+    if (node == NULL)
+	return(nodes);
+
+    ret = xmlXPathNodeSetCreate(NULL);
+    if (xmlXPathNodeSetIsEmpty(nodes) ||
+	(!xmlXPathNodeSetContains(nodes, node)))
+	return(ret);
+
+    l = xmlXPathNodeSetGetLength(nodes);
+    for (i = 0; i < l; i++) {
+	cur = xmlXPathNodeSetItem(nodes, i);
+	if (cur == node)
+	    break;
+	xmlXPathNodeSetAddUnique(ret, cur);
+    }
+    return(ret);
+}
+
+/**
+ * xmlXPathNodeTrailing:
+ * @nodes:  a node-set
+ * @node:  a node
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ *    node-set set:trailing (node-set, node-set)
+ * @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
+ * is called.
+ *
+ * Returns the nodes in @nodes that follow @node in document order,
+ *         @nodes if @node is NULL or an empty node-set if @nodes
+ *         doesn't contain @node
+ */
+xmlNodeSetPtr
+xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
+    xmlXPathNodeSetSort(nodes);
+    return(xmlXPathNodeTrailingSorted(nodes, node));
+}
+
+/**
+ * xmlXPathTrailingSorted:
+ * @nodes1:  a node-set, sorted by document order
+ * @nodes2:  a node-set, sorted by document order
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ *    node-set set:trailing (node-set, node-set)
+ *
+ * Returns the nodes in @nodes1 that follow the first node in @nodes2
+ *         in document order, @nodes1 if @nodes2 is NULL or empty or
+ *         an empty node-set if @nodes1 doesn't contain @nodes2
+ */
+xmlNodeSetPtr
+xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+    if (xmlXPathNodeSetIsEmpty(nodes2))
+	return(nodes1);
+    return(xmlXPathNodeTrailingSorted(nodes1,
+				      xmlXPathNodeSetItem(nodes2, 0)));
+}
+
+/**
+ * xmlXPathTrailing:
+ * @nodes1:  a node-set
+ * @nodes2:  a node-set
+ *
+ * Implements the EXSLT - Sets trailing() function:
+ *    node-set set:trailing (node-set, node-set)
+ * @nodes1 and @nodes2 are sorted by document order, then
+ * #xmlXPathTrailingSorted is called.
+ *
+ * Returns the nodes in @nodes1 that follow the first node in @nodes2
+ *         in document order, @nodes1 if @nodes2 is NULL or empty or
+ *         an empty node-set if @nodes1 doesn't contain @nodes2
+ */
+xmlNodeSetPtr
+xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
+    if (xmlXPathNodeSetIsEmpty(nodes2))
+	return(nodes1);
+    if (xmlXPathNodeSetIsEmpty(nodes1))
+	return(xmlXPathNodeSetCreate(NULL));
+    xmlXPathNodeSetSort(nodes1);
+    xmlXPathNodeSetSort(nodes2);
+    return(xmlXPathNodeTrailingSorted(nodes1,
+				      xmlXPathNodeSetItem(nodes2, 0)));
+}
+
 /************************************************************************
  *									*
  *		Routines to handle extra functions			*
@@ -2192,6 +2699,30 @@
 }
 
 /**
+ * xmlXPathWrapExternal:
+ * @val:  the user data
+ *
+ * Wraps the @val data into an XPath object.
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathWrapExternal (void *val) {
+    xmlXPathObjectPtr ret;
+
+    ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
+    if (ret == NULL) {
+        xmlGenericError(xmlGenericErrorContext,
+		"xmlXPathWrapString: out of memory\n");
+	return(NULL);
+    }
+    memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+    ret->type = XPATH_USERS;
+    ret->user = val;
+    return(ret);
+}
+
+/**
  * xmlXPathObjectCopy:
  * @val:  the original object
  *