Index: third_party/libxml/src/xpath.c |
diff --git a/third_party/libxml/src/xpath.c b/third_party/libxml/src/xpath.c |
index 9423ef808a364dbfe7d1b9a21c17b61f8c8cb019..dc41ce6b327774adad749c3c58401f485b5a5f0c 100644 |
--- a/third_party/libxml/src/xpath.c |
+++ b/third_party/libxml/src/xpath.c |
@@ -55,6 +55,8 @@ |
#include <libxml/pattern.h> |
#endif |
+#include "buf.h" |
+ |
#ifdef LIBXML_PATTERN_ENABLED |
#define XPATH_STREAMING |
#endif |
@@ -64,6 +66,15 @@ |
"Unimplemented block at %s:%d\n", \ |
__FILE__, __LINE__); |
+/** |
+ * WITH_TIM_SORT: |
+ * |
+ * Use the Timsort algorithm provided in timsort.h to sort |
+ * nodeset as this is a great improvement over the old Shell sort |
+ * used in xmlXPathNodeSetSort() |
+ */ |
+#define WITH_TIM_SORT |
+ |
/* |
* XP_OPTIMIZED_NON_ELEM_COMPARISON: |
* If defined, this will use xmlXPathCmpNodesExt() instead of |
@@ -90,252 +101,609 @@ |
/* #define XP_DEBUG_OBJ_USAGE */ |
/* |
- * TODO: |
- * There are a few spots where some tests are done which depend upon ascii |
- * data. These should be enhanced for full UTF8 support (see particularly |
- * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT) |
+ * XPATH_MAX_STEPS: |
+ * when compiling an XPath expression we arbitrary limit the maximum |
+ * number of step operation in the compiled expression. 1000000 is |
+ * an insanely large value which should never be reached under normal |
+ * circumstances |
*/ |
- |
-#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) |
- |
-/************************************************************************ |
- * * |
- * Floating point stuff * |
- * * |
- ************************************************************************/ |
- |
-#ifndef TRIO_REPLACE_STDIO |
-#define TRIO_PUBLIC static |
-#endif |
-#include "trionan.c" |
+#define XPATH_MAX_STEPS 1000000 |
/* |
- * The lack of portability of this section of the libc is annoying ! |
- */ |
-double xmlXPathNAN = 0; |
-double xmlXPathPINF = 1; |
-double xmlXPathNINF = -1; |
-static double xmlXPathNZERO = 0; /* not exported from headers */ |
-static int xmlXPathInitialized = 0; |
- |
-/** |
- * xmlXPathInit: |
- * |
- * Initialize the XPath environment |
- */ |
-void |
-xmlXPathInit(void) { |
- if (xmlXPathInitialized) return; |
- |
- xmlXPathPINF = trio_pinf(); |
- xmlXPathNINF = trio_ninf(); |
- xmlXPathNAN = trio_nan(); |
- xmlXPathNZERO = trio_nzero(); |
- |
- xmlXPathInitialized = 1; |
-} |
- |
-/** |
- * xmlXPathIsNaN: |
- * @val: a double value |
- * |
- * Provides a portable isnan() function to detect whether a double |
- * is a NotaNumber. Based on trio code |
- * http://sourceforge.net/projects/ctrio/ |
- * |
- * Returns 1 if the value is a NaN, 0 otherwise |
- */ |
-int |
-xmlXPathIsNaN(double val) { |
- return(trio_isnan(val)); |
-} |
- |
-/** |
- * xmlXPathIsInf: |
- * @val: a double value |
- * |
- * Provides a portable isinf() function to detect whether a double |
- * is a +Infinite or -Infinite. Based on trio code |
- * http://sourceforge.net/projects/ctrio/ |
- * |
- * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise |
- */ |
-int |
-xmlXPathIsInf(double val) { |
- return(trio_isinf(val)); |
-} |
- |
-#endif /* SCHEMAS or XPATH */ |
-#ifdef LIBXML_XPATH_ENABLED |
-/** |
- * xmlXPathGetSign: |
- * @val: a double value |
- * |
- * Provides a portable function to detect the sign of a double |
- * Modified from trio code |
- * http://sourceforge.net/projects/ctrio/ |
- * |
- * Returns 1 if the value is Negative, 0 if positive |
+ * XPATH_MAX_STACK_DEPTH: |
+ * when evaluating an XPath expression we arbitrary limit the maximum |
+ * number of object allowed to be pushed on the stack. 1000000 is |
+ * an insanely large value which should never be reached under normal |
+ * circumstances |
*/ |
-static int |
-xmlXPathGetSign(double val) { |
- return(trio_signbit(val)); |
-} |
- |
+#define XPATH_MAX_STACK_DEPTH 1000000 |
/* |
- * TODO: when compatibility allows remove all "fake node libxslt" strings |
- * the test should just be name[0] = ' ' |
+ * XPATH_MAX_NODESET_LENGTH: |
+ * when evaluating an XPath expression nodesets are created and we |
+ * arbitrary limit the maximum length of those node set. 10000000 is |
+ * an insanely large value which should never be reached under normal |
+ * circumstances, one would first need to construct an in memory tree |
+ * with more than 10 millions nodes. |
*/ |
-#ifdef DEBUG_XPATH_EXPRESSION |
-#define DEBUG_STEP |
-#define DEBUG_EXPR |
-#define DEBUG_EVAL_COUNTS |
-#endif |
+#define XPATH_MAX_NODESET_LENGTH 10000000 |
-static xmlNs xmlXPathXMLNamespaceStruct = { |
- NULL, |
- XML_NAMESPACE_DECL, |
- XML_XML_NAMESPACE, |
- BAD_CAST "xml", |
- NULL, |
- NULL |
-}; |
-static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct; |
-#ifndef LIBXML_THREAD_ENABLED |
/* |
- * Optimizer is disabled only when threaded apps are detected while |
- * the library ain't compiled for thread safety. |
+ * TODO: |
+ * There are a few spots where some tests are done which depend upon ascii |
+ * data. These should be enhanced for full UTF8 support (see particularly |
+ * any use of the macros IS_ASCII_CHARACTER and IS_ASCII_DIGIT) |
*/ |
-static int xmlXPathDisableOptimizer = 0; |
-#endif |
- |
-/************************************************************************ |
- * * |
- * Error handling routines * |
- * * |
- ************************************************************************/ |
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON |
/** |
- * XP_ERRORNULL: |
- * @X: the error code |
+ * xmlXPathCmpNodesExt: |
+ * @node1: the first node |
+ * @node2: the second node |
* |
- * Macro to raise an XPath error and return NULL. |
- */ |
-#define XP_ERRORNULL(X) \ |
- { xmlXPathErr(ctxt, X); return(NULL); } |
- |
-/* |
- * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError |
- */ |
-static const char *xmlXPathErrorMessages[] = { |
- "Ok\n", |
- "Number encoding\n", |
- "Unfinished literal\n", |
- "Start of literal\n", |
- "Expected $ for variable reference\n", |
- "Undefined variable\n", |
- "Invalid predicate\n", |
- "Invalid expression\n", |
- "Missing closing curly brace\n", |
- "Unregistered function\n", |
- "Invalid operand\n", |
- "Invalid type\n", |
- "Invalid number of arguments\n", |
- "Invalid context size\n", |
- "Invalid context position\n", |
- "Memory allocation error\n", |
- "Syntax error\n", |
- "Resource error\n", |
- "Sub resource error\n", |
- "Undefined namespace prefix\n", |
- "Encoding error\n", |
- "Char out of XML range\n", |
- "Invalid or incomplete context\n", |
- "?? Unknown error ??\n" /* Must be last in the list! */ |
-}; |
-#define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \ |
- sizeof(xmlXPathErrorMessages[0])) - 1) |
-/** |
- * xmlXPathErrMemory: |
- * @ctxt: an XPath context |
- * @extra: extra informations |
+ * Compare two nodes w.r.t document order. |
+ * This one is optimized for handling of non-element nodes. |
* |
- * Handle a redefinition of attribute error |
+ * Returns -2 in case of error 1 if first point < second point, 0 if |
+ * it's the same node, -1 otherwise |
*/ |
-static void |
-xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra) |
-{ |
- if (ctxt != NULL) { |
- if (extra) { |
- xmlChar buf[200]; |
+static int |
+xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) { |
+ int depth1, depth2; |
+ int misc = 0, precedence1 = 0, precedence2 = 0; |
+ xmlNodePtr miscNode1 = NULL, miscNode2 = NULL; |
+ xmlNodePtr cur, root; |
+ long l1, l2; |
- xmlStrPrintf(buf, 200, |
- BAD_CAST "Memory allocation failed : %s\n", |
- extra); |
- ctxt->lastError.message = (char *) xmlStrdup(buf); |
- } else { |
- ctxt->lastError.message = (char *) |
- xmlStrdup(BAD_CAST "Memory allocation failed\n"); |
- } |
- ctxt->lastError.domain = XML_FROM_XPATH; |
- ctxt->lastError.code = XML_ERR_NO_MEMORY; |
- if (ctxt->error != NULL) |
- ctxt->error(ctxt->userData, &ctxt->lastError); |
- } else { |
- if (extra) |
- __xmlRaiseError(NULL, NULL, NULL, |
- NULL, NULL, XML_FROM_XPATH, |
- XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
- extra, NULL, NULL, 0, 0, |
- "Memory allocation failed : %s\n", extra); |
- else |
- __xmlRaiseError(NULL, NULL, NULL, |
- NULL, NULL, XML_FROM_XPATH, |
- XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
- NULL, NULL, NULL, 0, 0, |
- "Memory allocation failed\n"); |
- } |
-} |
+ if ((node1 == NULL) || (node2 == NULL)) |
+ return(-2); |
-/** |
- * xmlXPathPErrMemory: |
- * @ctxt: an XPath parser context |
- * @extra: extra informations |
- * |
- * Handle a redefinition of attribute error |
- */ |
-static void |
-xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra) |
-{ |
- if (ctxt == NULL) |
- xmlXPathErrMemory(NULL, extra); |
- else { |
- ctxt->error = XPATH_MEMORY_ERROR; |
- xmlXPathErrMemory(ctxt->context, extra); |
- } |
-} |
+ if (node1 == node2) |
+ return(0); |
-/** |
- * xmlXPathErr: |
- * @ctxt: a XPath parser context |
- * @error: the error code |
- * |
- * Handle an XPath error |
- */ |
-void |
-xmlXPathErr(xmlXPathParserContextPtr ctxt, int error) |
-{ |
- if ((error < 0) || (error > MAXERRNO)) |
- error = MAXERRNO; |
- if (ctxt == NULL) { |
- __xmlRaiseError(NULL, NULL, NULL, |
- NULL, NULL, XML_FROM_XPATH, |
- error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
- XML_ERR_ERROR, NULL, 0, |
- NULL, NULL, NULL, 0, 0, |
- "%s", xmlXPathErrorMessages[error]); |
- return; |
+ /* |
+ * a couple of optimizations which will avoid computations in most cases |
+ */ |
+ switch (node1->type) { |
+ case XML_ELEMENT_NODE: |
+ if (node2->type == XML_ELEMENT_NODE) { |
+ if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */ |
+ (0 > (long) node2->content) && |
+ (node1->doc == node2->doc)) |
+ { |
+ l1 = -((long) node1->content); |
+ l2 = -((long) node2->content); |
+ if (l1 < l2) |
+ return(1); |
+ if (l1 > l2) |
+ return(-1); |
+ } else |
+ goto turtle_comparison; |
+ } |
+ break; |
+ case XML_ATTRIBUTE_NODE: |
+ precedence1 = 1; /* element is owner */ |
+ miscNode1 = node1; |
+ node1 = node1->parent; |
+ misc = 1; |
+ break; |
+ case XML_TEXT_NODE: |
+ case XML_CDATA_SECTION_NODE: |
+ case XML_COMMENT_NODE: |
+ case XML_PI_NODE: { |
+ miscNode1 = node1; |
+ /* |
+ * Find nearest element node. |
+ */ |
+ if (node1->prev != NULL) { |
+ do { |
+ node1 = node1->prev; |
+ if (node1->type == XML_ELEMENT_NODE) { |
+ precedence1 = 3; /* element in prev-sibl axis */ |
+ break; |
+ } |
+ if (node1->prev == NULL) { |
+ precedence1 = 2; /* element is parent */ |
+ /* |
+ * URGENT TODO: Are there any cases, where the |
+ * parent of such a node is not an element node? |
+ */ |
+ node1 = node1->parent; |
+ break; |
+ } |
+ } while (1); |
+ } else { |
+ precedence1 = 2; /* element is parent */ |
+ node1 = node1->parent; |
+ } |
+ if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) || |
+ (0 <= (long) node1->content)) { |
+ /* |
+ * Fallback for whatever case. |
+ */ |
+ node1 = miscNode1; |
+ precedence1 = 0; |
+ } else |
+ misc = 1; |
+ } |
+ break; |
+ case XML_NAMESPACE_DECL: |
+ /* |
+ * TODO: why do we return 1 for namespace nodes? |
+ */ |
+ return(1); |
+ default: |
+ break; |
+ } |
+ switch (node2->type) { |
+ case XML_ELEMENT_NODE: |
+ break; |
+ case XML_ATTRIBUTE_NODE: |
+ precedence2 = 1; /* element is owner */ |
+ miscNode2 = node2; |
+ node2 = node2->parent; |
+ misc = 1; |
+ break; |
+ case XML_TEXT_NODE: |
+ case XML_CDATA_SECTION_NODE: |
+ case XML_COMMENT_NODE: |
+ case XML_PI_NODE: { |
+ miscNode2 = node2; |
+ if (node2->prev != NULL) { |
+ do { |
+ node2 = node2->prev; |
+ if (node2->type == XML_ELEMENT_NODE) { |
+ precedence2 = 3; /* element in prev-sibl axis */ |
+ break; |
+ } |
+ if (node2->prev == NULL) { |
+ precedence2 = 2; /* element is parent */ |
+ node2 = node2->parent; |
+ break; |
+ } |
+ } while (1); |
+ } else { |
+ precedence2 = 2; /* element is parent */ |
+ node2 = node2->parent; |
+ } |
+ if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) || |
+ (0 <= (long) node2->content)) |
+ { |
+ node2 = miscNode2; |
+ precedence2 = 0; |
+ } else |
+ misc = 1; |
+ } |
+ break; |
+ case XML_NAMESPACE_DECL: |
+ return(1); |
+ default: |
+ break; |
+ } |
+ if (misc) { |
+ if (node1 == node2) { |
+ if (precedence1 == precedence2) { |
+ /* |
+ * The ugly case; but normally there aren't many |
+ * adjacent non-element nodes around. |
+ */ |
+ cur = miscNode2->prev; |
+ while (cur != NULL) { |
+ if (cur == miscNode1) |
+ return(1); |
+ if (cur->type == XML_ELEMENT_NODE) |
+ return(-1); |
+ cur = cur->prev; |
+ } |
+ return (-1); |
+ } else { |
+ /* |
+ * Evaluate based on higher precedence wrt to the element. |
+ * TODO: This assumes attributes are sorted before content. |
+ * Is this 100% correct? |
+ */ |
+ if (precedence1 < precedence2) |
+ return(1); |
+ else |
+ return(-1); |
+ } |
+ } |
+ /* |
+ * Special case: One of the helper-elements is contained by the other. |
+ * <foo> |
+ * <node2> |
+ * <node1>Text-1(precedence1 == 2)</node1> |
+ * </node2> |
+ * Text-6(precedence2 == 3) |
+ * </foo> |
+ */ |
+ if ((precedence2 == 3) && (precedence1 > 1)) { |
+ cur = node1->parent; |
+ while (cur) { |
+ if (cur == node2) |
+ return(1); |
+ cur = cur->parent; |
+ } |
+ } |
+ if ((precedence1 == 3) && (precedence2 > 1)) { |
+ cur = node2->parent; |
+ while (cur) { |
+ if (cur == node1) |
+ return(-1); |
+ cur = cur->parent; |
+ } |
+ } |
+ } |
+ |
+ /* |
+ * Speedup using document order if availble. |
+ */ |
+ if ((node1->type == XML_ELEMENT_NODE) && |
+ (node2->type == XML_ELEMENT_NODE) && |
+ (0 > (long) node1->content) && |
+ (0 > (long) node2->content) && |
+ (node1->doc == node2->doc)) { |
+ |
+ l1 = -((long) node1->content); |
+ l2 = -((long) node2->content); |
+ if (l1 < l2) |
+ return(1); |
+ if (l1 > l2) |
+ return(-1); |
+ } |
+ |
+turtle_comparison: |
+ |
+ if (node1 == node2->prev) |
+ return(1); |
+ if (node1 == node2->next) |
+ return(-1); |
+ /* |
+ * compute depth to root |
+ */ |
+ for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { |
+ if (cur == node1) |
+ return(1); |
+ depth2++; |
+ } |
+ root = cur; |
+ for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { |
+ if (cur == node2) |
+ return(-1); |
+ depth1++; |
+ } |
+ /* |
+ * Distinct document (or distinct entities :-( ) case. |
+ */ |
+ if (root != cur) { |
+ return(-2); |
+ } |
+ /* |
+ * get the nearest common ancestor. |
+ */ |
+ while (depth1 > depth2) { |
+ depth1--; |
+ node1 = node1->parent; |
+ } |
+ while (depth2 > depth1) { |
+ depth2--; |
+ node2 = node2->parent; |
+ } |
+ while (node1->parent != node2->parent) { |
+ node1 = node1->parent; |
+ node2 = node2->parent; |
+ /* should not happen but just in case ... */ |
+ if ((node1 == NULL) || (node2 == NULL)) |
+ return(-2); |
+ } |
+ /* |
+ * Find who's first. |
+ */ |
+ if (node1 == node2->prev) |
+ return(1); |
+ if (node1 == node2->next) |
+ return(-1); |
+ /* |
+ * Speedup using document order if availble. |
+ */ |
+ if ((node1->type == XML_ELEMENT_NODE) && |
+ (node2->type == XML_ELEMENT_NODE) && |
+ (0 > (long) node1->content) && |
+ (0 > (long) node2->content) && |
+ (node1->doc == node2->doc)) { |
+ |
+ l1 = -((long) node1->content); |
+ l2 = -((long) node2->content); |
+ if (l1 < l2) |
+ return(1); |
+ if (l1 > l2) |
+ return(-1); |
+ } |
+ |
+ for (cur = node1->next;cur != NULL;cur = cur->next) |
+ if (cur == node2) |
+ return(1); |
+ return(-1); /* assume there is no sibling list corruption */ |
+} |
+#endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */ |
+ |
+/* |
+ * Wrapper for the Timsort argorithm from timsort.h |
+ */ |
+#ifdef WITH_TIM_SORT |
+#define SORT_NAME libxml_domnode |
+#define SORT_TYPE xmlNodePtr |
+/** |
+ * wrap_cmp: |
+ * @x: a node |
+ * @y: another node |
+ * |
+ * Comparison function for the Timsort implementation |
+ * |
+ * Returns -2 in case of error -1 if first point < second point, 0 if |
+ * it's the same node, +1 otherwise |
+ */ |
+static |
+int wrap_cmp( xmlNodePtr x, xmlNodePtr y ); |
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON |
+ static int wrap_cmp( xmlNodePtr x, xmlNodePtr y ) |
+ { |
+ int res = xmlXPathCmpNodesExt(x, y); |
+ return res == -2 ? res : -res; |
+ } |
+#else |
+ static int wrap_cmp( xmlNodePtr x, xmlNodePtr y ) |
+ { |
+ int res = xmlXPathCmpNodes(x, y); |
+ return res == -2 ? res : -res; |
+ } |
+#endif |
+#define SORT_CMP(x, y) (wrap_cmp(x, y)) |
+#include "timsort.h" |
+#endif /* WITH_TIM_SORT */ |
+ |
+#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) |
+ |
+/************************************************************************ |
+ * * |
+ * Floating point stuff * |
+ * * |
+ ************************************************************************/ |
+ |
+#ifndef TRIO_REPLACE_STDIO |
+#define TRIO_PUBLIC static |
+#endif |
+#include "trionan.c" |
+ |
+/* |
+ * The lack of portability of this section of the libc is annoying ! |
+ */ |
+double xmlXPathNAN = 0; |
+double xmlXPathPINF = 1; |
+double xmlXPathNINF = -1; |
+static double xmlXPathNZERO = 0; /* not exported from headers */ |
+static int xmlXPathInitialized = 0; |
+ |
+/** |
+ * xmlXPathInit: |
+ * |
+ * Initialize the XPath environment |
+ */ |
+void |
+xmlXPathInit(void) { |
+ if (xmlXPathInitialized) return; |
+ |
+ xmlXPathPINF = trio_pinf(); |
+ xmlXPathNINF = trio_ninf(); |
+ xmlXPathNAN = trio_nan(); |
+ xmlXPathNZERO = trio_nzero(); |
+ |
+ xmlXPathInitialized = 1; |
+} |
+ |
+/** |
+ * xmlXPathIsNaN: |
+ * @val: a double value |
+ * |
+ * Provides a portable isnan() function to detect whether a double |
+ * is a NotaNumber. Based on trio code |
+ * http://sourceforge.net/projects/ctrio/ |
+ * |
+ * Returns 1 if the value is a NaN, 0 otherwise |
+ */ |
+int |
+xmlXPathIsNaN(double val) { |
+ return(trio_isnan(val)); |
+} |
+ |
+/** |
+ * xmlXPathIsInf: |
+ * @val: a double value |
+ * |
+ * Provides a portable isinf() function to detect whether a double |
+ * is a +Infinite or -Infinite. Based on trio code |
+ * http://sourceforge.net/projects/ctrio/ |
+ * |
+ * Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise |
+ */ |
+int |
+xmlXPathIsInf(double val) { |
+ return(trio_isinf(val)); |
+} |
+ |
+#endif /* SCHEMAS or XPATH */ |
+#ifdef LIBXML_XPATH_ENABLED |
+/** |
+ * xmlXPathGetSign: |
+ * @val: a double value |
+ * |
+ * Provides a portable function to detect the sign of a double |
+ * Modified from trio code |
+ * http://sourceforge.net/projects/ctrio/ |
+ * |
+ * Returns 1 if the value is Negative, 0 if positive |
+ */ |
+static int |
+xmlXPathGetSign(double val) { |
+ return(trio_signbit(val)); |
+} |
+ |
+ |
+/* |
+ * TODO: when compatibility allows remove all "fake node libxslt" strings |
+ * the test should just be name[0] = ' ' |
+ */ |
+#ifdef DEBUG_XPATH_EXPRESSION |
+#define DEBUG_STEP |
+#define DEBUG_EXPR |
+#define DEBUG_EVAL_COUNTS |
+#endif |
+ |
+static xmlNs xmlXPathXMLNamespaceStruct = { |
+ NULL, |
+ XML_NAMESPACE_DECL, |
+ XML_XML_NAMESPACE, |
+ BAD_CAST "xml", |
+ NULL, |
+ NULL |
+}; |
+static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct; |
+#ifndef LIBXML_THREAD_ENABLED |
+/* |
+ * Optimizer is disabled only when threaded apps are detected while |
+ * the library ain't compiled for thread safety. |
+ */ |
+static int xmlXPathDisableOptimizer = 0; |
+#endif |
+ |
+/************************************************************************ |
+ * * |
+ * Error handling routines * |
+ * * |
+ ************************************************************************/ |
+ |
+/** |
+ * XP_ERRORNULL: |
+ * @X: the error code |
+ * |
+ * Macro to raise an XPath error and return NULL. |
+ */ |
+#define XP_ERRORNULL(X) \ |
+ { xmlXPathErr(ctxt, X); return(NULL); } |
+ |
+/* |
+ * The array xmlXPathErrorMessages corresponds to the enum xmlXPathError |
+ */ |
+static const char *xmlXPathErrorMessages[] = { |
+ "Ok\n", |
+ "Number encoding\n", |
+ "Unfinished literal\n", |
+ "Start of literal\n", |
+ "Expected $ for variable reference\n", |
+ "Undefined variable\n", |
+ "Invalid predicate\n", |
+ "Invalid expression\n", |
+ "Missing closing curly brace\n", |
+ "Unregistered function\n", |
+ "Invalid operand\n", |
+ "Invalid type\n", |
+ "Invalid number of arguments\n", |
+ "Invalid context size\n", |
+ "Invalid context position\n", |
+ "Memory allocation error\n", |
+ "Syntax error\n", |
+ "Resource error\n", |
+ "Sub resource error\n", |
+ "Undefined namespace prefix\n", |
+ "Encoding error\n", |
+ "Char out of XML range\n", |
+ "Invalid or incomplete context\n", |
+ "Stack usage error\n", |
+ "Forbidden variable\n", |
+ "?? Unknown error ??\n" /* Must be last in the list! */ |
+}; |
+#define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \ |
+ sizeof(xmlXPathErrorMessages[0])) - 1) |
+/** |
+ * xmlXPathErrMemory: |
+ * @ctxt: an XPath context |
+ * @extra: extra informations |
+ * |
+ * Handle a redefinition of attribute error |
+ */ |
+static void |
+xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra) |
+{ |
+ if (ctxt != NULL) { |
+ if (extra) { |
+ xmlChar buf[200]; |
+ |
+ xmlStrPrintf(buf, 200, |
+ BAD_CAST "Memory allocation failed : %s\n", |
+ extra); |
+ ctxt->lastError.message = (char *) xmlStrdup(buf); |
+ } else { |
+ ctxt->lastError.message = (char *) |
+ xmlStrdup(BAD_CAST "Memory allocation failed\n"); |
+ } |
+ ctxt->lastError.domain = XML_FROM_XPATH; |
+ ctxt->lastError.code = XML_ERR_NO_MEMORY; |
+ if (ctxt->error != NULL) |
+ ctxt->error(ctxt->userData, &ctxt->lastError); |
+ } else { |
+ if (extra) |
+ __xmlRaiseError(NULL, NULL, NULL, |
+ NULL, NULL, XML_FROM_XPATH, |
+ XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
+ extra, NULL, NULL, 0, 0, |
+ "Memory allocation failed : %s\n", extra); |
+ else |
+ __xmlRaiseError(NULL, NULL, NULL, |
+ NULL, NULL, XML_FROM_XPATH, |
+ XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, |
+ NULL, NULL, NULL, 0, 0, |
+ "Memory allocation failed\n"); |
+ } |
+} |
+ |
+/** |
+ * xmlXPathPErrMemory: |
+ * @ctxt: an XPath parser context |
+ * @extra: extra informations |
+ * |
+ * Handle a redefinition of attribute error |
+ */ |
+static void |
+xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra) |
+{ |
+ if (ctxt == NULL) |
+ xmlXPathErrMemory(NULL, extra); |
+ else { |
+ ctxt->error = XPATH_MEMORY_ERROR; |
+ xmlXPathErrMemory(ctxt->context, extra); |
+ } |
+} |
+ |
+/** |
+ * xmlXPathErr: |
+ * @ctxt: a XPath parser context |
+ * @error: the error code |
+ * |
+ * Handle an XPath error |
+ */ |
+void |
+xmlXPathErr(xmlXPathParserContextPtr ctxt, int error) |
+{ |
+ if ((error < 0) || (error > MAXERRNO)) |
+ error = MAXERRNO; |
+ if (ctxt == NULL) { |
+ __xmlRaiseError(NULL, NULL, NULL, |
+ NULL, NULL, XML_FROM_XPATH, |
+ error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
+ XML_ERR_ERROR, NULL, 0, |
+ NULL, NULL, NULL, 0, 0, |
+ "%s", xmlXPathErrorMessages[error]); |
+ return; |
} |
ctxt->error = error; |
if (ctxt->context == NULL) { |
@@ -419,8 +787,7 @@ xmlPointerListAddSize(xmlPointerListPtr list, |
if (list->items == NULL) { |
if (initialSize <= 0) |
initialSize = 1; |
- list->items = (void **) xmlMalloc( |
- initialSize * sizeof(void *)); |
+ list->items = (void **) xmlMalloc(initialSize * sizeof(void *)); |
if (list->items == NULL) { |
xmlXPathErrMemory(NULL, |
"xmlPointerListCreate: allocating item\n"); |
@@ -429,12 +796,17 @@ xmlPointerListAddSize(xmlPointerListPtr list, |
list->number = 0; |
list->size = initialSize; |
} else if (list->size <= list->number) { |
+ if (list->size > 50000000) { |
+ xmlXPathErrMemory(NULL, |
+ "xmlPointerListAddSize: re-allocating item\n"); |
+ return(-1); |
+ } |
list->size *= 2; |
list->items = (void **) xmlRealloc(list->items, |
list->size * sizeof(void *)); |
if (list->items == NULL) { |
xmlXPathErrMemory(NULL, |
- "xmlPointerListCreate: re-allocating item\n"); |
+ "xmlPointerListAddSize: re-allocating item\n"); |
list->size = 0; |
return(-1); |
} |
@@ -552,8 +924,6 @@ typedef enum { |
NODE_TYPE_PI = XML_PI_NODE |
} xmlXPathTypeVal; |
-#define XP_REWRITE_DOS_CHILD_ELEM 1 |
- |
typedef struct _xmlXPathStepOp xmlXPathStepOp; |
typedef xmlXPathStepOp *xmlXPathStepOpPtr; |
struct _xmlXPathStepOp { |
@@ -567,7 +937,6 @@ struct _xmlXPathStepOp { |
void *value5; |
void *cache; |
void *cacheURI; |
- int rewriteType; |
}; |
struct _xmlXPathCompExpr { |
@@ -722,6 +1091,10 @@ xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2, |
if (comp->nbStep >= comp->maxStep) { |
xmlXPathStepOp *real; |
+ if (comp->maxStep >= XPATH_MAX_STEPS) { |
+ xmlXPathErrMemory(NULL, "adding step\n"); |
+ return(-1); |
+ } |
comp->maxStep *= 2; |
real = (xmlXPathStepOp *) xmlRealloc(comp->steps, |
comp->maxStep * sizeof(xmlXPathStepOp)); |
@@ -733,7 +1106,6 @@ xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2, |
comp->steps = real; |
} |
comp->last = comp->nbStep; |
- comp->steps[comp->nbStep].rewriteType = 0; |
comp->steps[comp->nbStep].ch1 = ch1; |
comp->steps[comp->nbStep].ch2 = ch2; |
comp->steps[comp->nbStep].op = op; |
@@ -2051,6 +2423,11 @@ xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val) |
ret->type = XPATH_NODESET; |
ret->boolval = 0; |
ret->nodesetval = xmlXPathNodeSetCreate(val); |
+ if (ret->nodesetval == NULL) { |
+ ctxt->lastError.domain = XML_FROM_XPATH; |
+ ctxt->lastError.code = XML_ERR_NO_MEMORY; |
+ return(NULL); |
+ } |
#ifdef XP_DEBUG_OBJ_USAGE |
xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); |
#endif |
@@ -2398,6 +2775,42 @@ xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { |
************************************************************************/ |
/** |
+ * xmlXPathSetFrame: |
+ * @ctxt: an XPath parser context |
+ * |
+ * Set the callee evaluation frame |
+ * |
+ * Returns the previous frame value to be restored once done |
+ */ |
+static int |
+xmlXPathSetFrame(xmlXPathParserContextPtr ctxt) { |
+ int ret; |
+ |
+ if (ctxt == NULL) |
+ return(0); |
+ ret = ctxt->valueFrame; |
+ ctxt->valueFrame = ctxt->valueNr; |
+ return(ret); |
+} |
+ |
+/** |
+ * xmlXPathPopFrame: |
+ * @ctxt: an XPath parser context |
+ * @frame: the previous frame value |
+ * |
+ * Remove the callee evaluation frame |
+ */ |
+static void |
+xmlXPathPopFrame(xmlXPathParserContextPtr ctxt, int frame) { |
+ if (ctxt == NULL) |
+ return; |
+ if (ctxt->valueNr < ctxt->valueFrame) { |
+ xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR); |
+ } |
+ ctxt->valueFrame = frame; |
+} |
+ |
+/** |
* valuePop: |
* @ctxt: an XPath evaluation context |
* |
@@ -2412,6 +2825,12 @@ valuePop(xmlXPathParserContextPtr ctxt) |
if ((ctxt == NULL) || (ctxt->valueNr <= 0)) |
return (NULL); |
+ |
+ if (ctxt->valueNr <= ctxt->valueFrame) { |
+ xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_STACK_ERROR); |
+ return (NULL); |
+ } |
+ |
ctxt->valueNr--; |
if (ctxt->valueNr > 0) |
ctxt->value = ctxt->valueTab[ctxt->valueNr - 1]; |
@@ -2437,11 +2856,17 @@ valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value) |
if (ctxt->valueNr >= ctxt->valueMax) { |
xmlXPathObjectPtr *tmp; |
+ if (ctxt->valueMax >= XPATH_MAX_STACK_DEPTH) { |
+ xmlXPathErrMemory(NULL, "XPath stack depth limit reached\n"); |
+ ctxt->error = XPATH_MEMORY_ERROR; |
+ return (0); |
+ } |
tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab, |
2 * ctxt->valueMax * |
sizeof(ctxt->valueTab[0])); |
if (tmp == NULL) { |
- xmlGenericError(xmlGenericErrorContext, "realloc failed !\n"); |
+ xmlXPathErrMemory(NULL, "pushing value\n"); |
+ ctxt->error = XPATH_MEMORY_ERROR; |
return (0); |
} |
ctxt->valueMax *= 2; |
@@ -2751,6 +3176,12 @@ xmlXPathFormatNumber(double number, char buffer[], int buffersize) |
fraction_place, number); |
} |
+ /* Remove leading spaces sometimes inserted by snprintf */ |
+ while (work[0] == ' ') { |
+ for (ptr = &work[0];(ptr[0] = ptr[1]);ptr++); |
+ size--; |
+ } |
+ |
/* Remove fractional trailing zeroes */ |
after_fraction = work + size; |
ptr = after_fraction; |
@@ -2791,390 +3222,103 @@ xmlXPathFormatNumber(double number, char buffer[], int buffersize) |
* |
* Returns the number of elements found in the document or -1 in case |
* of error. |
- */ |
-long |
-xmlXPathOrderDocElems(xmlDocPtr doc) { |
- long count = 0; |
- xmlNodePtr cur; |
- |
- if (doc == NULL) |
- return(-1); |
- cur = doc->children; |
- while (cur != NULL) { |
- if (cur->type == XML_ELEMENT_NODE) { |
- cur->content = (void *) (-(++count)); |
- if (cur->children != NULL) { |
- cur = cur->children; |
- continue; |
- } |
- } |
- if (cur->next != NULL) { |
- cur = cur->next; |
- continue; |
- } |
- do { |
- cur = cur->parent; |
- if (cur == NULL) |
- break; |
- if (cur == (xmlNodePtr) doc) { |
- cur = NULL; |
- break; |
- } |
- if (cur->next != NULL) { |
- cur = cur->next; |
- break; |
- } |
- } while (cur != NULL); |
- } |
- return(count); |
-} |
- |
-/** |
- * xmlXPathCmpNodes: |
- * @node1: the first node |
- * @node2: the second node |
- * |
- * Compare two nodes w.r.t document order |
- * |
- * Returns -2 in case of error 1 if first point < second point, 0 if |
- * it's the same node, -1 otherwise |
- */ |
-int |
-xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) { |
- int depth1, depth2; |
- int attr1 = 0, attr2 = 0; |
- xmlNodePtr attrNode1 = NULL, attrNode2 = NULL; |
- xmlNodePtr cur, root; |
- |
- if ((node1 == NULL) || (node2 == NULL)) |
- return(-2); |
- /* |
- * a couple of optimizations which will avoid computations in most cases |
- */ |
- if (node1 == node2) /* trivial case */ |
- return(0); |
- if (node1->type == XML_ATTRIBUTE_NODE) { |
- attr1 = 1; |
- attrNode1 = node1; |
- node1 = node1->parent; |
- } |
- if (node2->type == XML_ATTRIBUTE_NODE) { |
- attr2 = 1; |
- attrNode2 = node2; |
- node2 = node2->parent; |
- } |
- if (node1 == node2) { |
- if (attr1 == attr2) { |
- /* not required, but we keep attributes in order */ |
- if (attr1 != 0) { |
- cur = attrNode2->prev; |
- while (cur != NULL) { |
- if (cur == attrNode1) |
- return (1); |
- cur = cur->prev; |
- } |
- return (-1); |
- } |
- return(0); |
- } |
- if (attr2 == 1) |
- return(1); |
- return(-1); |
- } |
- if ((node1->type == XML_NAMESPACE_DECL) || |
- (node2->type == XML_NAMESPACE_DECL)) |
- return(1); |
- if (node1 == node2->prev) |
- return(1); |
- if (node1 == node2->next) |
- return(-1); |
- |
- /* |
- * Speedup using document order if availble. |
- */ |
- if ((node1->type == XML_ELEMENT_NODE) && |
- (node2->type == XML_ELEMENT_NODE) && |
- (0 > (long) node1->content) && |
- (0 > (long) node2->content) && |
- (node1->doc == node2->doc)) { |
- long l1, l2; |
- |
- l1 = -((long) node1->content); |
- l2 = -((long) node2->content); |
- if (l1 < l2) |
- return(1); |
- if (l1 > l2) |
- return(-1); |
- } |
- |
- /* |
- * compute depth to root |
- */ |
- for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) { |
- if (cur == node1) |
- return(1); |
- depth2++; |
- } |
- root = cur; |
- for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) { |
- if (cur == node2) |
- return(-1); |
- depth1++; |
- } |
- /* |
- * Distinct document (or distinct entities :-( ) case. |
- */ |
- if (root != cur) { |
- return(-2); |
- } |
- /* |
- * get the nearest common ancestor. |
- */ |
- while (depth1 > depth2) { |
- depth1--; |
- node1 = node1->parent; |
- } |
- while (depth2 > depth1) { |
- depth2--; |
- node2 = node2->parent; |
- } |
- while (node1->parent != node2->parent) { |
- node1 = node1->parent; |
- node2 = node2->parent; |
- /* should not happen but just in case ... */ |
- if ((node1 == NULL) || (node2 == NULL)) |
- return(-2); |
- } |
- /* |
- * Find who's first. |
- */ |
- if (node1 == node2->prev) |
- return(1); |
- if (node1 == node2->next) |
- return(-1); |
- /* |
- * Speedup using document order if availble. |
- */ |
- if ((node1->type == XML_ELEMENT_NODE) && |
- (node2->type == XML_ELEMENT_NODE) && |
- (0 > (long) node1->content) && |
- (0 > (long) node2->content) && |
- (node1->doc == node2->doc)) { |
- long l1, l2; |
+ */ |
+long |
+xmlXPathOrderDocElems(xmlDocPtr doc) { |
+ long count = 0; |
+ xmlNodePtr cur; |
- l1 = -((long) node1->content); |
- l2 = -((long) node2->content); |
- if (l1 < l2) |
- return(1); |
- if (l1 > l2) |
- return(-1); |
+ if (doc == NULL) |
+ return(-1); |
+ cur = doc->children; |
+ while (cur != NULL) { |
+ if (cur->type == XML_ELEMENT_NODE) { |
+ cur->content = (void *) (-(++count)); |
+ if (cur->children != NULL) { |
+ cur = cur->children; |
+ continue; |
+ } |
+ } |
+ if (cur->next != NULL) { |
+ cur = cur->next; |
+ continue; |
+ } |
+ do { |
+ cur = cur->parent; |
+ if (cur == NULL) |
+ break; |
+ if (cur == (xmlNodePtr) doc) { |
+ cur = NULL; |
+ break; |
+ } |
+ if (cur->next != NULL) { |
+ cur = cur->next; |
+ break; |
+ } |
+ } while (cur != NULL); |
} |
- |
- for (cur = node1->next;cur != NULL;cur = cur->next) |
- if (cur == node2) |
- return(1); |
- return(-1); /* assume there is no sibling list corruption */ |
+ return(count); |
} |
-#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON |
/** |
- * xmlXPathCmpNodesExt: |
+ * xmlXPathCmpNodes: |
* @node1: the first node |
* @node2: the second node |
* |
- * Compare two nodes w.r.t document order. |
- * This one is optimized for handling of non-element nodes. |
+ * Compare two nodes w.r.t document order |
* |
* Returns -2 in case of error 1 if first point < second point, 0 if |
* it's the same node, -1 otherwise |
*/ |
-static int |
-xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) { |
+int |
+xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) { |
int depth1, depth2; |
- int misc = 0, precedence1 = 0, precedence2 = 0; |
- xmlNodePtr miscNode1 = NULL, miscNode2 = NULL; |
+ int attr1 = 0, attr2 = 0; |
+ xmlNodePtr attrNode1 = NULL, attrNode2 = NULL; |
xmlNodePtr cur, root; |
- long l1, l2; |
if ((node1 == NULL) || (node2 == NULL)) |
return(-2); |
- |
- if (node1 == node2) |
- return(0); |
- |
/* |
* a couple of optimizations which will avoid computations in most cases |
*/ |
- switch (node1->type) { |
- case XML_ELEMENT_NODE: |
- if (node2->type == XML_ELEMENT_NODE) { |
- if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */ |
- (0 > (long) node2->content) && |
- (node1->doc == node2->doc)) |
- { |
- l1 = -((long) node1->content); |
- l2 = -((long) node2->content); |
- if (l1 < l2) |
- return(1); |
- if (l1 > l2) |
- return(-1); |
- } else |
- goto turtle_comparison; |
- } |
- break; |
- case XML_ATTRIBUTE_NODE: |
- precedence1 = 1; /* element is owner */ |
- miscNode1 = node1; |
- node1 = node1->parent; |
- misc = 1; |
- break; |
- case XML_TEXT_NODE: |
- case XML_CDATA_SECTION_NODE: |
- case XML_COMMENT_NODE: |
- case XML_PI_NODE: { |
- miscNode1 = node1; |
- /* |
- * Find nearest element node. |
- */ |
- if (node1->prev != NULL) { |
- do { |
- node1 = node1->prev; |
- if (node1->type == XML_ELEMENT_NODE) { |
- precedence1 = 3; /* element in prev-sibl axis */ |
- break; |
- } |
- if (node1->prev == NULL) { |
- precedence1 = 2; /* element is parent */ |
- /* |
- * URGENT TODO: Are there any cases, where the |
- * parent of such a node is not an element node? |
- */ |
- node1 = node1->parent; |
- break; |
- } |
- } while (1); |
- } else { |
- precedence1 = 2; /* element is parent */ |
- node1 = node1->parent; |
- } |
- if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) || |
- (0 <= (long) node1->content)) { |
- /* |
- * Fallback for whatever case. |
- */ |
- node1 = miscNode1; |
- precedence1 = 0; |
- } else |
- misc = 1; |
- } |
- break; |
- case XML_NAMESPACE_DECL: |
- /* |
- * TODO: why do we return 1 for namespace nodes? |
- */ |
- return(1); |
- default: |
- break; |
+ if (node1 == node2) /* trivial case */ |
+ return(0); |
+ if (node1->type == XML_ATTRIBUTE_NODE) { |
+ attr1 = 1; |
+ attrNode1 = node1; |
+ node1 = node1->parent; |
} |
- switch (node2->type) { |
- case XML_ELEMENT_NODE: |
- break; |
- case XML_ATTRIBUTE_NODE: |
- precedence2 = 1; /* element is owner */ |
- miscNode2 = node2; |
- node2 = node2->parent; |
- misc = 1; |
- break; |
- case XML_TEXT_NODE: |
- case XML_CDATA_SECTION_NODE: |
- case XML_COMMENT_NODE: |
- case XML_PI_NODE: { |
- miscNode2 = node2; |
- if (node2->prev != NULL) { |
- do { |
- node2 = node2->prev; |
- if (node2->type == XML_ELEMENT_NODE) { |
- precedence2 = 3; /* element in prev-sibl axis */ |
- break; |
- } |
- if (node2->prev == NULL) { |
- precedence2 = 2; /* element is parent */ |
- node2 = node2->parent; |
- break; |
- } |
- } while (1); |
- } else { |
- precedence2 = 2; /* element is parent */ |
- node2 = node2->parent; |
- } |
- if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) || |
- (0 <= (long) node1->content)) |
- { |
- node2 = miscNode2; |
- precedence2 = 0; |
- } else |
- misc = 1; |
- } |
- break; |
- case XML_NAMESPACE_DECL: |
- return(1); |
- default: |
- break; |
+ if (node2->type == XML_ATTRIBUTE_NODE) { |
+ attr2 = 1; |
+ attrNode2 = node2; |
+ node2 = node2->parent; |
} |
- if (misc) { |
- if (node1 == node2) { |
- if (precedence1 == precedence2) { |
- /* |
- * The ugly case; but normally there aren't many |
- * adjacent non-element nodes around. |
- */ |
- cur = miscNode2->prev; |
+ if (node1 == node2) { |
+ if (attr1 == attr2) { |
+ /* not required, but we keep attributes in order */ |
+ if (attr1 != 0) { |
+ cur = attrNode2->prev; |
while (cur != NULL) { |
- if (cur == miscNode1) |
- return(1); |
- if (cur->type == XML_ELEMENT_NODE) |
- return(-1); |
+ if (cur == attrNode1) |
+ return (1); |
cur = cur->prev; |
} |
return (-1); |
- } else { |
- /* |
- * Evaluate based on higher precedence wrt to the element. |
- * TODO: This assumes attributes are sorted before content. |
- * Is this 100% correct? |
- */ |
- if (precedence1 < precedence2) |
- return(1); |
- else |
- return(-1); |
- } |
- } |
- /* |
- * Special case: One of the helper-elements is contained by the other. |
- * <foo> |
- * <node2> |
- * <node1>Text-1(precedence1 == 2)</node1> |
- * </node2> |
- * Text-6(precedence2 == 3) |
- * </foo> |
- */ |
- if ((precedence2 == 3) && (precedence1 > 1)) { |
- cur = node1->parent; |
- while (cur) { |
- if (cur == node2) |
- return(1); |
- cur = cur->parent; |
- } |
- } |
- if ((precedence1 == 3) && (precedence2 > 1)) { |
- cur = node2->parent; |
- while (cur) { |
- if (cur == node1) |
- return(-1); |
- cur = cur->parent; |
} |
+ return(0); |
} |
+ if (attr2 == 1) |
+ return(1); |
+ return(-1); |
} |
+ if ((node1->type == XML_NAMESPACE_DECL) || |
+ (node2->type == XML_NAMESPACE_DECL)) |
+ return(1); |
+ if (node1 == node2->prev) |
+ return(1); |
+ if (node1 == node2->next) |
+ return(-1); |
/* |
* Speedup using document order if availble. |
@@ -3184,6 +3328,7 @@ xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) { |
(0 > (long) node1->content) && |
(0 > (long) node2->content) && |
(node1->doc == node2->doc)) { |
+ long l1, l2; |
l1 = -((long) node1->content); |
l2 = -((long) node2->content); |
@@ -3191,14 +3336,8 @@ xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) { |
return(1); |
if (l1 > l2) |
return(-1); |
- } |
- |
-turtle_comparison: |
+ } |
- if (node1 == node2->prev) |
- return(1); |
- if (node1 == node2->next) |
- return(-1); |
/* |
* compute depth to root |
*/ |
@@ -3252,6 +3391,7 @@ turtle_comparison: |
(0 > (long) node1->content) && |
(0 > (long) node2->content) && |
(node1->doc == node2->doc)) { |
+ long l1, l2; |
l1 = -((long) node1->content); |
l2 = -((long) node2->content); |
@@ -3266,7 +3406,6 @@ turtle_comparison: |
return(1); |
return(-1); /* assume there is no sibling list corruption */ |
} |
-#endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */ |
/** |
* xmlXPathNodeSetSort: |
@@ -3276,13 +3415,19 @@ turtle_comparison: |
*/ |
void |
xmlXPathNodeSetSort(xmlNodeSetPtr set) { |
+#ifndef WITH_TIM_SORT |
int i, j, incr, len; |
xmlNodePtr tmp; |
+#endif |
if (set == NULL) |
return; |
- /* Use Shell's sort to sort the node-set */ |
+#ifndef WITH_TIM_SORT |
+ /* |
+ * Use the old Shell's sort implementation to sort the node-set |
+ * Timsort ought to be quite faster |
+ */ |
len = set->nodeNr; |
for (incr = len / 2; incr > 0; incr /= 2) { |
for (i = incr; i < len; i++) { |
@@ -3305,6 +3450,9 @@ xmlXPathNodeSetSort(xmlNodeSetPtr set) { |
} |
} |
} |
+#else /* WITH_TIM_SORT */ |
+ libxml_domnode_tim_sort(set->nodeTab, set->nodeNr); |
+#endif /* WITH_TIM_SORT */ |
} |
#define XML_NODESET_DEFAULT 10 |
@@ -3483,8 +3631,10 @@ xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) { |
* @ns: a the namespace node |
* |
* add a new namespace node to an existing NodeSet |
+ * |
+ * Returns 0 in case of success and -1 in case of error |
*/ |
-void |
+int |
xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
int i; |
@@ -3492,7 +3642,7 @@ xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
if ((cur == NULL) || (ns == NULL) || (node == NULL) || |
(ns->type != XML_NAMESPACE_DECL) || |
(node->type != XML_ELEMENT_NODE)) |
- return; |
+ return(-1); |
/* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
/* |
@@ -3503,7 +3653,7 @@ xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
(cur->nodeTab[i]->type == XML_NAMESPACE_DECL) && |
(((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) && |
(xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix))) |
- return; |
+ return(0); |
} |
/* |
@@ -3514,7 +3664,7 @@ xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
sizeof(xmlNodePtr)); |
if (cur->nodeTab == NULL) { |
xmlXPathErrMemory(NULL, "growing nodeset\n"); |
- return; |
+ return(-1); |
} |
memset(cur->nodeTab, 0 , |
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
@@ -3522,16 +3672,21 @@ xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
} else if (cur->nodeNr == cur->nodeMax) { |
xmlNodePtr *temp; |
- cur->nodeMax *= 2; |
- temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * |
+ if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) { |
+ xmlXPathErrMemory(NULL, "growing nodeset hit limit\n"); |
+ return(-1); |
+ } |
+ temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 * |
sizeof(xmlNodePtr)); |
if (temp == NULL) { |
xmlXPathErrMemory(NULL, "growing nodeset\n"); |
- return; |
+ return(-1); |
} |
+ cur->nodeMax *= 2; |
cur->nodeTab = temp; |
} |
cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns); |
+ return(0); |
} |
/** |
@@ -3540,24 +3695,21 @@ xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) { |
* @val: a new xmlNodePtr |
* |
* add a new xmlNodePtr to an existing NodeSet |
+ * |
+ * Returns 0 in case of success, and -1 in case of error |
*/ |
-void |
+int |
xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
int i; |
- if ((cur == NULL) || (val == NULL)) return; |
- |
-#if 0 |
- if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) |
- return; /* an XSLT fake node */ |
-#endif |
+ if ((cur == NULL) || (val == NULL)) return(-1); |
/* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
/* |
* prevent duplcates |
*/ |
for (i = 0;i < cur->nodeNr;i++) |
- if (cur->nodeTab[i] == val) return; |
+ if (cur->nodeTab[i] == val) return(0); |
/* |
* grow the nodeTab if needed |
@@ -3567,7 +3719,7 @@ xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
sizeof(xmlNodePtr)); |
if (cur->nodeTab == NULL) { |
xmlXPathErrMemory(NULL, "growing nodeset\n"); |
- return; |
+ return(-1); |
} |
memset(cur->nodeTab, 0 , |
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
@@ -3575,13 +3727,17 @@ xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
} else if (cur->nodeNr == cur->nodeMax) { |
xmlNodePtr *temp; |
- cur->nodeMax *= 2; |
- temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * |
+ if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) { |
+ xmlXPathErrMemory(NULL, "growing nodeset hit limit\n"); |
+ return(-1); |
+ } |
+ temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 * |
sizeof(xmlNodePtr)); |
if (temp == NULL) { |
xmlXPathErrMemory(NULL, "growing nodeset\n"); |
- return; |
+ return(-1); |
} |
+ cur->nodeMax *= 2; |
cur->nodeTab = temp; |
} |
if (val->type == XML_NAMESPACE_DECL) { |
@@ -3591,6 +3747,7 @@ xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
} else |
cur->nodeTab[cur->nodeNr++] = val; |
+ return(0); |
} |
/** |
@@ -3600,15 +3757,12 @@ xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) { |
* |
* add a new xmlNodePtr to an existing NodeSet, optimized version |
* when we are sure the node is not already in the set. |
+ * |
+ * Returns 0 in case of success and -1 in case of failure |
*/ |
-void |
+int |
xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { |
- if ((cur == NULL) || (val == NULL)) return; |
- |
-#if 0 |
- if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' ')) |
- return; /* an XSLT fake node */ |
-#endif |
+ if ((cur == NULL) || (val == NULL)) return(-1); |
/* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
/* |
@@ -3619,7 +3773,7 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { |
sizeof(xmlNodePtr)); |
if (cur->nodeTab == NULL) { |
xmlXPathErrMemory(NULL, "growing nodeset\n"); |
- return; |
+ return(-1); |
} |
memset(cur->nodeTab, 0 , |
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
@@ -3627,14 +3781,18 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { |
} else if (cur->nodeNr == cur->nodeMax) { |
xmlNodePtr *temp; |
- cur->nodeMax *= 2; |
- temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * |
+ if (cur->nodeMax >= XPATH_MAX_NODESET_LENGTH) { |
+ xmlXPathErrMemory(NULL, "growing nodeset hit limit\n"); |
+ return(-1); |
+ } |
+ temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 * |
sizeof(xmlNodePtr)); |
if (temp == NULL) { |
xmlXPathErrMemory(NULL, "growing nodeset\n"); |
- return; |
+ return(-1); |
} |
cur->nodeTab = temp; |
+ cur->nodeMax *= 2; |
} |
if (val->type == XML_NAMESPACE_DECL) { |
xmlNsPtr ns = (xmlNsPtr) val; |
@@ -3643,6 +3801,7 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { |
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
} else |
cur->nodeTab[cur->nodeNr++] = val; |
+ return(0); |
} |
/** |
@@ -3738,14 +3897,18 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { |
} else if (val1->nodeNr == val1->nodeMax) { |
xmlNodePtr *temp; |
- val1->nodeMax *= 2; |
- temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * |
+ if (val1->nodeMax >= XPATH_MAX_NODESET_LENGTH) { |
+ xmlXPathErrMemory(NULL, "merging nodeset hit limit\n"); |
+ return(NULL); |
+ } |
+ temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * 2 * |
sizeof(xmlNodePtr)); |
if (temp == NULL) { |
xmlXPathErrMemory(NULL, "merging nodeset\n"); |
return(NULL); |
} |
val1->nodeTab = temp; |
+ val1->nodeMax *= 2; |
} |
if (n2->type == XML_NAMESPACE_DECL) { |
xmlNsPtr ns = (xmlNsPtr) n2; |
@@ -3759,68 +3922,6 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { |
return(val1); |
} |
-#if 0 /* xmlXPathNodeSetMergeUnique() is currently not used anymore */ |
-/** |
- * xmlXPathNodeSetMergeUnique: |
- * @val1: the first NodeSet or NULL |
- * @val2: the second NodeSet |
- * |
- * Merges two nodesets, all nodes from @val2 are added to @val1 |
- * if @val1 is NULL, a new set is created and copied from @val2 |
- * |
- * Returns @val1 once extended or NULL in case of error. |
- */ |
-static xmlNodeSetPtr |
-xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { |
- int i; |
- |
- if (val2 == NULL) return(val1); |
- if (val1 == NULL) { |
- val1 = xmlXPathNodeSetCreate(NULL); |
- } |
- if (val1 == NULL) |
- return (NULL); |
- |
- /* @@ with_ns to check whether namespace nodes should be looked at @@ */ |
- |
- for (i = 0;i < val2->nodeNr;i++) { |
- /* |
- * grow the nodeTab if needed |
- */ |
- if (val1->nodeMax == 0) { |
- val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * |
- sizeof(xmlNodePtr)); |
- if (val1->nodeTab == NULL) { |
- xmlXPathErrMemory(NULL, "merging nodeset\n"); |
- return(NULL); |
- } |
- memset(val1->nodeTab, 0 , |
- XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr)); |
- val1->nodeMax = XML_NODESET_DEFAULT; |
- } else if (val1->nodeNr == val1->nodeMax) { |
- xmlNodePtr *temp; |
- |
- val1->nodeMax *= 2; |
- temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax * |
- sizeof(xmlNodePtr)); |
- if (temp == NULL) { |
- xmlXPathErrMemory(NULL, "merging nodeset\n"); |
- return(NULL); |
- } |
- val1->nodeTab = temp; |
- } |
- if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) { |
- xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i]; |
- |
- val1->nodeTab[val1->nodeNr++] = |
- xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns); |
- } else |
- val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i]; |
- } |
- |
- return(val1); |
-} |
-#endif /* xmlXPathNodeSetMergeUnique() is currently not used anymore */ |
/** |
* xmlXPathNodeSetMergeAndClear: |
@@ -3907,14 +4008,18 @@ xmlXPathNodeSetMergeAndClear(xmlNodeSetPtr set1, xmlNodeSetPtr set2, |
} else if (set1->nodeNr >= set1->nodeMax) { |
xmlNodePtr *temp; |
- set1->nodeMax *= 2; |
+ if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) { |
+ xmlXPathErrMemory(NULL, "merging nodeset hit limit\n"); |
+ return(NULL); |
+ } |
temp = (xmlNodePtr *) xmlRealloc( |
- set1->nodeTab, set1->nodeMax * sizeof(xmlNodePtr)); |
+ set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr)); |
if (temp == NULL) { |
xmlXPathErrMemory(NULL, "merging nodeset\n"); |
return(NULL); |
} |
set1->nodeTab = temp; |
+ set1->nodeMax *= 2; |
} |
if (n2->type == XML_NAMESPACE_DECL) { |
xmlNsPtr ns = (xmlNsPtr) n2; |
@@ -3991,14 +4096,18 @@ xmlXPathNodeSetMergeAndClearNoDupls(xmlNodeSetPtr set1, xmlNodeSetPtr set2, |
} else if (set1->nodeNr >= set1->nodeMax) { |
xmlNodePtr *temp; |
- set1->nodeMax *= 2; |
+ if (set1->nodeMax >= XPATH_MAX_NODESET_LENGTH) { |
+ xmlXPathErrMemory(NULL, "merging nodeset hit limit\n"); |
+ return(NULL); |
+ } |
temp = (xmlNodePtr *) xmlRealloc( |
- set1->nodeTab, set1->nodeMax * sizeof(xmlNodePtr)); |
+ set1->nodeTab, set1->nodeMax * 2 * sizeof(xmlNodePtr)); |
if (temp == NULL) { |
xmlXPathErrMemory(NULL, "merging nodeset\n"); |
return(NULL); |
} |
set1->nodeTab = temp; |
+ set1->nodeMax *= 2; |
} |
set1->nodeTab[set1->nodeNr++] = n2; |
} |
@@ -4289,9 +4398,12 @@ xmlXPathNewNodeSetList(xmlNodeSetPtr val) |
ret = xmlXPathNewNodeSet(NULL); |
else { |
ret = xmlXPathNewNodeSet(val->nodeTab[0]); |
- if (ret) |
- for (i = 1; i < val->nodeNr; ++i) |
- xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]); |
+ if (ret) { |
+ for (i = 1; i < val->nodeNr; ++i) { |
+ if (xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]) |
+ < 0) break; |
+ } |
+ } |
} |
return (ret); |
@@ -4367,8 +4479,10 @@ xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
for (i = 0; i < l1; i++) { |
cur = xmlXPathNodeSetItem(nodes1, i); |
- if (!xmlXPathNodeSetContains(nodes2, cur)) |
- xmlXPathNodeSetAddUnique(ret, cur); |
+ if (!xmlXPathNodeSetContains(nodes2, cur)) { |
+ if (xmlXPathNodeSetAddUnique(ret, cur) < 0) |
+ break; |
+ } |
} |
return(ret); |
} |
@@ -4401,8 +4515,10 @@ xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) { |
for (i = 0; i < l1; i++) { |
cur = xmlXPathNodeSetItem(nodes1, i); |
- if (xmlXPathNodeSetContains(nodes2, cur)) |
- xmlXPathNodeSetAddUnique(ret, cur); |
+ if (xmlXPathNodeSetContains(nodes2, cur)) { |
+ if (xmlXPathNodeSetAddUnique(ret, cur) < 0) |
+ break; |
+ } |
} |
return(ret); |
} |
@@ -4438,7 +4554,8 @@ xmlXPathDistinctSorted (xmlNodeSetPtr nodes) { |
strval = xmlXPathCastNodeToString(cur); |
if (xmlHashLookup(hash, strval) == NULL) { |
xmlHashAddEntry(hash, strval, strval); |
- xmlXPathNodeSetAddUnique(ret, cur); |
+ if (xmlXPathNodeSetAddUnique(ret, cur) < 0) |
+ break; |
} else { |
xmlFree(strval); |
} |
@@ -4530,7 +4647,8 @@ xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { |
cur = xmlXPathNodeSetItem(nodes, i); |
if (cur == node) |
break; |
- xmlXPathNodeSetAddUnique(ret, cur); |
+ if (xmlXPathNodeSetAddUnique(ret, cur) < 0) |
+ break; |
} |
return(ret); |
} |
@@ -4634,7 +4752,8 @@ xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) { |
cur = xmlXPathNodeSetItem(nodes, i); |
if (cur == node) |
break; |
- xmlXPathNodeSetAddUnique(ret, cur); |
+ if (xmlXPathNodeSetAddUnique(ret, cur) < 0) |
+ break; |
} |
xmlXPathNodeSetSort(ret); /* bug 413451 */ |
return(ret); |
@@ -6154,6 +6273,7 @@ xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) { |
ret->valueNr = 0; |
ret->valueMax = 10; |
ret->value = NULL; |
+ ret->valueFrame = 0; |
ret->context = ctxt; |
ret->comp = comp; |
@@ -7656,6 +7776,7 @@ xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
return(NULL); |
} |
+#if 0 |
/** |
* xmlXPathNextDescendantOrSelfElemParent: |
* @ctxt: the XPath Parser context |
@@ -7683,7 +7804,7 @@ xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur, |
#ifdef LIBXML_DOCB_ENABLED |
case XML_DOCB_DOCUMENT_NODE: |
#endif |
- case XML_HTML_DOCUMENT_NODE: |
+ case XML_HTML_DOCUMENT_NODE: |
return(contextNode); |
default: |
return(NULL); |
@@ -7731,6 +7852,7 @@ next_sibling: |
} |
return(NULL); |
} |
+#endif |
/** |
* xmlXPathNextDescendant: |
@@ -7758,6 +7880,8 @@ xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
return(ctxt->context->node->children); |
} |
+ if (cur->type == XML_NAMESPACE_DECL) |
+ return(NULL); |
if (cur->children != NULL) { |
/* |
* Do not descend on entities declarations |
@@ -8140,6 +8264,10 @@ xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { |
static int |
xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) { |
if ((ancestor == NULL) || (node == NULL)) return(0); |
+ if (node->type == XML_NAMESPACE_DECL) |
+ return(0); |
+ if (ancestor->type == XML_NAMESPACE_DECL) |
+ return(0); |
/* nodes need to be in the same document */ |
if (ancestor->doc != node->doc) return(0); |
/* avoid searching if ancestor or node is the root node */ |
@@ -8177,7 +8305,7 @@ xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) |
if (cur->type == XML_ATTRIBUTE_NODE) |
return(cur->parent); |
} |
- if (cur == NULL) |
+ if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) |
return (NULL); |
if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) |
cur = cur->prev; |
@@ -8224,6 +8352,8 @@ xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt, |
return (NULL); |
ctxt->ancestor = cur->parent; |
} |
+ if (cur->type == XML_NAMESPACE_DECL) |
+ return(NULL); |
if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) |
cur = cur->prev; |
while (cur->prev == NULL) { |
@@ -8431,7 +8561,7 @@ xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
int i = 0; |
tmp = cur->nodesetval->nodeTab[0]; |
- if (tmp != NULL) { |
+ if ((tmp != NULL) && (tmp->type != XML_NAMESPACE_DECL)) { |
tmp = tmp->children; |
while (tmp != NULL) { |
tmp = tmp->next; |
@@ -9101,7 +9231,7 @@ void |
xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
xmlXPathObjectPtr str; |
xmlXPathObjectPtr find; |
- xmlBufferPtr target; |
+ xmlBufPtr target; |
const xmlChar *point; |
int offset; |
@@ -9111,16 +9241,16 @@ xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
CAST_TO_STRING; |
str = valuePop(ctxt); |
- target = xmlBufferCreate(); |
+ target = xmlBufCreate(); |
if (target) { |
point = xmlStrstr(str->stringval, find->stringval); |
if (point) { |
offset = (int)(point - str->stringval); |
- xmlBufferAdd(target, str->stringval, offset); |
+ xmlBufAdd(target, str->stringval, offset); |
} |
valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, |
- xmlBufferContent(target))); |
- xmlBufferFree(target); |
+ xmlBufContent(target))); |
+ xmlBufFree(target); |
} |
xmlXPathReleaseObject(ctxt->context, str); |
xmlXPathReleaseObject(ctxt->context, find); |
@@ -9144,7 +9274,7 @@ void |
xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
xmlXPathObjectPtr str; |
xmlXPathObjectPtr find; |
- xmlBufferPtr target; |
+ xmlBufPtr target; |
const xmlChar *point; |
int offset; |
@@ -9154,17 +9284,17 @@ xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
CAST_TO_STRING; |
str = valuePop(ctxt); |
- target = xmlBufferCreate(); |
+ target = xmlBufCreate(); |
if (target) { |
point = xmlStrstr(str->stringval, find->stringval); |
if (point) { |
offset = (int)(point - str->stringval) + xmlStrlen(find->stringval); |
- xmlBufferAdd(target, &str->stringval[offset], |
+ xmlBufAdd(target, &str->stringval[offset], |
xmlStrlen(str->stringval) - offset); |
} |
valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, |
- xmlBufferContent(target))); |
- xmlBufferFree(target); |
+ xmlBufContent(target))); |
+ xmlBufFree(target); |
} |
xmlXPathReleaseObject(ctxt->context, str); |
xmlXPathReleaseObject(ctxt->context, find); |
@@ -9188,7 +9318,7 @@ void |
xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
xmlXPathObjectPtr obj = NULL; |
xmlChar *source = NULL; |
- xmlBufferPtr target; |
+ xmlBufPtr target; |
xmlChar blank; |
if (ctxt == NULL) return; |
@@ -9206,7 +9336,7 @@ xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
obj = valuePop(ctxt); |
source = obj->stringval; |
- target = xmlBufferCreate(); |
+ target = xmlBufCreate(); |
if (target && source) { |
/* Skip leading whitespaces */ |
@@ -9220,16 +9350,16 @@ xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
blank = 0x20; |
} else { |
if (blank) { |
- xmlBufferAdd(target, &blank, 1); |
+ xmlBufAdd(target, &blank, 1); |
blank = 0; |
} |
- xmlBufferAdd(target, source, 1); |
+ xmlBufAdd(target, source, 1); |
} |
source++; |
} |
valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, |
- xmlBufferContent(target))); |
- xmlBufferFree(target); |
+ xmlBufContent(target))); |
+ xmlBufFree(target); |
} |
xmlXPathReleaseObject(ctxt->context, obj); |
} |
@@ -9260,7 +9390,7 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
xmlXPathObjectPtr str; |
xmlXPathObjectPtr from; |
xmlXPathObjectPtr to; |
- xmlBufferPtr target; |
+ xmlBufPtr target; |
int offset, max; |
xmlChar ch; |
const xmlChar *point; |
@@ -9275,7 +9405,7 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
CAST_TO_STRING; |
str = valuePop(ctxt); |
- target = xmlBufferCreate(); |
+ target = xmlBufCreate(); |
if (target) { |
max = xmlUTF8Strlen(to->stringval); |
for (cptr = str->stringval; (ch=*cptr); ) { |
@@ -9284,10 +9414,10 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
if (offset < max) { |
point = xmlUTF8Strpos(to->stringval, offset); |
if (point) |
- xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1)); |
+ xmlBufAdd(target, point, xmlUTF8Strsize(point, 1)); |
} |
} else |
- xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1)); |
+ xmlBufAdd(target, cptr, xmlUTF8Strsize(cptr, 1)); |
/* Step to next character in input */ |
cptr++; |
@@ -9296,6 +9426,7 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
if ( (ch & 0xc0) != 0xc0 ) { |
xmlGenericError(xmlGenericErrorContext, |
"xmlXPathTranslateFunction: Invalid UTF8 string\n"); |
+ /* not asserting an XPath error is probably better */ |
break; |
} |
/* then skip over remaining bytes for this char */ |
@@ -9303,6 +9434,7 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
if ( (*cptr++ & 0xc0) != 0x80 ) { |
xmlGenericError(xmlGenericErrorContext, |
"xmlXPathTranslateFunction: Invalid UTF8 string\n"); |
+ /* not asserting an XPath error is probably better */ |
break; |
} |
if (ch & 0x80) /* must have had error encountered */ |
@@ -9311,8 +9443,8 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
} |
} |
valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, |
- xmlBufferContent(target))); |
- xmlBufferFree(target); |
+ xmlBufContent(target))); |
+ xmlBufFree(target); |
xmlXPathReleaseObject(ctxt->context, str); |
xmlXPathReleaseObject(ctxt->context, from); |
xmlXPathReleaseObject(ctxt->context, to); |
@@ -9819,7 +9951,7 @@ xmlChar * |
xmlXPathParseName(xmlXPathParserContextPtr ctxt) { |
const xmlChar *in; |
xmlChar *ret; |
- int count = 0; |
+ size_t count = 0; |
if ((ctxt == NULL) || (ctxt->cur == NULL)) return(NULL); |
/* |
@@ -9838,6 +9970,10 @@ xmlXPathParseName(xmlXPathParserContextPtr ctxt) { |
in++; |
if ((*in > 0) && (*in < 0x80)) { |
count = in - ctxt->cur; |
+ if (count > XML_MAX_NAME_LENGTH) { |
+ ctxt->cur = in; |
+ XP_ERRORNULL(XPATH_EXPR_ERROR); |
+ } |
ret = xmlStrndup(ctxt->cur, count); |
ctxt->cur = in; |
return(ret); |
@@ -9881,6 +10017,9 @@ xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) { |
xmlChar *buffer; |
int max = len * 2; |
+ if (len > XML_MAX_NAME_LENGTH) { |
+ XP_ERRORNULL(XPATH_EXPR_ERROR); |
+ } |
buffer = (xmlChar *) xmlMallocAtomic(max * sizeof(xmlChar)); |
if (buffer == NULL) { |
XP_ERRORNULL(XPATH_MEMORY_ERROR); |
@@ -9892,6 +10031,9 @@ xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified) { |
(IS_COMBINING(c)) || |
(IS_EXTENDER(c))) { |
if (len + 10 > max) { |
+ if (max > XML_MAX_NAME_LENGTH) { |
+ XP_ERRORNULL(XPATH_EXPR_ERROR); |
+ } |
max *= 2; |
buffer = (xmlChar *) xmlRealloc(buffer, |
max * sizeof(xmlChar)); |
@@ -10044,7 +10186,6 @@ static void |
xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) |
{ |
double ret = 0.0; |
- double mult = 1; |
int ok = 0; |
int exponent = 0; |
int is_exponent_negative = 0; |
@@ -10080,15 +10221,23 @@ xmlXPathCompNumber(xmlXPathParserContextPtr ctxt) |
} |
#endif |
if (CUR == '.') { |
+ int v, frac = 0; |
+ double fraction = 0; |
+ |
NEXT; |
if (((CUR < '0') || (CUR > '9')) && (!ok)) { |
XP_ERROR(XPATH_NUMBER_ERROR); |
} |
- while ((CUR >= '0') && (CUR <= '9')) { |
- mult /= 10; |
- ret = ret + (CUR - '0') * mult; |
+ while ((CUR >= '0') && (CUR <= '9') && (frac < MAX_FRAC)) { |
+ v = (CUR - '0'); |
+ fraction = fraction * 10 + v; |
+ frac = frac + 1; |
NEXT; |
} |
+ fraction /= my_pow10[frac]; |
+ ret = ret + fraction; |
+ while ((CUR >= '0') && (CUR <= '9')) |
+ NEXT; |
} |
if ((CUR == 'e') || (CUR == 'E')) { |
NEXT; |
@@ -10237,7 +10386,7 @@ xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) { |
name, prefix); |
SKIP_BLANKS; |
if ((ctxt->context != NULL) && (ctxt->context->flags & XML_XPATH_NOVAR)) { |
- XP_ERROR(XPATH_UNDEF_VARIABLE_ERROR); |
+ XP_ERROR(XPATH_FORBID_VARIABLE_ERROR); |
} |
} |
@@ -11262,7 +11411,10 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { |
} |
} |
- CHECK_ERROR; |
+ if (ctxt->error != XPATH_EXPRESSION_OK) { |
+ xmlFree(name); |
+ return; |
+ } |
name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name); |
if (test == 0) |
@@ -11598,9 +11750,13 @@ xmlXPathCompOpEvalPredicate(xmlXPathParserContextPtr ctxt, |
*/ |
if (contextObj == NULL) |
contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode); |
- else |
- xmlXPathNodeSetAddUnique(contextObj->nodesetval, |
- contextNode); |
+ else { |
+ if (xmlXPathNodeSetAddUnique(contextObj->nodesetval, |
+ contextNode) < 0) { |
+ ctxt->error = XPATH_MEMORY_ERROR; |
+ goto evaluation_exit; |
+ } |
+ } |
valuePush(ctxt, contextObj); |
@@ -11701,6 +11857,7 @@ xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt, |
xmlXPathObjectPtr contextObj = NULL, exprRes = NULL; |
xmlNodePtr oldContextNode, contextNode = NULL; |
xmlXPathContextPtr xpctxt = ctxt->context; |
+ int frame; |
#ifdef LIBXML_XPTR_ENABLED |
/* |
@@ -11720,6 +11877,8 @@ xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt, |
*/ |
exprOp = &ctxt->comp->steps[op->ch2]; |
for (i = 0; i < set->nodeNr; i++) { |
+ xmlXPathObjectPtr tmp; |
+ |
if (set->nodeTab[i] == NULL) |
continue; |
@@ -11743,17 +11902,21 @@ xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt, |
*/ |
if (contextObj == NULL) |
contextObj = xmlXPathCacheNewNodeSet(xpctxt, contextNode); |
- else |
- xmlXPathNodeSetAddUnique(contextObj->nodesetval, |
- contextNode); |
+ else { |
+ if (xmlXPathNodeSetAddUnique(contextObj->nodesetval, |
+ contextNode) < 0) { |
+ ctxt->error = XPATH_MEMORY_ERROR; |
+ goto evaluation_exit; |
+ } |
+ } |
+ frame = xmlXPathSetFrame(ctxt); |
valuePush(ctxt, contextObj); |
res = xmlXPathCompOpEvalToBoolean(ctxt, exprOp, 1); |
+ tmp = valuePop(ctxt); |
+ xmlXPathPopFrame(ctxt, frame); |
if ((ctxt->error != XPATH_EXPRESSION_OK) || (res == -1)) { |
- xmlXPathObjectPtr tmp; |
- /* pop the result if any */ |
- tmp = valuePop(ctxt); |
while (tmp != contextObj) { |
/* |
* Free up the result |
@@ -11764,6 +11927,8 @@ xmlXPathCompOpEvalPositionalPredicate(xmlXPathParserContextPtr ctxt, |
} |
goto evaluation_error; |
} |
+ /* push the result back onto the stack */ |
+ valuePush(ctxt, tmp); |
if (res) |
pos++; |
@@ -11915,22 +12080,25 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
#define XP_TEST_HIT \ |
if (hasAxisRange != 0) { \ |
if (++pos == maxPos) { \ |
- addNode(seq, cur); \ |
- goto axis_range_end; } \ |
+ if (addNode(seq, cur) < 0) \ |
+ ctxt->error = XPATH_MEMORY_ERROR; \ |
+ goto axis_range_end; } \ |
} else { \ |
- addNode(seq, cur); \ |
+ if (addNode(seq, cur) < 0) \ |
+ ctxt->error = XPATH_MEMORY_ERROR; \ |
if (breakOnFirstHit) goto first_hit; } |
#define XP_TEST_HIT_NS \ |
if (hasAxisRange != 0) { \ |
if (++pos == maxPos) { \ |
hasNsNodes = 1; \ |
- xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur); \ |
+ if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \ |
+ ctxt->error = XPATH_MEMORY_ERROR; \ |
goto axis_range_end; } \ |
} else { \ |
hasNsNodes = 1; \ |
- xmlXPathNodeSetAddNs(seq, \ |
- xpctxt->node, (xmlNsPtr) cur); \ |
+ if (xmlXPathNodeSetAddNs(seq, xpctxt->node, (xmlNsPtr) cur) < 0) \ |
+ ctxt->error = XPATH_MEMORY_ERROR; \ |
if (breakOnFirstHit) goto first_hit; } |
xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value; |
@@ -11950,8 +12118,6 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
xmlNodeSetPtr contextSeq; |
int contextIdx; |
xmlNodePtr contextNode; |
- /* The context node for a compound traversal */ |
- xmlNodePtr outerContextNode; |
/* The final resulting node set wrt to all context nodes */ |
xmlNodeSetPtr outSeq; |
/* |
@@ -11967,9 +12133,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
int breakOnFirstHit; |
xmlXPathTraversalFunction next = NULL; |
- /* compound axis traversal */ |
- xmlXPathTraversalFunctionExt outerNext = NULL; |
- void (*addNode) (xmlNodeSetPtr, xmlNodePtr); |
+ int (*addNode) (xmlNodeSetPtr, xmlNodePtr); |
xmlXPathNodeSetMergeFunction mergeAndClear; |
xmlNodePtr oldContextNode; |
xmlXPathContextPtr xpctxt = ctxt->context; |
@@ -12018,13 +12182,6 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
break; |
case AXIS_CHILD: |
last = NULL; |
- if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) { |
- /* |
- * This iterator will give us only nodes which can |
- * hold element nodes. |
- */ |
- outerNext = xmlXPathNextDescendantOrSelfElemParent; |
- } |
if (((test == NODE_TEST_NAME) || (test == NODE_TEST_ALL)) && |
(type == NODE_TYPE_NODE)) |
{ |
@@ -12154,32 +12311,13 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
addNode = xmlXPathNodeSetAddUnique; |
outSeq = NULL; |
seq = NULL; |
- outerContextNode = NULL; |
contextNode = NULL; |
contextIdx = 0; |
- while ((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) { |
- if (outerNext != NULL) { |
- /* |
- * This is a compound traversal. |
- */ |
- if (contextNode == NULL) { |
- /* |
- * Set the context for the outer traversal. |
- */ |
- outerContextNode = contextSeq->nodeTab[contextIdx++]; |
- contextNode = outerNext(NULL, outerContextNode); |
- } else |
- contextNode = outerNext(contextNode, outerContextNode); |
- if (contextNode == NULL) |
- continue; |
- /* |
- * Set the context for the main traversal. |
- */ |
- xpctxt->node = contextNode; |
- } else |
- xpctxt->node = contextSeq->nodeTab[contextIdx++]; |
+ while (((contextIdx < contextSeq->nodeNr) || (contextNode != NULL)) && |
+ (ctxt->error == XPATH_EXPRESSION_OK)) { |
+ xpctxt->node = contextSeq->nodeTab[contextIdx++]; |
if (seq == NULL) { |
seq = xmlXPathNodeSetCreate(NULL); |
@@ -12287,7 +12425,14 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
if (axis == AXIS_ATTRIBUTE) { |
if (cur->type == XML_ATTRIBUTE_NODE) |
{ |
- XP_TEST_HIT |
+ if (prefix == NULL) |
+ { |
+ XP_TEST_HIT |
+ } else if ((cur->ns != NULL) && |
+ (xmlStrEqual(URI, cur->ns->href))) |
+ { |
+ XP_TEST_HIT |
+ } |
} |
} else if (axis == AXIS_NAMESPACE) { |
if (cur->type == XML_NAMESPACE_DECL) |
@@ -12377,7 +12522,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, |
} |
break; |
} /* switch(test) */ |
- } while (cur != NULL); |
+ } while ((cur != NULL) && (ctxt->error == XPATH_EXPRESSION_OK)); |
goto apply_predicates; |
@@ -12418,13 +12563,16 @@ first_hit: /* ---------------------------------------------------------- */ |
#endif |
apply_predicates: /* --------------------------------------------------- */ |
+ if (ctxt->error != XPATH_EXPRESSION_OK) |
+ goto error; |
+ |
/* |
* Apply predicates. |
*/ |
if ((predOp != NULL) && (seq->nodeNr > 0)) { |
/* |
* E.g. when we have a "/foo[some expression][n]". |
- */ |
+ */ |
/* |
* QUESTION TODO: The old predicate evaluation took into |
* account location-sets. |
@@ -12433,7 +12581,7 @@ apply_predicates: /* --------------------------------------------------- */ |
* All what I learned now from the evaluation semantics |
* does not indicate that a location-set will be processed |
* here, so this looks OK. |
- */ |
+ */ |
/* |
* Iterate over all predicates, starting with the outermost |
* predicate. |
@@ -12926,8 +13074,10 @@ xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt, |
tmp = xmlXPathCacheNewNodeSet(ctxt->context, |
ctxt->context->node); |
} else { |
- xmlXPathNodeSetAddUnique(tmp->nodesetval, |
- ctxt->context->node); |
+ if (xmlXPathNodeSetAddUnique(tmp->nodesetval, |
+ ctxt->context->node) < 0) { |
+ ctxt->error = XPATH_MEMORY_ERROR; |
+ } |
} |
valuePush(ctxt, tmp); |
if (op->ch2 != -1) |
@@ -13040,8 +13190,10 @@ xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt, |
tmp = xmlXPathCacheNewNodeSet(ctxt->context, |
ctxt->context->node); |
} else { |
- xmlXPathNodeSetAddUnique(tmp->nodesetval, |
- ctxt->context->node); |
+ if (xmlXPathNodeSetAddUnique(tmp->nodesetval, |
+ ctxt->context->node) < 0) { |
+ ctxt->error = XPATH_MEMORY_ERROR; |
+ } |
} |
valuePush(ctxt, tmp); |
ctxt->context->contextSize = oldset->nodeNr; |
@@ -13059,7 +13211,8 @@ xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt, |
*/ |
res = valuePop(ctxt); |
if (xmlXPathEvaluatePredicateResult(ctxt, res)) { |
- xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); |
+ if (xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]) < 0) |
+ ctxt->error = XPATH_MEMORY_ERROR; |
} |
/* |
* Cleanup |
@@ -13368,23 +13521,33 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
xmlXPathFunction func; |
const xmlChar *oldFunc, *oldFuncURI; |
int i; |
+ int frame; |
- if (op->ch1 != -1) |
+ frame = xmlXPathSetFrame(ctxt); |
+ if (op->ch1 != -1) { |
total += |
xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
- if (ctxt->valueNr < op->value) { |
+ if (ctxt->error != XPATH_EXPRESSION_OK) { |
+ xmlXPathPopFrame(ctxt, frame); |
+ return (total); |
+ } |
+ } |
+ if (ctxt->valueNr < ctxt->valueFrame + op->value) { |
xmlGenericError(xmlGenericErrorContext, |
"xmlXPathCompOpEval: parameter error\n"); |
ctxt->error = XPATH_INVALID_OPERAND; |
+ xmlXPathPopFrame(ctxt, frame); |
return (total); |
} |
- for (i = 0; i < op->value; i++) |
+ for (i = 0; i < op->value; i++) { |
if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) { |
xmlGenericError(xmlGenericErrorContext, |
"xmlXPathCompOpEval: parameter error\n"); |
ctxt->error = XPATH_INVALID_OPERAND; |
+ xmlXPathPopFrame(ctxt, frame); |
return (total); |
} |
+ } |
if (op->cache != NULL) |
XML_CAST_FPTR(func) = op->cache; |
else { |
@@ -13400,6 +13563,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
xmlGenericError(xmlGenericErrorContext, |
"xmlXPathCompOpEval: function %s bound to undefined prefix %s\n", |
(char *)op->value4, (char *)op->value5); |
+ xmlXPathPopFrame(ctxt, frame); |
ctxt->error = XPATH_UNDEF_PREFIX_ERROR; |
return (total); |
} |
@@ -13422,6 +13586,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
func(ctxt, op->value); |
ctxt->context->function = oldFunc; |
ctxt->context->functionURI = oldFuncURI; |
+ xmlXPathPopFrame(ctxt, frame); |
return (total); |
} |
case XPATH_OP_ARG: |
@@ -13429,17 +13594,20 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
bak = ctxt->context->node; |
pp = ctxt->context->proximityPosition; |
cs = ctxt->context->contextSize; |
- if (op->ch1 != -1) |
+ if (op->ch1 != -1) { |
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); |
- ctxt->context->contextSize = cs; |
- ctxt->context->proximityPosition = pp; |
- ctxt->context->node = bak; |
- ctxt->context->doc = bakd; |
- CHECK_ERROR0; |
+ ctxt->context->contextSize = cs; |
+ ctxt->context->proximityPosition = pp; |
+ ctxt->context->node = bak; |
+ ctxt->context->doc = bakd; |
+ CHECK_ERROR0; |
+ } |
if (op->ch2 != -1) { |
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); |
- ctxt->context->doc = bakd; |
- ctxt->context->node = bak; |
+ ctxt->context->contextSize = cs; |
+ ctxt->context->proximityPosition = pp; |
+ ctxt->context->node = bak; |
+ ctxt->context->doc = bakd; |
CHECK_ERROR0; |
} |
return (total); |
@@ -13731,8 +13899,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
tmp = xmlXPathCacheNewNodeSet(ctxt->context, |
ctxt->context->node); |
} else { |
- xmlXPathNodeSetAddUnique(tmp->nodesetval, |
- ctxt->context->node); |
+ if (xmlXPathNodeSetAddUnique(tmp->nodesetval, |
+ ctxt->context->node) < 0) { |
+ ctxt->error = XPATH_MEMORY_ERROR; |
+ } |
} |
valuePush(ctxt, tmp); |
ctxt->context->contextSize = oldset->nodeNr; |
@@ -13762,7 +13932,9 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
*/ |
res = valuePop(ctxt); |
if (xmlXPathEvaluatePredicateResult(ctxt, res)) { |
- xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); |
+ if (xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]) |
+ < 0) |
+ ctxt->error = XPATH_MEMORY_ERROR; |
} |
/* |
@@ -13978,6 +14150,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) |
} |
xmlGenericError(xmlGenericErrorContext, |
"XPath: unknown precompiled operation %d\n", op->op); |
+ ctxt->error = XPATH_INVALID_OPERAND; |
return (total); |
} |
@@ -14124,7 +14297,7 @@ xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp, |
if (toBool) |
return(1); |
xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, |
- (xmlNodePtr) ctxt->doc); |
+ (xmlNodePtr) ctxt->doc); |
} else { |
/* Select "self::node()" */ |
if (toBool) |
@@ -14217,7 +14390,11 @@ next_node: |
} else if (ret == 1) { |
if (toBool) |
goto return_1; |
- xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur); |
+ if (xmlXPathNodeSetAddUnique((*resultSeq)->nodesetval, cur) |
+ < 0) { |
+ ctxt->lastError.domain = XML_FROM_XPATH; |
+ ctxt->lastError.code = XML_ERR_NO_MEMORY; |
+ } |
} |
if ((cur->children == NULL) || (depth >= max_depth)) { |
ret = xmlStreamPop(patstream); |
@@ -14233,6 +14410,7 @@ next_node: |
} |
scan_children: |
+ if (cur->type == XML_NAMESPACE_DECL) break; |
if ((cur->children != NULL) && (depth < max_depth)) { |
/* |
* Do not descend on entities declarations |
@@ -14325,6 +14503,7 @@ xmlXPathRunEval(xmlXPathParserContextPtr ctxt, int toBool) |
ctxt->valueNr = 0; |
ctxt->valueMax = 10; |
ctxt->value = NULL; |
+ ctxt->valueFrame = 0; |
} |
#ifdef XPATH_STREAMING |
if (ctxt->comp->stream) { |
@@ -14552,57 +14731,64 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { |
} |
#endif /* XPATH_STREAMING */ |
-static int |
-xmlXPathCanRewriteDosExpression(xmlChar *expr) |
-{ |
- if (expr == NULL) |
- return(0); |
- do { |
- if ((*expr == '/') && (*(++expr) == '/')) |
- return(1); |
- } while (*expr++); |
- return(0); |
-} |
static void |
-xmlXPathRewriteDOSExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op) |
+xmlXPathOptimizeExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op) |
{ |
/* |
* Try to rewrite "descendant-or-self::node()/foo" to an optimized |
* internal representation. |
*/ |
- if (op->ch1 != -1) { |
- if ((op->op == XPATH_OP_COLLECT /* 11 */) && |
- ((xmlXPathAxisVal) op->value == AXIS_CHILD /* 4 */) && |
- ((xmlXPathTestVal) op->value2 == NODE_TEST_NAME /* 5 */) && |
- ((xmlXPathTypeVal) op->value3 == NODE_TYPE_NODE /* 0 */)) |
- { |
- /* |
- * This is a "child::foo" |
- */ |
- xmlXPathStepOpPtr prevop = &comp->steps[op->ch1]; |
- |
- if ((prevop->op == XPATH_OP_COLLECT /* 11 */) && |
- (prevop->ch1 != -1) && |
- ((xmlXPathAxisVal) prevop->value == |
- AXIS_DESCENDANT_OR_SELF) && |
- (prevop->ch2 == -1) && |
- ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) && |
- ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE) && |
- (comp->steps[prevop->ch1].op == XPATH_OP_ROOT)) |
- { |
- /* |
- * This is a "/descendant-or-self::node()" without predicates. |
- * Eliminate it. |
- */ |
- op->ch1 = prevop->ch1; |
- op->rewriteType = XP_REWRITE_DOS_CHILD_ELEM; |
- } |
+ |
+ if ((op->op == XPATH_OP_COLLECT /* 11 */) && |
+ (op->ch1 != -1) && |
+ (op->ch2 == -1 /* no predicate */)) |
+ { |
+ xmlXPathStepOpPtr prevop = &comp->steps[op->ch1]; |
+ |
+ if ((prevop->op == XPATH_OP_COLLECT /* 11 */) && |
+ ((xmlXPathAxisVal) prevop->value == |
+ AXIS_DESCENDANT_OR_SELF) && |
+ (prevop->ch2 == -1) && |
+ ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) && |
+ ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE)) |
+ { |
+ /* |
+ * This is a "descendant-or-self::node()" without predicates. |
+ * Try to eliminate it. |
+ */ |
+ |
+ switch ((xmlXPathAxisVal) op->value) { |
+ case AXIS_CHILD: |
+ case AXIS_DESCENDANT: |
+ /* |
+ * Convert "descendant-or-self::node()/child::" or |
+ * "descendant-or-self::node()/descendant::" to |
+ * "descendant::" |
+ */ |
+ op->ch1 = prevop->ch1; |
+ op->value = AXIS_DESCENDANT; |
+ break; |
+ case AXIS_SELF: |
+ case AXIS_DESCENDANT_OR_SELF: |
+ /* |
+ * Convert "descendant-or-self::node()/self::" or |
+ * "descendant-or-self::node()/descendant-or-self::" to |
+ * to "descendant-or-self::" |
+ */ |
+ op->ch1 = prevop->ch1; |
+ op->value = AXIS_DESCENDANT_OR_SELF; |
+ break; |
+ default: |
+ break; |
+ } |
} |
- if (op->ch1 != -1) |
- xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch1]); |
} |
+ |
+ /* Recurse */ |
+ if (op->ch1 != -1) |
+ xmlXPathOptimizeExpression(comp, &comp->steps[op->ch1]); |
if (op->ch2 != -1) |
- xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch2]); |
+ xmlXPathOptimizeExpression(comp, &comp->steps[op->ch2]); |
} |
/** |
@@ -14660,12 +14846,8 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) { |
comp->string = xmlStrdup(str); |
comp->nb = 0; |
#endif |
- if ((comp->expr != NULL) && |
- (comp->nbStep > 2) && |
- (comp->last >= 0) && |
- (xmlXPathCanRewriteDosExpression(comp->expr) == 1)) |
- { |
- xmlXPathRewriteDOSExpression(comp, &comp->steps[comp->last]); |
+ if ((comp->nbStep > 1) && (comp->last >= 0)) { |
+ xmlXPathOptimizeExpression(comp, &comp->steps[comp->last]); |
} |
} |
return(comp); |
@@ -14842,17 +15024,12 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { |
#endif |
{ |
xmlXPathCompileExpr(ctxt, 1); |
- /* |
- * In this scenario the expression string will sit in ctxt->base. |
- */ |
if ((ctxt->error == XPATH_EXPRESSION_OK) && |
(ctxt->comp != NULL) && |
- (ctxt->base != NULL) && |
- (ctxt->comp->nbStep > 2) && |
- (ctxt->comp->last >= 0) && |
- (xmlXPathCanRewriteDosExpression((xmlChar *) ctxt->base) == 1)) |
+ (ctxt->comp->nbStep > 1) && |
+ (ctxt->comp->last >= 0)) |
{ |
- xmlXPathRewriteDOSExpression(ctxt->comp, |
+ xmlXPathOptimizeExpression(ctxt->comp, |
&ctxt->comp->steps[ctxt->comp->last]); |
} |
} |
@@ -14923,6 +15100,49 @@ xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) { |
} |
/** |
+ * xmlXPathSetContextNode: |
+ * @node: the node to to use as the context node |
+ * @ctx: the XPath context |
+ * |
+ * Sets 'node' as the context node. The node must be in the same |
+ * document as that associated with the context. |
+ * |
+ * Returns -1 in case of error or 0 if successful |
+ */ |
+int |
+xmlXPathSetContextNode(xmlNodePtr node, xmlXPathContextPtr ctx) { |
+ if ((node == NULL) || (ctx == NULL)) |
+ return(-1); |
+ |
+ if (node->doc == ctx->doc) { |
+ ctx->node = node; |
+ return(0); |
+ } |
+ return(-1); |
+} |
+ |
+/** |
+ * xmlXPathNodeEval: |
+ * @node: the node to to use as the context node |
+ * @str: the XPath expression |
+ * @ctx: the XPath context |
+ * |
+ * Evaluate the XPath Location Path in the given context. The node 'node' |
+ * is set as the context node. The context node is not restored. |
+ * |
+ * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL. |
+ * the caller has to free the object. |
+ */ |
+xmlXPathObjectPtr |
+xmlXPathNodeEval(xmlNodePtr node, const xmlChar *str, xmlXPathContextPtr ctx) { |
+ if (str == NULL) |
+ return(NULL); |
+ if (xmlXPathSetContextNode(node, ctx) < 0) |
+ return(NULL); |
+ return(xmlXPathEval(str, ctx)); |
+} |
+ |
+/** |
* xmlXPathEvalExpression: |
* @str: the XPath expression |
* @ctxt: the XPath context |
@@ -15026,7 +15246,7 @@ static void |
xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
xmlXPathObjectPtr str; |
int escape_reserved; |
- xmlBufferPtr target; |
+ xmlBufPtr target; |
xmlChar *cptr; |
xmlChar escape[4]; |
@@ -15037,7 +15257,7 @@ xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
CAST_TO_STRING; |
str = valuePop(ctxt); |
- target = xmlBufferCreate(); |
+ target = xmlBufCreate(); |
escape[0] = '%'; |
escape[3] = 0; |
@@ -15062,7 +15282,7 @@ xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
*cptr == ':' || *cptr == '@' || *cptr == '&' || |
*cptr == '=' || *cptr == '+' || *cptr == '$' || |
*cptr == ','))) { |
- xmlBufferAdd(target, cptr, 1); |
+ xmlBufAdd(target, cptr, 1); |
} else { |
if ((*cptr >> 4) < 10) |
escape[1] = '0' + (*cptr >> 4); |
@@ -15073,13 +15293,13 @@ xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) { |
else |
escape[2] = 'A' - 10 + (*cptr & 0xF); |
- xmlBufferAdd(target, &escape[0], 3); |
+ xmlBufAdd(target, &escape[0], 3); |
} |
} |
} |
valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, |
- xmlBufferContent(target))); |
- xmlBufferFree(target); |
+ xmlBufContent(target))); |
+ xmlBufFree(target); |
xmlXPathReleaseObject(ctxt->context, str); |
} |