Added XPath code (http://www.w3.org/TR/xpath), updated HTML support and docs, Daniel
diff --git a/xpath.c b/xpath.c
new file mode 100644
index 0000000..d6c8f7e
--- /dev/null
+++ b/xpath.c
@@ -0,0 +1,3698 @@
+/*
+ * xpath.c: XML Path Language implementation
+ * XPath is a language for addressing parts of an XML document,
+ * designed to be used by both XSLT and XPointer.
+ *
+ * Reference: W3C Working Draft internal 5 July 1999
+ * http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html
+ * Public reference:
+ * http://www.w3.org/TR/WD-xpath/
+ *
+ * See COPYRIGHT for the status of this software
+ *
+ * Author: Daniel.Veillard@w3.org
+ */
+
+#include <malloc.h>
+#include <math.h>
+#include <stdio.h>
+#include "tree.h"
+#include "xpath.h"
+#include "parserInternals.h"
+
+/*
+ * TODO: create a pseudo document element and affect it as the
+ * actual root.
+ */
+/*
+ * That sucks but I couldn't find NAN on a PeeCee Linux Glibc 2.1
+ */
+#ifndef NAN
+#define NAN 12345679
+#endif
+
+/* #define DEBUG */
+/* #define DEBUG_STEP */
+/* #define DEBUG_EXPR */
+
+FILE *xmlXPathDebug = NULL;
+
+#define TODO \
+ fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \
+ __FILE__, __LINE__);
+
+#define STRANGE \
+ fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \
+ __FILE__, __LINE__);
+
+float xmlXPathStringEvalNumber(const CHAR *str);
+
+/************************************************************************
+ * *
+ * Parser stacks related functions and macros *
+ * *
+ ************************************************************************/
+
+/*
+ * Generic function for accessing stacks in the Parser Context
+ */
+
+#define PUSH_AND_POP(type, name) \
+extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
+ if (ctxt->name##Nr >= ctxt->name##Max) { \
+ ctxt->name##Max *= 2; \
+ ctxt->name##Tab = (void *) realloc(ctxt->name##Tab, \
+ ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
+ if (ctxt->name##Tab == NULL) { \
+ fprintf(xmlXPathDebug, "realloc failed !\n"); \
+ exit(1); \
+ } \
+ } \
+ ctxt->name##Tab[ctxt->name##Nr] = value; \
+ ctxt->name = value; \
+ return(ctxt->name##Nr++); \
+} \
+extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
+ type ret; \
+ if (ctxt->name##Nr <= 0) return(0); \
+ ctxt->name##Nr--; \
+ if (ctxt->name##Nr > 0) \
+ ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
+ else \
+ ctxt->name = NULL; \
+ ret = ctxt->name##Tab[ctxt->name##Nr]; \
+ ctxt->name##Tab[ctxt->name##Nr] = 0; \
+ return(ret); \
+} \
+
+PUSH_AND_POP(xmlXPathObjectPtr, value)
+
+/*
+ * Macros for accessing the content. Those should be used only by the parser,
+ * and not exported.
+ *
+ * Dirty macros, i.e. one need to make assumption on the context to use them
+ *
+ * CUR_PTR return the current pointer to the CHAR to be parsed.
+ * CUR returns the current CHAR value, i.e. a 8 bit value if compiled
+ * in ISO-Latin or UTF-8, and the current 16 bit value if compiled
+ * in UNICODE mode. This should be used internally by the parser
+ * only to compare to ASCII values otherwise it would break when
+ * running with UTF-8 encoding.
+ * NXT(n) returns the n'th next CHAR. Same as CUR is should be used only
+ * to compare on ASCII based substring.
+ * SKIP(n) Skip n CHAR, and must also be used only to skip ASCII defined
+ * strings within the parser.
+ * CURRENT Returns the current char value, with the full decoding of
+ * UTF-8 if we are using this mode. It returns an int.
+ * NEXT Skip to the next character, this does the proper decoding
+ * in UTF-8 mode. It also pop-up unfinished entities on the fly.
+ * It returns the pointer to the current CHAR.
+ */
+
+#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(*(ctxt->cur))) NEXT
+
+#ifndef USE_UTF_8
+#define CURRENT (*ctxt->cur)
+#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
+#else
+#endif
+
+/************************************************************************
+ * *
+ * Error handling routines *
+ * *
+ ************************************************************************/
+
+#define XPATH_EXPRESSION_OK 0
+#define XPATH_NUMBER_ERROR 1
+#define XPATH_UNFINISHED_LITERAL_ERROR 2
+#define XPATH_START_LITERAL_ERROR 3
+#define XPATH_VARIABLE_REF_ERROR 4
+#define XPATH_UNDEF_VARIABLE_ERROR 5
+#define XPATH_INVALID_PREDICATE_ERROR 6
+#define XPATH_EXPR_ERROR 7
+#define XPATH_UNCLOSED_ERROR 8
+#define XPATH_UNKNOWN_FUNC_ERROR 9
+#define XPATH_INVALID_OPERAND 10
+#define XPATH_INVALID_TYPE 11
+#define XPATH_INVALID_ARITY 12
+
+const char *xmlXPathErrorMessages[] = {
+ "Ok",
+ "Number encoding",
+ "Unfinished litteral",
+ "Start of litteral",
+ "Expected $ for variable reference",
+ "Undefined variable",
+ "Invalid predicate",
+ "Invalid expression",
+ "Missing closing curly brace",
+ "Unregistered function",
+ "Invalid operand",
+ "Invalid type",
+ "Invalid number of arguments",
+};
+
+/**
+ * xmlXPathError:
+ * @ctxt: the XPath Parser context
+ * @file: the file name
+ * @line: the line number
+ * @no: the error number
+ *
+ * Create a new xmlNodeSetPtr of type float and of value @val
+ *
+ * Returns the newly created object.
+ */
+void
+xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
+ int line, int no) {
+ int n;
+ const char *cur;
+ const char *base;
+
+ fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line,
+ xmlXPathErrorMessages[no]);
+
+ cur = ctxt->cur;
+ base = ctxt->base;
+ while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
+ cur--;
+ }
+ n = 0;
+ while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
+ cur--;
+ if ((*cur == '\n') || (*cur == '\r')) cur++;
+ base = cur;
+ n = 0;
+ while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
+ fprintf(xmlXPathDebug, "%c", (unsigned char) *cur++);
+ n++;
+ }
+ fprintf(xmlXPathDebug, "\n");
+ cur = ctxt->cur;
+ while ((*cur == '\n') || (*cur == '\r'))
+ cur--;
+ n = 0;
+ while ((cur != base) && (n++ < 80)) {
+ fprintf(xmlXPathDebug, " ");
+ base++;
+ }
+ fprintf(xmlXPathDebug,"^\n");
+}
+
+#define CHECK_ERROR \
+ if (ctxt->error != XPATH_EXPRESSION_OK) return
+
+#define ERROR(X) \
+ { xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
+ ctxt->error = (X); return; }
+
+#define CHECK_TYPE(typeval) \
+ if ((ctxt->value == NULL) || (ctxt->value->type != typeval)) \
+ ERROR(XPATH_INVALID_TYPE) \
+
+
+/************************************************************************
+ * *
+ * Routines to handle NodeSets *
+ * *
+ ************************************************************************/
+
+#define XML_NODESET_DEFAULT 10
+/**
+ * xmlXPathNodeSetCreate:
+ * @val: an initial xmlNodePtr, or NULL
+ *
+ * Create a new xmlNodeSetPtr of type float and of value @val
+ *
+ * Returns the newly created object.
+ */
+xmlNodeSetPtr
+xmlXPathNodeSetCreate(xmlNodePtr val) {
+ xmlNodeSetPtr ret;
+
+ ret = (xmlNodeSetPtr) malloc(sizeof(xmlNodeSet));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlNodeSet));
+ if (val != NULL) {
+ ret->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
+ sizeof(xmlNodePtr));
+ if (ret->nodeTab == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
+ return(NULL);
+ }
+ memset(ret->nodeTab, 0 ,
+ XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
+ ret->nodeMax = XML_NODESET_DEFAULT;
+ ret->nodeTab[ret->nodeNr++] = val;
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathNodeSetAdd:
+ * @cur: the initial node set
+ * @val: a new xmlNodePtr
+ *
+ * add a new xmlNodePtr ot an existing NodeSet
+ */
+void
+xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
+ int i;
+
+ if (val == NULL) return;
+
+ /*
+ * check against doublons
+ */
+ for (i = 0;i < cur->nodeNr;i++)
+ if (cur->nodeTab[i] == val) return;
+
+ /*
+ * grow the nodeTab if needed
+ */
+ if (cur->nodeMax == 0) {
+ cur->nodeTab = (xmlNodePtr *) malloc(XML_NODESET_DEFAULT *
+ sizeof(xmlNodePtr));
+ if (cur->nodeTab == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
+ return;
+ }
+ memset(cur->nodeTab, 0 ,
+ XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
+ cur->nodeMax = XML_NODESET_DEFAULT;
+ } else if (cur->nodeNr == cur->nodeMax) {
+ xmlNodePtr *temp;
+
+ cur->nodeMax *= 2;
+ temp = (xmlNodePtr *) realloc(cur->nodeTab, cur->nodeMax *
+ sizeof(xmlNodePtr));
+ if (temp == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
+ return;
+ }
+ }
+ cur->nodeTab[cur->nodeNr++] = val;
+}
+
+/**
+ * xmlXPathNodeSetMerge:
+ * @val1: the first NodeSet
+ * @val2: the second NodeSet
+ *
+ * Merges two nodesets, all nodes from @val2 are added to @val1
+ *
+ * Returns val1 once extended or NULL in case of error.
+ */
+xmlNodeSetPtr
+xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
+ int i;
+
+ if (val1 == NULL) return(NULL);
+ if (val2 == NULL) return(val1);
+
+ /*
+ * !!!!! this can be optimized a lot, knowing that both
+ * val1 and val2 already have unicity of their values.
+ */
+
+ for (i = 0;i < val2->nodeNr;i++)
+ xmlXPathNodeSetAdd(val1, val2->nodeTab[i]);
+
+ return(val1);
+}
+
+/**
+ * xmlXPathNodeSetDel:
+ * @cur: the initial node set
+ * @val: an xmlNodePtr
+ *
+ * Removes an xmlNodePtr from an existing NodeSet
+ */
+void
+xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
+ int i;
+
+ if (cur == NULL) return;
+ if (val == NULL) return;
+
+ /*
+ * check against doublons
+ */
+ for (i = 0;i < cur->nodeNr;i++)
+ if (cur->nodeTab[i] == val) break;
+
+ if (i >= cur->nodeNr) {
+#ifdef DEBUG
+ fprintf(xmlXPathDebug,
+ "xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
+ val->name);
+#endif
+ return;
+ }
+ cur->nodeNr--;
+ for (;i < cur->nodeNr;i++)
+ cur->nodeTab[i] = cur->nodeTab[i + 1];
+ cur->nodeTab[cur->nodeNr] = NULL;
+}
+
+/**
+ * xmlXPathNodeSetRemove:
+ * @cur: the initial node set
+ * @val: the index to remove
+ *
+ * Removes an entry from an existing NodeSet list.
+ */
+void
+xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
+ if (cur == NULL) return;
+ if (val >= cur->nodeNr) return;
+ cur->nodeNr--;
+ for (;val < cur->nodeNr;val++)
+ cur->nodeTab[val] = cur->nodeTab[val + 1];
+ cur->nodeTab[cur->nodeNr] = NULL;
+}
+
+/**
+ * xmlXPathFreeNodeSet:
+ * @obj: the xmlNodeSetPtr to free
+ *
+ * Free the NodeSet compound (not the actual nodes !).
+ */
+void
+xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
+ if (obj == NULL) return;
+ if (obj->nodeTab != NULL) {
+#ifdef DEBUG
+ memset(obj->nodeTab, 0xB , sizeof(xmlNodePtr) * obj->nodeMax);
+#endif
+ free(obj->nodeTab);
+ }
+#ifdef DEBUG
+ memset(obj, 0xB , sizeof(xmlNodeSet));
+#endif
+ free(obj);
+}
+
+#ifdef DEBUG
+/**
+ * xmlXPathDebugNodeSet:
+ * @output: a FILE * for the output
+ * @obj: the xmlNodeSetPtr to free
+ *
+ * Quick display of a NodeSet
+ */
+void
+xmlXPathDebugNodeSet(FILE *output, xmlNodeSetPtr obj) {
+ int i;
+
+ if (output == NULL) output = xmlXPathDebug;
+ if (obj == NULL) {
+ fprintf(output, "NodeSet == NULL !\n");
+ return;
+ }
+ if (obj->nodeNr == 0) {
+ fprintf(output, "NodeSet is empty\n");
+ return;
+ }
+ if (obj->nodeTab == NULL) {
+ fprintf(output, " nodeTab == NULL !\n");
+ return;
+ }
+ for (i = 0; i < obj->nodeNr; i++) {
+ if (obj->nodeTab[i] == NULL) {
+ fprintf(output, " NULL !\n");
+ return;
+ }
+ if (obj->nodeTab[i]->name == NULL)
+ fprintf(output, " noname!");
+ else fprintf(output, " %s", obj->nodeTab[i]->name);
+ }
+ fprintf(output, "\n");
+}
+#endif
+
+/************************************************************************
+ * *
+ * Routines to handle Variable *
+ * *
+ * UNIMPLEMENTED CURRENTLY *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathVariablelookup:
+ * @ctxt: the XPath Parser context
+ * @prefix: the variable name namespace if any
+ * @name: the variable name
+ *
+ * Search in the Variable array of the context for the given
+ * variable value.
+ *
+ * UNIMPLEMENTED: always return NULL.
+ *
+ * Returns the value or NULL if not found
+ */
+xmlXPathObjectPtr
+xmlXPathVariablelookup(xmlXPathParserContextPtr ctxt,
+ const CHAR *prefix, const CHAR *name) {
+ return(NULL);
+}
+
+/************************************************************************
+ * *
+ * Routines to handle Values *
+ * *
+ ************************************************************************/
+
+/* Allocations are terrible, one need to optimize all this !!! */
+
+/**
+ * xmlXPathNewFloat:
+ * @val: the float value
+ *
+ * Create a new xmlXPathObjectPtr of type float and of value @val
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathNewFloat(float val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathObject));
+ ret->type = XPATH_NUMBER;
+ ret->floatval = val;
+ return(ret);
+}
+
+/**
+ * xmlXPathNewBoolean:
+ * @val: the boolean value
+ *
+ * Create a new xmlXPathObjectPtr of type boolean and of value @val
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathNewBoolean(int val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathObject));
+ ret->type = XPATH_BOOLEAN;
+ ret->boolval = (val != 0);
+ return(ret);
+}
+
+/**
+ * xmlXPathNewMarker:
+ *
+ * Create a new xmlXPathObjectPtr of a special marker type for functions
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathNewMarker(void) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathObject));
+ ret->type = XPATH_MARKER;
+ return(ret);
+}
+
+/**
+ * xmlXPathNewBoolean:
+ * @val: the CHAR * value
+ *
+ * Create a new xmlXPathObjectPtr of type string and of value @val
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathNewString(const CHAR *val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathObject));
+ ret->type = XPATH_STRING;
+ ret->stringval = xmlStrdup(val);
+ return(ret);
+}
+
+/**
+ * xmlXPathNewNodeSet:
+ * @val: the NodePtr value
+ *
+ * Create a new xmlXPathObjectPtr of type NodeSet and initialize
+ * it with the single Node @val
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathNewNodeSet(xmlNodePtr val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathObject));
+ ret->type = XPATH_NODESET;
+ ret->nodesetval = xmlXPathNodeSetCreate(val);
+ return(ret);
+}
+
+/**
+ * xmlXPathNewNodeSetList:
+ * @val: an existing NodeSet
+ *
+ * Create a new xmlXPathObjectPtr of type NodeSet and initialize
+ * it with the Nodeset @val
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathObject));
+ ret->type = XPATH_NODESET;
+ ret->nodesetval = val;
+ return(ret);
+}
+
+/**
+ * xmlXPathFreeObject:
+ * @obj: the object to free
+ *
+ * Free up an xmlXPathObjectPtr object.
+ */
+void
+xmlXPathFreeObject(xmlXPathObjectPtr obj) {
+ if (obj == NULL) return;
+ if (obj->nodesetval != NULL)
+ xmlXPathFreeNodeSet(obj->nodesetval);
+ if (obj->stringval != NULL)
+ free(obj->stringval);
+#ifdef DEBUG
+ memset(obj, 0xB , sizeof(xmlXPathObject));
+#endif
+ free(obj);
+}
+
+/************************************************************************
+ * *
+ * Routines to handle XPath contexts *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathNewContext:
+ * @doc: the XML document
+ * @variables: the variable list
+ * @functions: the function list
+ * @namespaces: the namespace list
+ *
+ * Create a new xmlXPathContext
+ *
+ * Returns the xmlXPathContext just allocated.
+ */
+xmlXPathContextPtr
+xmlXPathNewContext(xmlDocPtr doc, void *variables, void *functions,
+ void *namespaces) {
+ xmlXPathContextPtr ret;
+
+ ret = (xmlXPathContextPtr) malloc(sizeof(xmlXPathContext));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewContext: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathContext));
+ ret->doc = doc;
+ ret->variables = variables;
+ ret->functions = functions;
+ ret->namespaces = namespaces;
+ return(ret);
+}
+
+/**
+ * xmlXPathFreeContext:
+ * @ctxt: the context to free
+ *
+ * Free up an xmlXPathContext
+ */
+void
+xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
+#ifdef DEBUG
+ memset(ctxt, 0xB , sizeof(xmlXPathContext));
+#endif
+ free(ctxt);
+}
+
+/************************************************************************
+ * *
+ * Routines to handle XPath parser contexts *
+ * *
+ ************************************************************************/
+
+#define CHECK_CTXT \
+ if (ctxt == NULL) { \
+ fprintf(xmlXPathDebug, "%s:%d Internal error: ctxt == NULL\n", \
+ __FILE__, __LINE__); \
+ } \
+
+
+#define CHECK_CONTEXT \
+ if (ctxt == NULL) { \
+ fprintf(xmlXPathDebug, "%s:%d Internal error: no context\n", \
+ __FILE__, __LINE__); \
+ } \
+ if (ctxt->doc == NULL) { \
+ fprintf(xmlXPathDebug, "%s:%d Internal error: no document\n", \
+ __FILE__, __LINE__); \
+ } \
+ if (ctxt->doc->root == NULL) { \
+ fprintf(xmlXPathDebug, \
+ "%s:%d Internal error: document without root\n", \
+ __FILE__, __LINE__); \
+ } \
+
+
+/**
+ * xmlXPathNewParserContext:
+ * @str: the XPath expression
+ * @ctxt: the XPath context
+ *
+ * Create a new xmlXPathParserContext
+ *
+ * Returns the xmlXPathParserContext just allocated.
+ */
+xmlXPathParserContextPtr
+xmlXPathNewParserContext(const CHAR *str, xmlXPathContextPtr ctxt) {
+ xmlXPathParserContextPtr ret;
+
+ ret = (xmlXPathParserContextPtr) malloc(sizeof(xmlXPathParserContext));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewParserContext: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , sizeof(xmlXPathParserContext));
+ ret->cur = ret->base = str;
+ ret->context = ctxt;
+
+ /* Allocate the value stack */
+ ret->valueTab = (xmlXPathObjectPtr *)
+ malloc(10 * sizeof(xmlXPathObjectPtr));
+ ret->valueNr = 0;
+ ret->valueMax = 10;
+ ret->value = NULL;
+ return(ret);
+}
+
+/**
+ * xmlXPathFreeParserContext:
+ * @ctxt: the context to free
+ *
+ * Free up an xmlXPathParserContext
+ */
+void
+xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
+ if (ctxt->valueTab != NULL) {
+#ifdef DEBUG
+ memset(ctxt->valueTab, 0xB , 10 * sizeof(xmlXPathObjectPtr));
+#endif
+ free(ctxt->valueTab);
+ }
+#ifdef DEBUG
+ memset(ctxt, 0xB , sizeof(xmlXPathParserContext));
+#endif
+ free(ctxt);
+}
+
+/************************************************************************
+ * *
+ * The implicit core function library *
+ * *
+ ************************************************************************/
+
+/*
+ * TODO: check the semantic for all these operations in case of operands
+ * with different types, Cast function probably need to be provided
+ * to simplify the coding.
+ */
+
+/*
+ * Auto-pop and cast to a number
+ */
+void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+#define CHECK_ARITY(x) \
+ if (nargs != (x)) { \
+ ERROR(XPATH_INVALID_ARITY); \
+ } \
+
+
+#define POP_FLOAT \
+ arg = valuePop(ctxt); \
+ if (arg == NULL) { \
+ ERROR(XPATH_INVALID_OPERAND); \
+ } \
+ if (arg->type != XPATH_NUMBER) { \
+ valuePush(ctxt, arg); \
+ xmlXPathNumberFunction(ctxt, 1); \
+ arg = valuePop(ctxt); \
+ }
+
+/**
+ * xmlXPathEqualValues:
+ * @arg1: first XPath object argument
+ * @arg2: second XPath object argument
+ *
+ * Implement the equal operation on XPath objects content: @arg1 == @arg2
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ * TODO: rewrite using the stack for evaluation
+ */
+int
+xmlXPathEqualValues(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
+ if (arg1 == arg2) {
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Equal: by pointer\n");
+#endif
+ return(1);
+ }
+ if ((arg1 == NULL) || (arg2 == NULL)) {
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Equal: arg NULL\n");
+#endif
+ return(0);
+ }
+ if (arg1->type != arg2->type) {
+ /* TODO : see 4.3 Boolean section !!!!!!!!!!! */
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Equal: distinct types\n");
+#endif
+ return(0);
+ }
+ switch (arg1->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Equal: undefined\n");
+#endif
+ return(0);
+ case XPATH_NODESET:
+ TODO /* compare nodesets */
+ break;
+ case XPATH_BOOLEAN:
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Equal: %d boolean %d \n",
+ arg1->boolval, arg2->boolval);
+#endif
+ return(arg1->boolval == arg2->boolval);
+ case XPATH_NUMBER:
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Equal: %f number %f \n",
+ arg1->floatval, arg2->floatval);
+#endif
+ return(arg1->floatval == arg2->floatval);
+ case XPATH_STRING:
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Equal: %s string %s \n",
+ arg1->stringval, arg2->stringval);
+#endif
+ return(!xmlStrcmp(arg1->stringval, arg2->stringval));
+ }
+ return(1);
+}
+
+/**
+ * xmlXPathCompareValues:
+ * @inf: less than (1) or greater than (2)
+ * @strict: is the comparison strict
+ * @arg1: first XPath object argument
+ * @arg2: second XPath object argument
+ *
+ * Implement the compare operation on XPath objects:
+ * @arg1 < @arg2 (1, 1, ...
+ * @arg1 <= @arg2 (1, 0, ...
+ * @arg1 > @arg2 (0, 1, ...
+ * @arg1 >= @arg2 (0, 0, ...
+ *
+ * Returns 0 or 1 depending on the results of the test.
+ */
+int
+xmlXPathCompareValues(int inf, int strict, xmlXPathObjectPtr arg1,
+ xmlXPathObjectPtr arg2) {
+ TODO /* compare */
+ return(0);
+}
+
+/**
+ * xmlXPathValueFlipSign:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the unary - operation on an XPath object
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+void
+xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+
+ POP_FLOAT
+ arg->floatval = -arg->floatval;
+ valuePush(ctxt, arg);
+}
+
+/**
+ * xmlXPathAddValues:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the add operation on XPath objects: @arg1 + @arg2
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+void
+xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ float val;
+
+ POP_FLOAT
+ val = arg->floatval;
+ xmlXPathFreeObject(arg);
+
+ POP_FLOAT
+ arg->floatval += val;
+ valuePush(ctxt, arg);
+}
+
+/**
+ * xmlXPathSubValues:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the substraction operation on XPath objects: @arg1 - @arg2
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+void
+xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ float val;
+
+ POP_FLOAT
+ val = arg->floatval;
+ xmlXPathFreeObject(arg);
+
+ POP_FLOAT
+ arg->floatval -= val;
+ valuePush(ctxt, arg);
+}
+
+/**
+ * xmlXPathMultValues:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the multiply operation on XPath objects: @arg1 * @arg2
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+void
+xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ float val;
+
+ POP_FLOAT
+ val = arg->floatval;
+ xmlXPathFreeObject(arg);
+
+ POP_FLOAT
+ arg->floatval *= val;
+ valuePush(ctxt, arg);
+}
+
+/**
+ * xmlXPathDivValues:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the div operation on XPath objects: @arg1 / @arg2
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+void
+xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ float val;
+
+ POP_FLOAT
+ val = arg->floatval;
+ xmlXPathFreeObject(arg);
+
+ POP_FLOAT
+ arg->floatval /= val;
+ valuePush(ctxt, arg);
+}
+
+/**
+ * xmlXPathModValues:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the div operation on XPath objects: @arg1 / @arg2
+ * The numeric operators convert their operands to numbers as if
+ * by calling the number function.
+ */
+void
+xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
+ xmlXPathObjectPtr arg;
+ float val;
+
+ POP_FLOAT
+ val = arg->floatval;
+ xmlXPathFreeObject(arg);
+
+ POP_FLOAT
+ arg->floatval /= val;
+ valuePush(ctxt, arg);
+}
+
+/************************************************************************
+ * *
+ * The traversal functions *
+ * *
+ ************************************************************************/
+
+#define AXIS_ANCESTOR 1
+#define AXIS_ANCESTOR_OR_SELF 2
+#define AXIS_ATTRIBUTE 3
+#define AXIS_CHILD 4
+#define AXIS_DESCENDANT 5
+#define AXIS_DESCENDANT_OR_SELF 6
+#define AXIS_FOLLOWING 7
+#define AXIS_FOLLOWING_SIBLING 8
+#define AXIS_NAMESPACE 9
+#define AXIS_PARENT 10
+#define AXIS_PRECEDING 11
+#define AXIS_PRECEDING_SIBLING 12
+#define AXIS_SELF 13
+
+/*
+ * A traversal function enumerates nodes along an axis.
+ * Initially it must be called with NULL, and it indicates
+ * termination on the axis by returning NULL.
+ */
+typedef xmlNodePtr (*xmlXPathTraversalFunction)
+ (xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
+
+/**
+ * mlXPathNextSelf:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "self" direction
+ * he self axis contains just the context node itself
+ */
+xmlNodePtr
+xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node);
+ return(NULL);
+}
+
+/**
+ * mlXPathNextChild:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "child" direction
+ * The child axis contains the children of the context node in document order.
+ */
+xmlNodePtr
+xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node->childs);
+ return(cur->next);
+}
+
+/**
+ * mlXPathNextDescendant:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "descendant" direction
+ * the descendant axis contains the descendants of the context node in document
+ * order; a descendant is a child or a child of a child and so on.
+ */
+xmlNodePtr
+xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node->childs);
+
+ if (cur->childs != NULL) return(cur->childs);
+ if (cur->next != NULL) return(cur->next);
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL) return(NULL);
+ if (cur == ctxt->context->node) return(NULL);
+ if (cur->next != NULL) {
+ cur = cur->next;
+ return(cur);
+ }
+ } while (cur != NULL);
+ return(cur);
+}
+
+/**
+ * mlXPathNextDescendantOrSelf:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "descendant-or-self" direction
+ * the descendant-or-self axis contains the context node and the descendants
+ * of the context node in document order; thus the context node is the first
+ * node on the axis, and the first child of the context node is the second node
+ * on the axis
+ */
+xmlNodePtr
+xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node);
+
+ if (cur->childs != NULL) return(cur->childs);
+ if (cur->next != NULL) return(cur->next);
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL) return(NULL);
+ if (cur == ctxt->context->node) return(NULL);
+ if (cur->next != NULL) {
+ cur = cur->next;
+ return(cur);
+ }
+ } while (cur != NULL);
+ return(cur);
+}
+
+/**
+ * xmlXPathNextParent:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "parent" direction
+ * The parent axis contains the parent of the context node, if there is one.
+ */
+xmlNodePtr
+xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ /*
+ * !!!!!!!!!!!!!
+ * the parent of an attribute or namespace node is the element
+ * to which the attribute or namespace node is attached
+ */
+ if (cur == NULL)
+ return(ctxt->context->node->parent);
+ return(NULL);
+}
+
+/**
+ * xmlXPathNextAncestor:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "ancestor" direction
+ * the ancestor axis contains the ancestors of the context node; the ancestors
+ * of the context node consist of the parent of context node and the parent's
+ * parent and so on; the nodes are ordered in reverse document order; thus the
+ * parent is the first node on the axis, and the parent's parent is the second
+ * node on the axis
+ */
+xmlNodePtr
+xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ /*
+ * !!!!!!!!!!!!!
+ * the parent of an attribute or namespace node is the element
+ * to which the attribute or namespace node is attached
+ */
+ if (cur == NULL)
+ return(ctxt->context->node->parent);
+ if (cur == ctxt->context->doc->root) return(NULL);
+ return(cur->parent);
+}
+
+/**
+ * xmlXPathNextAncestorOrSelf:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "ancestor-or-self" direction
+ * he ancestor-or-self axis contains the context node and ancestors of the context
+ * node in reverse document order; thus the context node is the first node on the
+ * axis, and the context node's parent the second; parent here is defined the same
+ * as with the parent axis.
+ */
+xmlNodePtr
+xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ /*
+ * !!!!!!!!!!!!!
+ * the parent of an attribute or namespace node is the element
+ * to which the attribute or namespace node is attached
+ */
+ if (cur == NULL)
+ return(ctxt->context->node);
+ if (cur == ctxt->context->doc->root) return(NULL);
+ return(cur->parent);
+}
+
+/**
+ * xmlXPathNextFollowingSibling:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "following-sibling" direction
+ * The following-sibling axis contains the following siblings of the context
+ * node in document order.
+ */
+xmlNodePtr
+xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node->next);
+ return(cur->next);
+}
+
+/**
+ * xmlXPathNextPrecedingSibling:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "preceding-sibling" direction
+ * The preceding-sibling axis contains the preceding siblings of the context
+ * node in reverse document order; the first preceding sibling is first on the
+ * axis; the sibling preceding that node is the second on the axis and so on.
+ */
+xmlNodePtr
+xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node->prev);
+ return(cur->prev);
+}
+
+/**
+ * xmlXPathNextFollowing:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "following" direction
+ * The following axis contains all nodes in the same document as the context
+ * node that are after the context node in document order, excluding any
+ * descendants and excluding attribute nodes and namespace nodes; the nodes
+ * are ordered in document order
+ */
+xmlNodePtr
+xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node->next);; /* !!!!!!!!! */
+ if (cur->childs != NULL) return(cur->childs);
+ if (cur->next != NULL) return(cur->next);
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL) return(NULL);
+ if (cur == ctxt->context->doc->root) return(NULL);
+ if (cur->next != NULL) {
+ cur = cur->next;
+ return(cur);
+ }
+ } while (cur != NULL);
+ return(cur);
+}
+
+/**
+ * xmlXPathNextPreceding:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "preceding" direction
+ * the preceding axis contains all nodes in the same document as the context
+ * node that are before the context node in document order, excluding any
+ * ancestors and excluding attribute nodes and namespace nodes; the nodes are
+ * ordered in reverse document order
+ */
+xmlNodePtr
+xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node->prev); /* !!!!!!!!! */
+ if (cur->last != NULL) return(cur->last);
+ if (cur->prev != NULL) return(cur->prev);
+
+ do {
+ cur = cur->parent;
+ if (cur == NULL) return(NULL);
+ if (cur == ctxt->context->doc->root) return(NULL);
+ if (cur->prev != NULL) {
+ cur = cur->prev;
+ return(cur);
+ }
+ } while (cur != NULL);
+ return(cur);
+}
+
+/**
+ * xmlXPathNextNamespace:
+ * @ctxt: the XPath Parser context
+ * @cur: the current attribute in the traversal
+ *
+ * Traversal function for the "namespace" direction
+ * the namespace axis contains the namespace nodes of the context node;
+ * the order of nodes on this axis is implementation-defined; the axis will
+ * be empty unless the context node is an element
+ */
+xmlAttrPtr
+xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
+ TODO /* namespace traversal */
+ return(NULL);
+}
+
+/**
+ * xmlXPathNextAttribute:
+ * @ctxt: the XPath Parser context
+ * @cur: the current attribute in the traversal
+ *
+ * Traversal function for the "attribute" direction
+ */
+xmlAttrPtr
+xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
+ if (cur == NULL)
+ return(ctxt->context->node->properties);
+ return(cur->next);
+}
+
+/************************************************************************
+ * *
+ * NodeTest Functions *
+ * *
+ ************************************************************************/
+
+#define NODE_TEST_NONE 0
+#define NODE_TEST_TYPE 1
+#define NODE_TEST_PI 2
+#define NODE_TEST_ALL 3
+#define NODE_TEST_NS 4
+#define NODE_TEST_NAME 5
+
+#define NODE_TYPE_COMMENT 50
+#define NODE_TYPE_TEXT 51
+#define NODE_TYPE_PI 52
+#define NODE_TYPE_NODE 53
+
+#define IS_FUNCTION 200
+
+/**
+ * xmlXPathNodeCollectAndTest:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node to test
+ *
+ * This is the function implementing a step: based on the current list
+ * of nodes, it builds up a new list, looking at all nodes under that
+ * axis and selecting them.
+ *
+ * Returns the new NodeSet resulting from the search.
+ */
+xmlNodeSetPtr
+xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, int axis,
+ int test, int type, const CHAR *prefix, const CHAR *name) {
+#ifdef DEBUG_STEP
+ int n = 0, t = 0;
+#endif
+ int i;
+ xmlNodeSetPtr ret;
+ xmlXPathTraversalFunction next = NULL;
+ xmlNodePtr cur = NULL;
+
+ if (ctxt->context->nodelist == NULL) {
+ if (ctxt->context->node == NULL) {
+ fprintf(xmlXPathDebug,
+ "xmlXPathNodeCollectAndTest %s:%d : nodelist and node are NULL\n",
+ __FILE__, __LINE__);
+ return(NULL);
+ }
+ STRANGE
+ return(NULL);
+ }
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "new step : ");
+#endif
+ switch (axis) {
+ case AXIS_ANCESTOR:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'ancestors' ");
+#endif
+ next = xmlXPathNextAncestor; break;
+ case AXIS_ANCESTOR_OR_SELF:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'ancestors-or-self' ");
+#endif
+ next = xmlXPathNextAncestorOrSelf; break;
+ case AXIS_ATTRIBUTE:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'attributes' ");
+#endif
+ TODO /* attribute axis */
+ break;
+ case AXIS_CHILD:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'child' ");
+#endif
+ next = xmlXPathNextChild; break;
+ case AXIS_DESCENDANT:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'descendant' ");
+#endif
+ next = xmlXPathNextDescendant; break;
+ case AXIS_DESCENDANT_OR_SELF:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'descendant-or-self' ");
+#endif
+ next = xmlXPathNextDescendantOrSelf; break;
+ case AXIS_FOLLOWING:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'following' ");
+#endif
+ next = xmlXPathNextFollowing; break;
+ case AXIS_FOLLOWING_SIBLING:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'following-siblings' ");
+#endif
+ next = xmlXPathNextFollowingSibling; break;
+ case AXIS_NAMESPACE:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'namespace' ");
+#endif
+ TODO /* namespace axis */
+ break;
+ case AXIS_PARENT:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'parent' ");
+#endif
+ next = xmlXPathNextParent; break;
+ case AXIS_PRECEDING:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'preceding' ");
+#endif
+ next = xmlXPathNextPreceding; break;
+ case AXIS_PRECEDING_SIBLING:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'preceding-sibling' ");
+#endif
+ next = xmlXPathNextPrecedingSibling; break;
+ case AXIS_SELF:
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "axis 'self' ");
+#endif
+ next = xmlXPathNextSelf; break;
+ }
+ if (next == NULL) return(NULL);
+ ret = xmlXPathNodeSetCreate(NULL);
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, " context contains %d nodes\n",
+ ctxt->context->nodelist->nodeNr);
+ switch (test) {
+ case NODE_TEST_NONE:
+ fprintf(xmlXPathDebug, " seaching for none !!!\n");
+ break;
+ case NODE_TEST_TYPE:
+ fprintf(xmlXPathDebug, " seaching for type %d\n", type);
+ break;
+ case NODE_TEST_PI:
+ fprintf(xmlXPathDebug, " seaching for PI !!!\n");
+ TODO /* PI search */
+ break;
+ case NODE_TEST_ALL:
+ fprintf(xmlXPathDebug, " seaching for *\n");
+ break;
+ case NODE_TEST_NS:
+ fprintf(xmlXPathDebug, " seaching for namespace %s\n",
+ prefix);
+ break;
+ case NODE_TEST_NAME:
+ fprintf(xmlXPathDebug, " seaching for name %s\n", name);
+ if (prefix != NULL)
+ fprintf(xmlXPathDebug, " with namespace %s\n",
+ prefix);
+ break;
+ }
+ fprintf(xmlXPathDebug, "Testing : ");
+#endif
+ for (i = 0;i < ctxt->context->nodelist->nodeNr; i++) {
+ ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
+
+ cur = NULL;
+ do {
+ cur = next(ctxt, cur);
+ if (cur == NULL) break;
+#ifdef DEBUG_STEP
+ t++;
+ fprintf(xmlXPathDebug, " %s", cur->name);
+#endif
+ switch (test) {
+ case NODE_TEST_NONE:
+ STRANGE
+ return(NULL);
+ case NODE_TEST_TYPE:
+ if (cur->type == type) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ xmlXPathNodeSetAdd(ret, cur);
+ }
+ break;
+ case NODE_TEST_PI:
+ TODO /* PI search */
+ break;
+ case NODE_TEST_ALL:
+ if (cur->type == XML_ELEMENT_NODE) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ xmlXPathNodeSetAdd(ret, cur);
+ }
+ break;
+ case NODE_TEST_NS:
+ TODO /* NS search */
+ break;
+ case NODE_TEST_NAME:
+ if (!xmlStrcmp(name, cur->name) &&
+ (((prefix == NULL) ||
+ ((cur->ns != NULL) &&
+ (!xmlStrcmp(prefix, cur->ns->href)))))) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ xmlXPathNodeSetAdd(ret, cur);
+ }
+ break;
+
+ }
+ } while (cur != NULL);
+ }
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug,
+ "\nExamined %d nodes, found %d nodes at that step\n", t, n);
+#endif
+ return(ret);
+}
+
+
+/************************************************************************
+ * *
+ * Implicit tree core function library *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathRoot:
+ * @ctxt: the XPath Parser context
+ *
+ * Initialize the context to the root of the document
+ */
+void
+xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
+ if (ctxt->context->nodelist != NULL)
+ xmlXPathFreeNodeSet(ctxt->context->nodelist);
+ ctxt->context->node = ctxt->context->doc->root;
+ ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->doc->root);
+}
+
+/**
+ * xmlXPathSearchPI:
+ * @ctxt: the XPath Parser context
+ *
+ * Search a processing instruction by name. The name is
+ * in the object on the stack.
+ */
+void
+xmlXPathSearchPI(xmlXPathParserContextPtr ctxt, int nargs) {
+ TODO /* Search PI */
+ CHECK_ARITY(0);
+ CHECK_TYPE(XPATH_NUMBER)
+}
+
+/************************************************************************
+ * *
+ * The explicit core function library *
+ *http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
+ * *
+ ************************************************************************/
+
+
+/**
+ * xmlXPathLastFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the last() XPath function
+ * The last function returns the number of nodes in the context node list.
+ */
+void
+xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ if ((ctxt->context->nodelist == NULL) ||
+ (ctxt->context->node == NULL) ||
+ (ctxt->context->nodelist->nodeNr == 0)) {
+ valuePush(ctxt, xmlXPathNewFloat((float) 0));
+ } else {
+ valuePush(ctxt,
+ xmlXPathNewFloat((float) ctxt->context->nodelist->nodeNr));
+ }
+}
+
+/**
+ * xmlXPathPositionFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the position() XPath function
+ * The position function returns the position of the context node in the
+ * context node list. The first position is 1, and so the last positionr
+ * will be equal to last().
+ */
+void
+xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ int i;
+
+ CHECK_ARITY(0);
+ if ((ctxt->context->nodelist == NULL) ||
+ (ctxt->context->node == NULL) ||
+ (ctxt->context->nodelist->nodeNr == 0)) {
+ valuePush(ctxt, xmlXPathNewFloat((float) 0));
+ }
+ for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) {
+ if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) {
+ valuePush(ctxt, xmlXPathNewFloat((float) i + 1));
+ return;
+ }
+ }
+ valuePush(ctxt, xmlXPathNewFloat((float) 0));
+}
+
+/**
+ * xmlXPathCountFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the count() XPath function
+ */
+void
+xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_NODESET);
+ cur = valuePop(ctxt);
+
+ valuePush(ctxt, xmlXPathNewFloat((float) cur->nodesetval->nodeNr));
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathIdFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the id() XPath function
+ * The id function selects elements by their unique ID
+ * (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
+ * then the result is the union of the result of applying id to the
+ * string value of each of the nodes in the argument node-set. When the
+ * argument to id is of any other type, the argument is converted to a
+ * string as if by a call to the string function; the string is split
+ * into a whitespace-separated list of tokens (whitespace is any sequence
+ * of characters matching the production S); the result is a node-set
+ * containing the elements in the same document as the context node that
+ * have a unique ID equal to any of the tokens in the list.
+ */
+void
+xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(1);
+ TODO /* Write ID/IDREF support in libxml first */
+}
+
+/**
+ * xmlXPathLocalPartFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the local-part() XPath function
+ * The local-part function returns a string containing the local part
+ * of the name of the node in the argument node-set that is first in
+ * document order. If the node-set is empty or the first node has no
+ * name, an empty string is returned. If the argument is omitted it
+ * defaults to the context node.
+ */
+void
+xmlXPathLocalPartFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_NODESET);
+ cur = valuePop(ctxt);
+
+ if (cur->nodesetval->nodeNr == 0) {
+ valuePush(ctxt, xmlXPathNewString(""));
+ } else {
+ int i = 0; /* Should be first in document order !!!!! */
+ valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
+ }
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathNamespaceFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the namespace() XPath function
+ * The namespace function returns a string containing the namespace URI
+ * of the expanded name of the node in the argument node-set that is
+ * first in document order. If the node-set is empty, the first node has
+ * no name, or the expanded name has no namespace URI, an empty string
+ * is returned. If the argument is omitted it defaults to the context node.
+ */
+void
+xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_NODESET);
+ cur = valuePop(ctxt);
+
+ if (cur->nodesetval->nodeNr == 0) {
+ valuePush(ctxt, xmlXPathNewString(""));
+ } else {
+ int i = 0; /* Should be first in document order !!!!! */
+
+ if (cur->nodesetval->nodeTab[i]->ns == NULL)
+ valuePush(ctxt, xmlXPathNewString(""));
+ else
+ valuePush(ctxt, xmlXPathNewString(
+ cur->nodesetval->nodeTab[i]->ns->href));
+ }
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathNameFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the name() XPath function
+ * The name function returns a string containing a QName representing
+ * the name of the node in the argument node-set that is first in documenti
+ * order. The QName must represent the name with respect to the namespace
+ * declarations in effect on the node whose name is being represented.
+ * Typically, this will be the form in which the name occurred in the XML
+ * source. This need not be the case if there are namespace declarations
+ * in effect on the node that associate multiple prefixes with the same
+ * namespace. However, an implementation may include information about
+ * the original prefix in its representation of nodes; in this case, an
+ * implementation can ensure that the returned string is always the same
+ * as the QName used in the XML source. If the argument it omitted it
+ * defaults to the context node.
+ * Libxml keep the original prefix so the "real qualified name" used is
+ * returned.
+ */
+void
+xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_NODESET);
+ cur = valuePop(ctxt);
+
+ if (cur->nodesetval->nodeNr == 0) {
+ valuePush(ctxt, xmlXPathNewString(""));
+ } else {
+ int i = 0; /* Should be first in document order !!!!! */
+
+ if (cur->nodesetval->nodeTab[i]->ns == NULL)
+ valuePush(ctxt, xmlXPathNewString(
+ cur->nodesetval->nodeTab[i]->name));
+
+ else {
+ CHAR name[2000];
+ sprintf(name, "%s:%s", cur->nodesetval->nodeTab[i]->ns->prefix,
+ cur->nodesetval->nodeTab[i]->name);
+ valuePush(ctxt, xmlXPathNewString(name));
+ }
+ }
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathStringFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the string() XPath function
+ * he string function converts an object to a string as follows:
+ * - A node-set is converted to a string by returning the value of
+ * the node in the node-set that is first in document order.
+ * If the node-set is empty, an empty string is returned.
+ * - A number is converted to a string as follows
+ * + NaN is converted to the string NaN
+ * + positive zero is converted to the string 0
+ * + negative zero is converted to the string 0
+ * + positive infinity is converted to the string Infinity
+ * + negative infinity is converted to the string -Infinity
+ * + if the number is an integer, the number is represented in
+ * decimal form as a Number with no decimal point and no leading
+ * zeros, preceded by a minus sign (-) if the number is negative
+ * + otherwise, the number is represented in decimal form as a
+ * Number including a decimal point with at least one digit
+ * before the decimal point and at least one digit after the
+ * decimal point, preceded by a minus sign (-) if the number
+ * is negative; there must be no leading zeros before the decimal
+ * point apart possibly from the one required digit immediatelyi
+ * before the decimal point; beyond the one required digit
+ * after the decimal point there must be as many, but only as
+ * many, more digits as are needed to uniquely distinguish the
+ * number from all other IEEE 754 numeric values.
+ * - The boolean false value is converted to the string false.
+ * The boolean true value is converted to the string true.
+ */
+void
+xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ CHECK_ARITY(1);
+ cur = valuePop(ctxt);
+ if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
+ switch (cur->type) {
+ case XPATH_NODESET:
+ if (cur->nodesetval->nodeNr == 0) {
+ valuePush(ctxt, xmlXPathNewString(""));
+ } else {
+ CHAR *res;
+ int i = 0; /* Should be first in document order !!!!! */
+ res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]);
+ valuePush(ctxt, xmlXPathNewString(res));
+ free(res);
+ }
+ xmlXPathFreeObject(cur);
+ return;
+ case XPATH_STRING:
+ valuePush(ctxt, cur);
+ return;
+ case XPATH_BOOLEAN:
+ if (cur->boolval) valuePush(ctxt, xmlXPathNewString("true"));
+ else valuePush(ctxt, xmlXPathNewString("false"));
+ xmlXPathFreeObject(cur);
+ return;
+ case XPATH_NUMBER: {
+ CHAR buf[100];
+
+ /* NAN, infinity, etc .... !!!!!! */
+ sprintf(buf, "%0g", cur->floatval);
+ valuePush(ctxt, xmlXPathNewString(buf));
+ return;
+ }
+ }
+ STRANGE
+}
+
+/**
+ * xmlXPathStringLengthFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the string-length() XPath function
+ * The string-length returns the number of characters in the string
+ * (see [3.6 Strings]). If the argument is omitted, it defaults to
+ * the context node converted to a string, in other words the value
+ * of the context node.
+ */
+void
+xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+
+ if (nargs == 0) {
+ if (ctxt->context->node == NULL) {
+ valuePush(ctxt, xmlXPathNewFloat(0));
+ } else {
+ CHAR *content;
+
+ content = xmlNodeGetContent(ctxt->context->node);
+ valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
+ free(content);
+ }
+ return;
+ }
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_STRING);
+ cur = valuePop(ctxt);
+ valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
+ xmlXPathFreeObject(cur);
+}
+
+/**
+ * xmlXPathConcatFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the concat() XPath function
+ * The concat function returns the concatenation of its arguments.
+ */
+void
+xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur, new;
+ CHAR *tmp;
+
+ if (nargs < 2) {
+ CHECK_ARITY(2);
+ }
+
+ cur = valuePop(ctxt);
+ if ((cur == NULL) || (cur->type != XPATH_STRING)) {
+ xmlXPathFreeObject(cur);
+ return;
+ }
+ nargs--;
+
+ while (nargs > 0) {
+ new = valuePop(ctxt);
+ if ((new == NULL) || (new->type != XPATH_STRING)) {
+ xmlXPathFreeObject(new);
+ xmlXPathFreeObject(cur);
+ ERROR(XPATH_INVALID_TYPE);
+ }
+ tmp = xmlStrcat(new->stringval, cur->stringval);
+ new->stringval = cur->stringval;
+ cur->stringval = tmp;
+
+ xmlXPathFreeObject(new);
+ nargs--;
+ }
+ valuePush(ctxt, cur);
+}
+
+/**
+ * xmlXPathContainsFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the contains() XPath function
+ * The contains function returns true if the first argument string
+ * contains the second argument string, and otherwise returns false.
+ */
+void
+xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr hay, needle;
+
+ CHECK_ARITY(2);
+ CHECK_TYPE(XPATH_STRING);
+ needle = valuePop(ctxt);
+ hay = valuePop(ctxt);
+ if ((hay == NULL) || (hay->type != XPATH_STRING)) {
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+ ERROR(XPATH_INVALID_TYPE);
+ }
+ if (xmlStrstr(hay->stringval, needle->stringval))
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+ else
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+}
+
+/**
+ * xmlXPathStartsWithFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the starts-with() XPath function
+ * The starts-with function returns true if the first argument string
+ * starts with the second argument string, and otherwise returns false.
+ */
+void
+xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr hay, needle;
+ int n;
+
+ CHECK_ARITY(2);
+ CHECK_TYPE(XPATH_STRING);
+ needle = valuePop(ctxt);
+ hay = valuePop(ctxt);
+ if ((hay == NULL) || (hay->type != XPATH_STRING)) {
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+ ERROR(XPATH_INVALID_TYPE);
+ }
+ n = xmlStrlen(needle->stringval);
+ if (xmlStrncmp(hay->stringval, needle->stringval, n))
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+ else
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+ xmlXPathFreeObject(hay);
+ xmlXPathFreeObject(needle);
+}
+
+/**
+ * xmlXPathSubstringFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the substring() XPath function
+ * The substring function returns the substring of the first argument
+ * starting at the position specified in the second argument with
+ * length specified in the third argument. For example,
+ * substring("12345",2,3) returns "234". If the third argument is not
+ * specified, it returns the substring starting at the position specified
+ * in the second argument and continuing to the end of the string. For
+ * example, substring("12345",2) returns "2345". More precisely, each
+ * character in the string (see [3.6 Strings]) is considered to have a
+ * numeric position: the position of the first character is 1, the position
+ * of the second character is 2 and so on. The returned substring contains
+ * those characters for which the position of the character is greater than
+ * or equal to the second argument and, if the third argument is specified,
+ * less than the sum of the second and third arguments; the comparisons
+ * and addition used for the above follow the standard IEEE 754 rules. Thus:
+ * - substring("12345", 1.5, 2.6) returns "234"
+ * - substring("12345", 0, 3) returns "12"
+ * - substring("12345", 0 div 0, 3) returns ""
+ * - substring("12345", 1, 0 div 0) returns ""
+ * - substring("12345", -42, 1 div 0) returns "12345"
+ * - substring("12345", -1 div 0, 1 div 0) returns ""
+ */
+void
+xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr str, start, len;
+ float le, in;
+ int i, l;
+ CHAR *ret;
+
+ /*
+ * Conformance needs to be checked !!!!!
+ */
+ if (nargs < 2) {
+ CHECK_ARITY(2);
+ }
+ if (nargs > 3) {
+ CHECK_ARITY(3);
+ }
+ if (nargs == 3) {
+ CHECK_TYPE(XPATH_NUMBER);
+ len = valuePop(ctxt);
+ le = len->floatval;
+ xmlXPathFreeObject(len);
+ } else {
+ le = 2000000000;
+ }
+ CHECK_TYPE(XPATH_NUMBER);
+ start = valuePop(ctxt);
+ in = start->floatval;
+ xmlXPathFreeObject(start);
+ CHECK_TYPE(XPATH_STRING);
+ str = valuePop(ctxt);
+ le += in;
+
+ /* integer index of the first char */
+ i = in;
+ if (((float)i) != in) i++;
+
+ /* integer index of the last char */
+ l = le;
+ if (((float)l) != le) l++;
+
+ /* back to a zero based len */
+ i--;
+ l--;
+
+ /* check against the string len */
+ if (l > 1024) {
+ l = xmlStrlen(str->stringval);
+ }
+ if (i < 0) {
+ i = 0;
+ }
+
+ /* number of chars to copy */
+ l -= i;
+
+ ret = xmlStrsub(str->stringval, i, l);
+ if (ret == NULL)
+ valuePush(ctxt, xmlXPathNewString(""));
+ else
+ valuePush(ctxt, xmlXPathNewString(ret));
+ xmlXPathFreeObject(str);
+}
+
+/**
+ * xmlXPathSubstringBeforeFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the substring-before() XPath function
+ * The substring-before function returns the substring of the first
+ * argument string that precedes the first occurrence of the second
+ * argument string in the first argument string, or the empty string
+ * if the first argument string does not contain the second argument
+ * string. For example, substring-before("1999/04/01","/") returns 1999.
+ */
+void
+xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(2);
+ TODO /* substring before */
+}
+
+/**
+ * xmlXPathSubstringAfterFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the substring-after() XPath function
+ * The substring-after function returns the substring of the first
+ * argument string that follows the first occurrence of the second
+ * argument string in the first argument string, or the empty stringi
+ * if the first argument string does not contain the second argument
+ * string. For example, substring-after("1999/04/01","/") returns 04/01,
+ * and substring-after("1999/04/01","19") returns 99/04/01.
+ */
+void
+xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(2);
+ TODO /* substring after */
+}
+
+/**
+ * xmlXPathNormalizeFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the normalize() XPath function
+ * The normalize function returns the argument string with white
+ * space normalized by stripping leading and trailing whitespace
+ * and replacing sequences of whitespace characters by a single
+ * space. Whitespace characters are the same allowed by the S production
+ * in XML. If the argument is omitted, it defaults to the context
+ * node converted to a string, in other words the value of the context node.
+ */
+void
+xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(1);
+ TODO /* normalize isn't as boring as translate, but pretty much */
+}
+
+/**
+ * xmlXPathTranslateFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the translate() XPath function
+ * The translate function returns the first argument string with
+ * occurrences of characters in the second argument string replaced
+ * by the character at the corresponding position in the third argument
+ * string. For example, translate("bar","abc","ABC") returns the string
+ * BAr. If there is a character in the second argument string with no
+ * character at a corresponding position in the third argument string
+ * (because the second argument string is longer than the third argument
+ * string), then occurrences of that character in the first argument
+ * string are removed. For example, translate("--aaa--","abc-","ABC")
+ * returns "AAA". If a character occurs more than once in second
+ * argument string, then the first occurrence determines the replacement
+ * character. If the third argument string is longer than the second
+ * argument string, then excess characters are ignored.
+ */
+void
+xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(3);
+ TODO /* translate is boring, waiting for UTF-8 representation too */
+}
+
+/**
+ * xmlXPathBooleanFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the boolean() XPath function
+ * he boolean function converts its argument to a boolean as follows:
+ * - a number is true if and only if it is neither positive or
+ * negative zero nor NaN
+ * - a node-set is true if and only if it is non-empty
+ * - a string is true if and only if its length is non-zero
+ */
+void
+xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+ int res = 0;
+
+ CHECK_ARITY(1);
+ cur = valuePop(ctxt);
+ if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
+ switch (cur->type) {
+ case XPATH_NODESET:
+ if ((cur->nodesetval == NULL) ||
+ (cur->nodesetval->nodeNr == 0)) res = 0;
+ else
+ res = 1;
+ break;
+ case XPATH_STRING:
+ if ((cur->stringval == NULL) ||
+ (cur->stringval[0] == 0)) res = 0;
+ else
+ res = 1;
+ break;
+ case XPATH_BOOLEAN:
+ valuePush(ctxt, cur);
+ return;
+ case XPATH_NUMBER:
+ if (cur->floatval) res = 1;
+ break;
+ default:
+ STRANGE
+ }
+ xmlXPathFreeObject(cur);
+ valuePush(ctxt, xmlXPathNewBoolean(res));
+}
+
+/**
+ * xmlXPathNotFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the not() XPath function
+ * The not function returns true if its argument is false,
+ * and false otherwise.
+ */
+void
+xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_BOOLEAN);
+ ctxt->value->boolval = ! ctxt->value->boolval;
+}
+
+/**
+ * xmlXPathTrueFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the true() XPath function
+ */
+void
+xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ valuePush(ctxt, xmlXPathNewBoolean(1));
+}
+
+/**
+ * xmlXPathFalseFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the false() XPath function
+ */
+void
+xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ valuePush(ctxt, xmlXPathNewBoolean(0));
+}
+
+/**
+ * xmlXPathLangFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the lang() XPath function
+ * The lang function returns true or false depending on whether the
+ * language of the context node as specified by xml:lang attributes
+ * is the same as or is a sublanguage of the language specified by
+ * the argument string. The language of the context node is determined
+ * by the value of the xml:lang attribute on the context node, or, if
+ * the context node has no xml:lang attribute, by the value of the
+ * xml:lang attribute on the nearest ancestor of the context node that
+ * has an xml:lang attribute. If there is no such attribute, then lang
+ * returns false. If there is such an attribute, then lang returns
+ * true if the attribute value is equal to the argument ignoring case,
+ * or if there is some suffix starting with - such that the attribute
+ * value is equal to the argument ignoring that suffix of the attribute
+ * value and ignoring case.
+ */
+void
+xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(0);
+ TODO /* Write down xml:lang support in libxml first */
+}
+
+/**
+ * xmlXPathNumberFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the number() XPath function
+ */
+void
+xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ xmlXPathObjectPtr cur;
+ float res;
+
+ CHECK_ARITY(1);
+ cur = valuePop(ctxt);
+ switch (cur->type) {
+ case XPATH_NODESET:
+ valuePush(ctxt, cur);
+ xmlXPathStringFunction(ctxt, 1);
+ cur = valuePop(ctxt);
+ case XPATH_STRING:
+ res = xmlXPathStringEvalNumber(cur->stringval);
+ valuePush(ctxt, xmlXPathNewFloat(res));
+ xmlXPathFreeObject(cur);
+ return;
+ case XPATH_BOOLEAN:
+ if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0));
+ else valuePush(ctxt, xmlXPathNewFloat(0.0));
+ xmlXPathFreeObject(cur);
+ return;
+ case XPATH_NUMBER:
+ valuePush(ctxt, cur);
+ return;
+ }
+ STRANGE
+}
+
+/**
+ * xmlXPathSumFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the sum() XPath function
+ * The sum function returns the sum of the values of the nodes in
+ * the argument node-set.
+ */
+void
+xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(1);
+ TODO /* BUG Sum : don't understand the definition */
+}
+
+/**
+ * xmlXPathFloorFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the floor() XPath function
+ * The floor function returns the largest (closest to positive infinity)
+ * number that is not greater than the argument and that is an integer.
+ */
+void
+xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_NUMBER);
+ /* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
+ ctxt->value->floatval = (float)((int) ctxt->value->floatval);
+}
+
+/**
+ * xmlXPathCeilingFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the ceiling() XPath function
+ * The ceiling function returns the smallest (closest to negative infinity)
+ * number that is not less than the argument and that is an integer.
+ */
+void
+xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ float f;
+
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_NUMBER);
+ f = (float)((int) ctxt->value->floatval);
+ if (f != ctxt->value->floatval)
+ ctxt->value->floatval = f + 1;
+}
+
+/**
+ * xmlXPathRoundFunction:
+ * @ctxt: the XPath Parser context
+ *
+ * Implement the round() XPath function
+ * The round function returns the number that is closest to the
+ * argument and that is an integer. If there are two such numbers,
+ * then the one that is even is returned.
+ */
+void
+xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ float f;
+
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_NUMBER);
+ /* round(0.50000001) => 0 !!!!! */
+ f = (float)((int) ctxt->value->floatval);
+ if (ctxt->value->floatval < f + 0.5)
+ ctxt->value->floatval = f;
+ else if (ctxt->value->floatval == f + 0.5)
+ ctxt->value->floatval = f; /* !!!! Not following the spec here */
+ else
+ ctxt->value->floatval = f + 1;
+}
+
+/************************************************************************
+ * *
+ * The Parser *
+ * *
+ ************************************************************************/
+
+/*
+ * a couple of forward declarations since we use a recursive call based
+ * implementation.
+ */
+void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt);
+void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt);
+void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt);
+void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt);
+
+/**
+ * xmlXPathParseNCName:
+ * @ctxt: the XPath Parser context
+ *
+ * parse an XML namespace non qualified name.
+ *
+ * [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
+ *
+ * [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
+ * CombiningChar | Extender
+ *
+ * Returns the namespace name or NULL
+ */
+
+CHAR *
+xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
+ const CHAR *q;
+ CHAR *ret = NULL;
+
+ if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
+ q = NEXT;
+
+ while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
+ (CUR == '.') || (CUR == '-') ||
+ (CUR == '_') ||
+ (IS_COMBINING(CUR)) ||
+ (IS_EXTENDER(CUR)))
+ NEXT;
+
+ ret = xmlStrndup(q, CUR_PTR - q);
+
+ return(ret);
+}
+
+/**
+ * xmlXPathParseQName:
+ * @ctxt: the XPath Parser context
+ * @prefix: a CHAR **
+ *
+ * parse an XML qualified name
+ *
+ * [NS 5] QName ::= (Prefix ':')? LocalPart
+ *
+ * [NS 6] Prefix ::= NCName
+ *
+ * [NS 7] LocalPart ::= NCName
+ *
+ * Returns the function returns the local part, and prefix is updated
+ * to get the Prefix if any.
+ */
+
+CHAR *
+xmlXPathParseQName(xmlXPathParserContextPtr ctxt, CHAR **prefix) {
+ CHAR *ret = NULL;
+
+ *prefix = NULL;
+ ret = xmlXPathParseNCName(ctxt);
+ if (CUR == ':') {
+ *prefix = ret;
+ NEXT;
+ ret = xmlXPathParseNCName(ctxt);
+ }
+ return(ret);
+}
+
+/**
+ * xmlXPathStringEvalNumber:
+ * @str: A string to scan
+ *
+ * [30] Number ::= Digits ('.' Digits)?
+ * | '.' Digits
+ * [31] Digits ::= [0-9]+
+ *
+ * Parse and evaluate a Number in the string
+ *
+ * BUG: "1.' is not valid ... James promised correction
+ * as Digits ('.' Digits?)?
+ *
+ * Returns the float value.
+ */
+float
+xmlXPathStringEvalNumber(const CHAR *str) {
+ const CHAR *cur = str;
+ float ret = 0.0;
+ float mult = 1;
+ int ok = 0;
+
+ while (*cur == ' ') cur++;
+ if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) {
+ return(NAN);
+ }
+ while ((*cur >= '0') && (*cur <= '9')) {
+ ret = ret * 10 + (*cur - '0');
+ ok = 1;
+ cur++;
+ }
+ if (*cur == '.') {
+ cur++;
+ if (((*cur < '0') || (*cur > '9')) && (!ok)) {
+ return(NAN);
+ }
+ while ((*cur >= '0') && (*cur <= '9')) {
+ mult /= 10;
+ ret = ret + (*cur - '0') * mult;
+ cur++;
+ }
+ }
+ while (*cur == ' ') cur++;
+ if (*cur != 0) return(NAN);
+ return(ret);
+}
+
+/**
+ * xmlXPathEvalNumber:
+ * @ctxt: the XPath Parser context
+ *
+ * [30] Number ::= Digits ('.' Digits)?
+ * | '.' Digits
+ * [31] Digits ::= [0-9]+
+ *
+ * Parse and evaluate a Number, then push it on the stack
+ *
+ * BUG: "1.' is not valid ... James promised correction
+ * as Digits ('.' Digits?)?
+ */
+void
+xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) {
+ float ret = 0.0;
+ float mult = 1;
+ int ok = 0;
+
+ CHECK_ERROR;
+ if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
+ ERROR(XPATH_NUMBER_ERROR);
+ }
+ while ((CUR >= '0') && (CUR <= '9')) {
+ ret = ret * 10 + (CUR - '0');
+ ok = 1;
+ NEXT;
+ }
+ if (CUR == '.') {
+ NEXT;
+ if (((CUR < '0') || (CUR > '9')) && (!ok)) {
+ ERROR(XPATH_NUMBER_ERROR);
+ }
+ while ((CUR >= '0') && (CUR <= '9')) {
+ mult /= 10;
+ ret = ret + (CUR - '0') * mult;
+ NEXT;
+ }
+ }
+ valuePush(ctxt, xmlXPathNewFloat(ret));
+}
+
+/**
+ * xmlXPathEvalLiteral:
+ * @ctxt: the XPath Parser context
+ *
+ * Parse a Literal and push it on the stack.
+ *
+ * [29] Literal ::= '"' [^"]* '"'
+ * | "'" [^']* "'"
+ *
+ * TODO: memory allocation could be improved.
+ */
+void
+xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
+ const CHAR *q;
+ CHAR *ret = NULL;
+
+ if (CUR == '"') {
+ NEXT;
+ q = CUR_PTR;
+ while ((IS_CHAR(CUR)) && (CUR != '"'))
+ NEXT;
+ if (!IS_CHAR(CUR)) {
+ ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
+ } else {
+ ret = xmlStrndup(q, CUR_PTR - q);
+ NEXT;
+ }
+ } else if (CUR == '\'') {
+ NEXT;
+ q = CUR_PTR;
+ while ((IS_CHAR(CUR)) && (CUR != '\''))
+ NEXT;
+ if (!IS_CHAR(CUR)) {
+ ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
+ } else {
+ ret = xmlStrndup(q, CUR_PTR - q);
+ NEXT;
+ }
+ } else {
+ ERROR(XPATH_START_LITERAL_ERROR);
+ }
+ if (ret == NULL) return;
+ valuePush(ctxt, xmlXPathNewString(ret));
+ free(ret);
+}
+
+/**
+ * xmlXPathEvalVariableReference:
+ * @ctxt: the XPath Parser context
+ *
+ * Parse a VariableReference, evaluate it and push it on the stack.
+ *
+ * The variable bindings consist of a mapping from variable names
+ * to variable values. The value of a variable is an object, which
+ * of any of the types that are possible for the value of an expression,
+ * and may also be of additional types not specified here.
+ *
+ * Early evaluation is possible since:
+ * The variable bindings [...] used to evaluate a subexpression are
+ * always the same as those used to evaluate the containing expression.
+ *
+ * [36] VariableReference ::= '$' QName
+ */
+void
+xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) {
+ CHAR *name;
+ CHAR *prefix;
+ xmlXPathObjectPtr value;
+
+ if (CUR != '$') {
+ ERROR(XPATH_VARIABLE_REF_ERROR);
+ }
+ name = xmlXPathParseQName(ctxt, &prefix);
+ if (name == NULL) {
+ ERROR(XPATH_VARIABLE_REF_ERROR);
+ }
+ value = xmlXPathVariablelookup(ctxt, prefix, name);
+ if (value == NULL) {
+ ERROR(XPATH_UNDEF_VARIABLE_ERROR);
+ }
+ valuePush(ctxt, value);
+ if (prefix != NULL) free(prefix);
+ free(name);
+}
+
+
+/**
+ * xmlXPathFunctionLookup:
+ * @ctxt: the XPath Parser context
+ * @name: a name string
+ *
+ * Search for a function of the given name
+ *
+ * [35] FunctionName ::= QName - NodeType
+ *
+ * TODO: for the moment the list is hardcoded from the spec !!!!
+ *
+ * Returns the xmlXPathFunction if found, or NULL otherwise
+ */
+xmlXPathFunction
+xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, CHAR *name) {
+ switch (name[0]) {
+ case 'b':
+ if (!xmlStrcmp(name, "boolean"))
+ return(xmlXPathBooleanFunction);
+ break;
+ case 'c':
+ if (!xmlStrcmp(name, "ceiling"))
+ return(xmlXPathCeilingFunction);
+ if (!xmlStrcmp(name, "count"))
+ return(xmlXPathCountFunction);
+ if (!xmlStrcmp(name, "concat"))
+ return(xmlXPathConcatFunction);
+ if (!xmlStrcmp(name, "contains"))
+ return(xmlXPathContainsFunction);
+ break;
+ case 'i':
+ if (!xmlStrcmp(name, "id"))
+ return(xmlXPathIdFunction);
+ break;
+ case 'f':
+ if (!xmlStrcmp(name, "false"))
+ return(xmlXPathFalseFunction);
+ if (!xmlStrcmp(name, "floor"))
+ return(xmlXPathFloorFunction);
+ break;
+ case 'l':
+ if (!xmlStrcmp(name, "last"))
+ return(xmlXPathLastFunction);
+ if (!xmlStrcmp(name, "lang"))
+ return(xmlXPathLangFunction);
+ if (!xmlStrcmp(name, "local-part"))
+ return(xmlXPathLocalPartFunction);
+ break;
+ case 'n':
+ if (!xmlStrcmp(name, "not"))
+ return(xmlXPathNotFunction);
+ if (!xmlStrcmp(name, "name"))
+ return(xmlXPathNameFunction);
+ if (!xmlStrcmp(name, "namespace"))
+ return(xmlXPathNamespaceFunction);
+ if (!xmlStrcmp(name, "normalize"))
+ return(xmlXPathNormalizeFunction);
+ if (!xmlStrcmp(name, "number"))
+ return(xmlXPathNumberFunction);
+ break;
+ case 'p':
+ if (!xmlStrcmp(name, "position"))
+ return(xmlXPathPositionFunction);
+ break;
+ case 'r':
+ if (!xmlStrcmp(name, "round"))
+ return(xmlXPathRoundFunction);
+ break;
+ case 's':
+ if (!xmlStrcmp(name, "string"))
+ return(xmlXPathStringFunction);
+ if (!xmlStrcmp(name, "string-length"))
+ return(xmlXPathStringLengthFunction);
+ if (!xmlStrcmp(name, "starts-with"))
+ return(xmlXPathStartsWithFunction);
+ if (!xmlStrcmp(name, "substring"))
+ return(xmlXPathSubstringFunction);
+ if (!xmlStrcmp(name, "substring-before"))
+ return(xmlXPathSubstringBeforeFunction);
+ if (!xmlStrcmp(name, "substring-after"))
+ return(xmlXPathSubstringAfterFunction);
+ if (!xmlStrcmp(name, "sum"))
+ return(xmlXPathSumFunction);
+ break;
+ case 't':
+ if (!xmlStrcmp(name, "true"))
+ return(xmlXPathTrueFunction);
+ if (!xmlStrcmp(name, "translate"))
+ return(xmlXPathTranslateFunction);
+ break;
+ }
+ return(NULL);
+}
+
+/**
+ * xmlXPathEvalLocationPathName:
+ * @ctxt: the XPath Parser context
+ * @name: a name string
+ *
+ * Various names in the beginning of a LocationPath expression
+ * indicate whether that's an Axis, a node type,
+ *
+ * [6] AxisName ::= 'ancestor'
+ * | 'ancestor-or-self'
+ * | 'attribute'
+ * | 'child'
+ * | 'descendant'
+ * | 'descendant-or-self'
+ * | 'following'
+ * | 'following-sibling'
+ * | 'namespace'
+ * | 'parent'
+ * | 'preceding'
+ * | 'preceding-sibling'
+ * | 'self'
+ * [38] NodeType ::= 'comment'
+ * | 'text'
+ * | 'processing-instruction'
+ * | 'node'
+ */
+int
+xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, CHAR *name) {
+ switch (name[0]) {
+ case 'a':
+ if (!xmlStrcmp(name, "ancestor")) return(AXIS_ANCESTOR);
+ if (!xmlStrcmp(name, "ancestor-or-self")) return(AXIS_ANCESTOR_OR_SELF);
+ if (!xmlStrcmp(name, "attribute")) return(AXIS_ATTRIBUTE);
+ break;
+ case 'c':
+ if (!xmlStrcmp(name, "child")) return(AXIS_CHILD);
+ if (!xmlStrcmp(name, "comment")) return(NODE_TYPE_COMMENT);
+ break;
+ case 'd':
+ if (!xmlStrcmp(name, "descendant")) return(AXIS_DESCENDANT);
+ if (!xmlStrcmp(name, "descendant-or-self")) return(AXIS_DESCENDANT_OR_SELF);
+ break;
+ case 'f':
+ if (!xmlStrcmp(name, "following")) return(AXIS_FOLLOWING);
+ if (!xmlStrcmp(name, "following-sibling")) return(AXIS_FOLLOWING_SIBLING);
+ break;
+ case 'n':
+ if (!xmlStrcmp(name, "namespace")) return(AXIS_NAMESPACE);
+ if (!xmlStrcmp(name, "node")) return(NODE_TYPE_NODE);
+ break;
+ case 'p':
+ if (!xmlStrcmp(name, "parent")) return(AXIS_PARENT);
+ if (!xmlStrcmp(name, "preceding")) return(AXIS_PRECEDING);
+ if (!xmlStrcmp(name, "preceding-sibling")) return(AXIS_PRECEDING_SIBLING);
+ if (!xmlStrcmp(name, "processing-instruction")) return(NODE_TYPE_PI);
+ break;
+ case 's':
+ if (!xmlStrcmp(name, "self")) return(AXIS_SELF);
+ break;
+ case 't':
+ if (!xmlStrcmp(name, "text")) return(NODE_TYPE_TEXT);
+ break;
+ }
+ if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION);
+ return(0);
+}
+
+/**
+ * xmlXPathEvalFunctionCall:
+ * @ctxt: the XPath Parser context
+ *
+ * [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
+ * [17] Argument ::= Expr
+ *
+ * Parse and evaluate a function call, the evaluation of all arguments are
+ * pushed on the stack
+ */
+void
+xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) {
+ CHAR *name;
+ CHAR *prefix;
+ xmlXPathFunction func;
+ int nbargs = 0;
+
+ name = xmlXPathParseQName(ctxt, &prefix);
+ if (name == NULL) {
+ ERROR(XPATH_EXPR_ERROR);
+ }
+ func = xmlXPathIsFunction(ctxt, name);
+ if (func == NULL) {
+ ERROR(XPATH_UNKNOWN_FUNC_ERROR);
+ }
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Calling function %s\n", name);
+#endif
+
+ if (CUR != '(') {
+ ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+ valuePush(ctxt, xmlXPathNewMarker());
+
+ while (CUR != ')') {
+ xmlXPathEvalExpr(ctxt);
+ nbargs++;
+ if (CUR == ')') break;
+ if (CUR != ',') {
+ ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+ }
+ NEXT;
+ func(ctxt, nbargs);
+}
+
+/**
+ * xmlXPathEvalPrimaryExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [15] PrimaryExpr ::= VariableReference
+ * | '(' Expr ')'
+ * | Literal
+ * | Number
+ * | FunctionCall
+ *
+ * Parse and evaluate a primary expression, then push the result on the stack
+ */
+void
+xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) {
+ if (CUR == '$') xmlXPathEvalVariableReference(ctxt);
+ else if (CUR == '(') {
+ NEXT;
+ xmlXPathEvalExpr(ctxt);
+ if (CUR != ')') {
+ ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+ } else if (IS_DIGIT(CUR)) {
+ xmlXPathEvalNumber(ctxt);
+ } else if ((CUR == '\'') || (CUR == '"')) {
+ xmlXPathEvalLiteral(ctxt);
+ } else {
+ xmlXPathEvalFunctionCall(ctxt);
+ }
+}
+
+/**
+ * xmlXPathEvalFilterExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [20] FilterExpr ::= PrimaryExpr
+ * | FilterExpr Predicate
+ *
+ * Parse and evaluate a filter expression, then push the result on the stack
+ * Square brackets are used to filter expressions in the same way that
+ * they are used in location paths. It is an error if the expression to
+ * be filtered does not evaluate to a node-set. The context node list
+ * used for evaluating the expression in square brackets is the node-set
+ * to be filtered listed in document order.
+ */
+
+void
+xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) {
+ /****
+ xmlNodeSetPtr oldset = NULL;
+ xmlXPathObjectPtr arg;
+ ****/
+
+ xmlXPathEvalPrimaryExpr(ctxt);
+ CHECK_ERROR;
+
+ if (CUR != '[') return;
+
+ CHECK_TYPE(XPATH_NODESET);
+
+ /******
+ TODO: transition from PathExpr to Expr using paths ...
+ arg = valuePop(ctxt);
+ oldset = ctxt->context->nodeset;
+ ctxt->context->nodeset = arg->nodesetval;
+ ******/
+
+ while (CUR == '[') {
+ xmlXPathEvalPredicate(ctxt);
+ }
+
+
+}
+
+/**
+ * xmlXPathEvalPathExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [19] PathExpr ::= LocationPath
+ * | FilterExpr
+ * | FilterExpr '/' RelativeLocationPath
+ * | FilterExpr '//' RelativeLocationPath
+ *
+ * Parse and evaluate a path expression, then push the result on the stack
+ * The / operator and // operators combine an arbitrary expression
+ * and a relative location path. It is an error if the expression
+ * does not evaluate to a node-set.
+ * The / operator does composition in the same way as when / is
+ * used in a location path. As in location paths, // is short for
+ * /descendant-or-self::node()/.
+ */
+
+void
+xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
+ xmlNodeSetPtr newset = NULL;
+
+ /*
+ * TODO: FilterExpr => PrimaryExpr => FunctionName is possible too
+ * so we may have to parse a name here, and depending on whether
+ * it's a function name or not parse a FilterExpr or a LocationPath
+ * !!!!!!!!!!!! See NOTE1
+ */
+
+ if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
+ (CUR == '\'') || (CUR == '"')) {
+ xmlXPathEvalFilterExpr(ctxt);
+ CHECK_ERROR;
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ SKIP(2);
+ if (ctxt->context->nodelist == NULL) {
+ STRANGE
+ xmlXPathRoot(ctxt);
+ }
+ newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
+ NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
+ if (ctxt->context->nodelist != NULL)
+ xmlXPathFreeNodeSet(ctxt->context->nodelist);
+ ctxt->context->nodelist = newset;
+ ctxt->context->node = NULL;
+ xmlXPathEvalRelativeLocationPath(ctxt);
+ } else if (CUR == '/') {
+ xmlXPathEvalRelativeLocationPath(ctxt);
+ }
+ } else {
+ xmlXPathEvalLocationPath(ctxt);
+ }
+}
+
+/**
+ * xmlXPathEvalUnionExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [18] UnionExpr ::= PathExpr
+ * | UnionExpr '|' PathExpr
+ *
+ * Parse and evaluate an union expression, then push the result on the stack
+ */
+
+void
+xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathEvalPathExpr(ctxt);
+ CHECK_ERROR;
+ if (CUR == '|') {
+ xmlNodeSetPtr old = ctxt->context->nodelist;
+
+ xmlXPathEvalPathExpr(ctxt);
+
+ if (ctxt->context->nodelist == NULL)
+ ctxt->context->nodelist = old;
+ else {
+ ctxt->context->nodelist =
+ xmlXPathNodeSetMerge(ctxt->context->nodelist, old);
+ xmlXPathFreeNodeSet(old);
+ }
+ }
+}
+
+/**
+ * xmlXPathEvalUnaryExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [27] UnaryExpr ::= UnionExpr
+ * | '-' UnaryExpr
+ *
+ * Parse and evaluate an unary expression, then push the result on the stack
+ */
+
+void
+xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) {
+ int minus = 0;
+
+ if (CUR == '-') {
+ minus = 1;
+ NEXT;
+ }
+ /* xmlXPathEvalUnionExpr(ctxt); NOTE1 !!! */
+ xmlXPathEvalPrimaryExpr(ctxt);
+ CHECK_ERROR;
+ if (minus) {
+ xmlXPathValueFlipSign(ctxt);
+ }
+}
+
+/**
+ * xmlXPathEvalMultiplicativeExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [26] MultiplicativeExpr ::= UnaryExpr
+ * | MultiplicativeExpr MultiplyOperator UnaryExpr
+ * | MultiplicativeExpr 'div' UnaryExpr
+ * | MultiplicativeExpr 'mod' UnaryExpr
+ * [34] MultiplyOperator ::= '*'
+ *
+ * Parse and evaluate an Additive expression, then push the result on the stack
+ */
+
+void
+xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathEvalUnaryExpr(ctxt);
+ CHECK_ERROR;
+ while ((CUR == '*') ||
+ ((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
+ ((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
+ int op = -1;
+
+ if (CUR == '*') {
+ op = 0;
+ NEXT;
+ } else if (CUR == 'd') {
+ op = 1;
+ SKIP(3);
+ } else if (CUR == 'm') {
+ op = 2;
+ SKIP(3);
+ }
+ xmlXPathEvalUnaryExpr(ctxt);
+ CHECK_ERROR;
+ switch (op) {
+ case 0:
+ xmlXPathMultValues(ctxt);
+ break;
+ case 1:
+ xmlXPathDivValues(ctxt);
+ break;
+ case 2:
+ xmlXPathModValues(ctxt);
+ break;
+ }
+ }
+}
+
+/**
+ * xmlXPathEvalAdditiveExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [25] AdditiveExpr ::= MultiplicativeExpr
+ * | AdditiveExpr '+' MultiplicativeExpr
+ * | AdditiveExpr '-' MultiplicativeExpr
+ *
+ * Parse and evaluate an Additive expression, then push the result on the stack
+ */
+
+void
+xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathEvalMultiplicativeExpr(ctxt);
+ CHECK_ERROR;
+ while ((CUR == '+') || (CUR == '-')) {
+ int plus;
+
+ if (CUR == '+') plus = 1;
+ else plus = 0;
+ NEXT;
+ xmlXPathEvalMultiplicativeExpr(ctxt);
+ CHECK_ERROR;
+ if (plus) xmlXPathAddValues(ctxt);
+ else xmlXPathSubValues(ctxt);
+ }
+}
+
+/**
+ * xmlXPathEvalRelationalExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [24] RelationalExpr ::= AdditiveExpr
+ * | RelationalExpr '<' AdditiveExpr
+ * | RelationalExpr '>' AdditiveExpr
+ * | RelationalExpr '<=' AdditiveExpr
+ * | RelationalExpr '>=' AdditiveExpr
+ *
+ * A <= B > C is allowed ? Answer from James, yes with
+ * (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
+ * which is basically what got implemented.
+ *
+ * Parse and evaluate a Relational expression, then push the result
+ * on the stack
+ */
+
+void
+xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathEvalAdditiveExpr(ctxt);
+ CHECK_ERROR;
+ while ((CUR == '<') ||
+ (CUR == '>') ||
+ ((CUR == '<') && (NXT(1) == '=')) ||
+ ((CUR == '>') && (NXT(1) == '='))) {
+ xmlXPathObjectPtr arg1, arg2, res;
+ int inf, strict, equal;
+
+ if (CUR == '<') inf = 1;
+ else inf = 0;
+ if (NXT(1) == '=') strict = 0;
+ else strict = 1;
+ NEXT;
+ if (!strict) NEXT;
+ xmlXPathEvalAdditiveExpr(ctxt);
+ CHECK_ERROR;
+ arg2 = valuePop(ctxt);
+ arg1 = valuePop(ctxt);
+ equal = xmlXPathCompareValues(inf, strict, arg1, arg2);
+ res = xmlXPathNewBoolean(equal);
+ valuePush(ctxt, res);
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ }
+}
+
+/**
+ * xmlXPathEvalEqualityExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [23] EqualityExpr ::= RelationalExpr
+ * | EqualityExpr '=' RelationalExpr
+ * | EqualityExpr '!=' RelationalExpr
+ *
+ * A != B != C is allowed ? Answer from James, yes with
+ * (RelationalExpr = RelationalExpr) = RelationalExpr
+ * (RelationalExpr != RelationalExpr) != RelationalExpr
+ * which is basically what got implemented.
+ *
+ * Parse and evaluate an Equality expression, then push the result on the stack
+ *
+ */
+void
+xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathEvalRelationalExpr(ctxt);
+ CHECK_ERROR;
+ while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
+ xmlXPathObjectPtr arg1, arg2, res;
+ int eq, equal;
+
+ if (CUR == '=') eq = 1;
+ else eq = 0;
+ NEXT;
+ if (!eq) NEXT;
+ xmlXPathEvalRelationalExpr(ctxt);
+ CHECK_ERROR;
+ arg2 = valuePop(ctxt);
+ arg1 = valuePop(ctxt);
+ equal = xmlXPathEqualValues(arg1, arg2);
+ if (eq) res = xmlXPathNewBoolean(equal);
+ else res = xmlXPathNewBoolean(!equal);
+ valuePush(ctxt, res);
+ xmlXPathFreeObject(arg1);
+ xmlXPathFreeObject(arg2);
+ }
+}
+
+/**
+ * xmlXPathEvalAndExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [22] AndExpr ::= EqualityExpr
+ * | AndExpr 'and' EqualityExpr
+ *
+ * Parse and evaluate an AND expression, then push the result on the stack
+ *
+ */
+void
+xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathEvalEqualityExpr(ctxt);
+ CHECK_ERROR;
+ while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'n')) {
+ xmlXPathObjectPtr arg1, arg2;
+
+ SKIP(3);
+ xmlXPathEvalEqualityExpr(ctxt);
+ CHECK_ERROR;
+ arg2 = valuePop(ctxt);
+ arg1 = valuePop(ctxt);
+ arg1->boolval &= arg2->boolval;
+ valuePush(ctxt, arg1);
+ xmlXPathFreeObject(arg2);
+ }
+}
+
+/**
+ * xmlXPathEvalExpr:
+ * @ctxt: the XPath Parser context
+ *
+ * [14] Expr ::= OrExpr
+ * [21] OrExpr ::= AndExpr
+ * | OrExpr 'or' AndExpr
+ *
+ * Parse and evaluate an expression, then push the result on the stack
+ *
+ */
+void
+xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
+ xmlXPathEvalAndExpr(ctxt);
+ CHECK_ERROR;
+ while ((CUR == 'o') && (NXT(1) == 'r')) {
+ xmlXPathObjectPtr arg1, arg2;
+
+ SKIP(2);
+ xmlXPathEvalAndExpr(ctxt);
+ CHECK_ERROR;
+ arg2 = valuePop(ctxt);
+ arg1 = valuePop(ctxt);
+ arg1->boolval |= arg2->boolval;
+ valuePush(ctxt, arg1);
+ xmlXPathFreeObject(arg2);
+ }
+}
+
+/**
+ * xmlXPathEvaluatePredicateResult:
+ * @ctxt: the XPath Parser context
+ * @res: the Predicate Expression evaluation result
+ * @index: index of the current node in the current list
+ *
+ * Evaluate a predicate result for the current node.
+ * A PredicateExpr is evaluated by evaluating the Expr and converting
+ * the result to a boolean. If the result is a number, the result will
+ * be converted to true if the number is equal to the position of the
+ * context node in the context node list (as returned by the position
+ * function) and will be converted to false otherwise; if the result
+ * is not a number, then the result will be converted as if by a call
+ * to the boolean function.
+ */
+int
+xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
+ xmlXPathObjectPtr res, int index) {
+ if (res == NULL) return(0);
+ switch (res->type) {
+ case XPATH_BOOLEAN:
+ return(res->boolval);
+ case XPATH_NUMBER:
+ return(res->floatval == index);
+ case XPATH_NODESET:
+ return(res->nodesetval->nodeNr != 0);
+ case XPATH_STRING:
+ return((res->stringval != NULL) &&
+ (xmlStrlen(res->stringval) != 0));
+ default:
+ STRANGE
+ }
+ return(0);
+}
+
+/**
+ * xmlXPathEvalPredicate:
+ * @ctxt: the XPath Parser context
+ *
+ * [8] Predicate ::= '[' PredicateExpr ']'
+ * [9] PredicateExpr ::= Expr
+ *
+ * Parse and evaluate a predicate for all the elements of the
+ * current node list. Then refine the list by removing all
+ * nodes where the predicate is false.
+ */
+void
+xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) {
+ const CHAR *cur;
+ xmlXPathObjectPtr res;
+ xmlNodeSetPtr newset = NULL;
+ int i;
+
+ if (CUR != '[') {
+ ERROR(XPATH_INVALID_PREDICATE_ERROR);
+ }
+ NEXT;
+ if ((ctxt->context->nodelist == NULL) ||
+ (ctxt->context->nodelist->nodeNr == 0)) {
+ ctxt->context->node = NULL;
+ xmlXPathEvalExpr(ctxt);
+ CHECK_ERROR;
+ res = valuePop(ctxt);
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ } else {
+ cur = ctxt->cur;
+ newset = xmlXPathNodeSetCreate(NULL);
+ for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) {
+ ctxt->cur = cur;
+ ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
+ xmlXPathEvalExpr(ctxt);
+ CHECK_ERROR;
+ res = valuePop(ctxt);
+ if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1))
+ xmlXPathNodeSetAdd(newset,
+ ctxt->context->nodelist->nodeTab[i]);
+ if (res != NULL)
+ xmlXPathFreeObject(res);
+ }
+ if (ctxt->context->nodelist != NULL)
+ xmlXPathFreeNodeSet(ctxt->context->nodelist);
+ ctxt->context->nodelist = newset;
+ ctxt->context->node = NULL;
+ }
+ if (CUR != ']') {
+ ERROR(XPATH_INVALID_PREDICATE_ERROR);
+ }
+ NEXT;
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "After predicate : ");
+ xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
+#endif
+}
+
+/**
+ * xmlXPathEvalBasis:
+ * @ctxt: the XPath Parser context
+ *
+ * [5] Basis ::= AxisName '::' NodeTest
+ * | AbbreviatedBasis
+ * [13] AbbreviatedBasis ::= NodeTest
+ * | '@' NodeTest
+ * [7] NodeTest ::= WildcardName
+ * | NodeType '(' ')'
+ * | 'processing-instruction' '(' Literal ')'
+ * [37] WildcardName ::= '*'
+ * | NCName ':' '*'
+ * | QName
+ *
+ * Evaluate one step in a Location Path
+ */
+void
+xmlXPathEvalBasis(xmlXPathParserContextPtr ctxt) {
+ CHAR *name = NULL;
+ CHAR *prefix = NULL;
+ int type = 0;
+ int axis = AXIS_CHILD; /* the default on abbreviated syntax */
+ int nodetest = NODE_TEST_NONE;
+ int nodetype = 0;
+ xmlNodeSetPtr newset = NULL;
+
+ if (CUR == '@') {
+ TODO /* attributes */
+ } else if (CUR == '*') {
+ NEXT;
+ nodetest = NODE_TEST_ALL;
+ } else {
+ name = xmlXPathParseNCName(ctxt);
+ if (name == NULL) {
+ ERROR(XPATH_EXPR_ERROR);
+ }
+ type = xmlXPathGetNameType(ctxt, name);
+ switch (type) {
+ /*
+ * Simple case: no axis seach all given node types.
+ */
+ case NODE_TYPE_COMMENT:
+ if ((CUR != '(') || (NXT(1) != ')')) break;
+ SKIP(2);
+ nodetest = NODE_TEST_TYPE;
+ nodetype = XML_COMMENT_NODE;
+ goto search_nodes;
+ case NODE_TYPE_TEXT:
+ if ((CUR != '(') || (NXT(1) != ')')) break;
+ SKIP(2);
+ nodetest = NODE_TEST_TYPE;
+ nodetype = XML_TEXT_NODE;
+ goto search_nodes;
+ case NODE_TYPE_NODE:
+ if ((CUR != '(') || (NXT(1) != ')')) {
+ nodetest = NODE_TEST_NAME;
+ break;
+ }
+ SKIP(2);
+ nodetest = NODE_TEST_TYPE;
+ nodetype = XML_ELEMENT_NODE;
+ goto search_nodes;
+ case NODE_TYPE_PI:
+ if (CUR != '(') break;
+ if (NXT(1) != ')') {
+ /*
+ * Specific case: search a PI by name.
+ */
+ SKIP(2);
+ nodetest = NODE_TEST_PI;
+ xmlXPathEvalLiteral(ctxt);
+ CHECK_ERROR;
+ if (CUR != ')')
+ ERROR(XPATH_UNCLOSED_ERROR);
+ xmlXPathSearchPI(ctxt, 0);
+ return;
+ }
+ SKIP(2);
+ nodetest = NODE_TEST_TYPE;
+ nodetype = XML_PI_NODE;
+ goto search_nodes;
+
+ /*
+ * Handling of the compund form: got the axis.
+ */
+ case AXIS_ANCESTOR:
+ case AXIS_ANCESTOR_OR_SELF:
+ case AXIS_ATTRIBUTE:
+ case AXIS_CHILD:
+ case AXIS_DESCENDANT:
+ case AXIS_DESCENDANT_OR_SELF:
+ case AXIS_FOLLOWING:
+ case AXIS_FOLLOWING_SIBLING:
+ case AXIS_NAMESPACE:
+ case AXIS_PARENT:
+ case AXIS_PRECEDING:
+ case AXIS_PRECEDING_SIBLING:
+ case AXIS_SELF:
+ if ((CUR != ':') || (NXT(1) != ':')) {
+ nodetest = NODE_TEST_NAME;
+ break;
+ }
+ SKIP(2);
+ axis = type;
+ break;
+
+ /*
+ * Default: abbreviated syntax the axis is AXIS_CHILD
+ */
+ default:
+ nodetest = NODE_TEST_NAME;
+ }
+ if (nodetest == NODE_TEST_NONE) {
+ if (CUR == '*') {
+ NEXT;
+ nodetest = NODE_TEST_ALL;
+ } else {
+ name = xmlXPathParseQName(ctxt, &prefix);
+ nodetest = NODE_TEST_NAME;
+ }
+ } else if ((CUR == ':') && (nodetest == NODE_TEST_NAME)) {
+ NEXT;
+ prefix = name;
+ if (CUR == '*') {
+ NEXT;
+ nodetest = NODE_TEST_ALL;
+ } else
+ name = xmlXPathParseNCName(ctxt);
+ } else if (name == NULL)
+ ERROR(XPATH_EXPR_ERROR);
+ }
+
+search_nodes:
+
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "Basis : computing new set\n");
+#endif
+ newset = xmlXPathNodeCollectAndTest(ctxt, axis, nodetest, nodetype,
+ prefix, name);
+ if (ctxt->context->nodelist != NULL)
+ xmlXPathFreeNodeSet(ctxt->context->nodelist);
+ ctxt->context->nodelist = newset;
+ ctxt->context->node = NULL;
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "Basis : ");
+ xmlXPathDebugNodeSet(stdout, ctxt->context->nodelist);
+#endif
+}
+
+/**
+ * xmlXPathEvalStep:
+ * @ctxt: the XPath Parser context
+ *
+ * [4] Step ::= Basis Predicate*
+ * | AbbreviatedStep
+ * [12] AbbreviatedStep ::= '.'
+ * | '..'
+ *
+ * Evaluate one step in a Location Path
+ * A location step of . is short for self::node(). This is
+ * particularly useful in conjunction with //. For example, the
+ * location path .//para is short for
+ * self::node()/descendant-or-self::node()/child::para
+ * and so will select all para descendant elements of the context
+ * node.
+ * Similarly, a location step of .. is short for parent::node().
+ * For example, ../title is short for parent::node()/child::title
+ * and so will select the title children of the parent of the context
+ * node.
+ */
+void
+xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) {
+ xmlNodeSetPtr newset = NULL;
+
+ if ((CUR == '.') && (NXT(1) == '.')) {
+ SKIP(2);
+ if (ctxt->context->nodelist == NULL) {
+ STRANGE
+ xmlXPathRoot(ctxt);
+ }
+ newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT,
+ NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
+ if (ctxt->context->nodelist != NULL)
+ xmlXPathFreeNodeSet(ctxt->context->nodelist);
+ ctxt->context->nodelist = newset;
+ ctxt->context->node = NULL;
+ } else if (CUR == '.') {
+ NEXT;
+ } else {
+ xmlXPathEvalBasis(ctxt);
+ while (CUR == '[') {
+ xmlXPathEvalPredicate(ctxt);
+ }
+ }
+#ifdef DEBUG_STEP
+ fprintf(xmlXPathDebug, "Step : ");
+ xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
+#endif
+}
+
+/**
+ * xmlXPathEvalRelativeLocationPath:
+ * @ctxt: the XPath Parser context
+ *
+ * [3] RelativeLocationPath ::= Step
+ * | RelativeLocationPath '/' Step
+ * | AbbreviatedRelativeLocationPath
+ * [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
+ *
+ */
+void
+xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt) {
+ xmlNodeSetPtr newset = NULL;
+
+ xmlXPathEvalStep(ctxt);
+ while (CUR == '/') {
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ SKIP(2);
+ if (ctxt->context->nodelist == NULL) {
+ STRANGE
+ xmlXPathRoot(ctxt);
+ }
+ newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
+ NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
+ if (ctxt->context->nodelist != NULL)
+ xmlXPathFreeNodeSet(ctxt->context->nodelist);
+ ctxt->context->nodelist = newset;
+ ctxt->context->node = NULL;
+ xmlXPathEvalStep(ctxt);
+ } else if (CUR == '/') {
+ NEXT;
+ xmlXPathEvalStep(ctxt);
+ }
+ }
+}
+
+/**
+ * xmlXPathEvalLocationPath:
+ * @ctxt: the XPath Parser context
+ *
+ * [1] LocationPath ::= RelativeLocationPath
+ * | AbsoluteLocationPath
+ * [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
+ * | AbbreviatedAbsoluteLocationPath
+ * [10] AbbreviatedAbsoluteLocationPath ::=
+ * '//' RelativeLocationPath
+ *
+ * // is short for /descendant-or-self::node()/. For example,
+ * //para is short for /descendant-or-self::node()/child::para and
+ * so will select any para element in the document (even a para element
+ * that is a document element will be selected by //para since the
+ * document element node is a child of the root node); div//para is
+ * short for div/descendant-or-self::node()/child::para and so will
+ * select all para descendants of div children.
+ */
+void
+xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) {
+ xmlNodeSetPtr newset = NULL;
+
+ while (CUR == '/') {
+ if ((CUR == '/') && (NXT(1) == '/')) {
+ SKIP(2);
+ if (ctxt->context->nodelist == NULL)
+ xmlXPathRoot(ctxt);
+ newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
+ NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
+ if (ctxt->context->nodelist != NULL)
+ xmlXPathFreeNodeSet(ctxt->context->nodelist);
+ ctxt->context->nodelist = newset;
+ ctxt->context->node = NULL;
+ xmlXPathEvalRelativeLocationPath(ctxt);
+ } else if (CUR == '/') {
+ NEXT;
+ xmlXPathRoot(ctxt);
+ xmlXPathEvalRelativeLocationPath(ctxt);
+ } else {
+ xmlXPathEvalRelativeLocationPath(ctxt);
+ }
+ }
+}
+
+/*
+ * TODO * extra spaces *
+ * more tokenization rules ... Not used currently, especially allowing
+ * spaces before and after ExprToken !!!!!!!!!!!!!
+ *
+ * [32] Operator ::= OperatorName
+ * | MultiplyOperator
+ * | '/' | '//' | '|' | '+' | '-' | '=' | '!='
+ * | '<'| '<=' | '>' | '>='
+ * [33] OperatorName ::= 'and' | 'or' | 'mod' | 'div'
+ * [39] ExprWhitespace ::= S
+ *
+ * BUG: ExprToken is never referenced.
+ *
+ * [28] ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'
+ * | WildcardName
+ * | NodeType
+ * | Operator
+ * | FunctionName
+ * | AxisName
+ * | Literal
+ * | Number
+ * | VariableReference
+ */
+
+/**
+ * xmlXPathEval:
+ * @str: the XPath expression
+ * @ctxt: the XPath context
+ *
+ * Evaluate the XPath Location Path in the given context.
+ *
+ * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
+ * the caller has to free the object.
+ */
+xmlXPathObjectPtr
+xmlXPathEval(const CHAR *str, xmlXPathContextPtr ctxt) {
+ xmlXPathParserContextPtr pctxt;
+ xmlXPathObjectPtr res;
+
+ CHECK_CONTEXT
+
+ if (xmlXPathDebug == NULL)
+ xmlXPathDebug = stderr;
+ pctxt = xmlXPathNewParserContext(str, ctxt);
+ xmlXPathEvalLocationPath(pctxt);
+
+ do {
+ res = valuePop(pctxt);
+#ifdef DEBUG
+#endif
+ } while (res != NULL);
+ res = xmlXPathNewNodeSetList(pctxt->context->nodelist);
+ xmlXPathFreeParserContext(pctxt);
+ return(res);
+}
+
+/**
+ * xmlXPathEvalExpression:
+ * @str: the XPath expression
+ * @ctxt: the XPath context
+ *
+ * Evaluate the XPath expression in the given context.
+ *
+ * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
+ * the caller has to free the object.
+ */
+xmlXPathObjectPtr
+xmlXPathEvalExpression(const CHAR *str, xmlXPathContextPtr ctxt) {
+ xmlXPathParserContextPtr pctxt;
+ xmlXPathObjectPtr res, tmp;
+
+ CHECK_CONTEXT
+
+ if (xmlXPathDebug == NULL)
+ xmlXPathDebug = stderr;
+ pctxt = xmlXPathNewParserContext(str, ctxt);
+ xmlXPathEvalExpr(pctxt);
+
+ res = valuePop(pctxt);
+ do {
+ tmp = valuePop(pctxt);
+#ifdef DEBUG
+#endif
+ } while (tmp != NULL);
+ xmlXPathFreeParserContext(pctxt);
+ return(res);
+}
+