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
*