| /* |
| * 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 Recommendation 16 November 1999 |
| * http://www.w3.org/TR/1999/REC-xpath-19991116 |
| * Public reference: |
| * http://www.w3.org/TR/xpath |
| * |
| * See Copyright for the status of this software |
| * |
| * Author: daniel@veillard.com |
| * |
| */ |
| |
| /* To avoid EBCDIC trouble when parsing on zOS */ |
| #if defined(__MVS__) |
| #pragma convert("ISO8859-1") |
| #endif |
| |
| #define IN_LIBXML |
| #include "libxml.h" |
| |
| #include <limits.h> |
| #include <string.h> |
| #include <stddef.h> |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #ifdef HAVE_MATH_H |
| #include <math.h> |
| #endif |
| #ifdef HAVE_FLOAT_H |
| #include <float.h> |
| #endif |
| #ifdef HAVE_CTYPE_H |
| #include <ctype.h> |
| #endif |
| #ifdef HAVE_SIGNAL_H |
| #include <signal.h> |
| #endif |
| |
| #include <libxml/xmlmemory.h> |
| #include <libxml/tree.h> |
| #include <libxml/valid.h> |
| #include <libxml/xpath.h> |
| #include <libxml/xpathInternals.h> |
| #include <libxml/parserInternals.h> |
| #include <libxml/hash.h> |
| #ifdef LIBXML_XPTR_ENABLED |
| #include <libxml/xpointer.h> |
| #endif |
| #ifdef LIBXML_DEBUG_ENABLED |
| #include <libxml/debugXML.h> |
| #endif |
| #include <libxml/xmlerror.h> |
| #include <libxml/threads.h> |
| #include <libxml/globals.h> |
| #ifdef LIBXML_PATTERN_ENABLED |
| #include <libxml/pattern.h> |
| #endif |
| |
| #include "buf.h" |
| |
| #ifdef LIBXML_PATTERN_ENABLED |
| #define XPATH_STREAMING |
| #endif |
| |
| #define TODO \ |
| xmlGenericError(xmlGenericErrorContext, \ |
| "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 |
| * xmlXPathCmpNodes(). The new function is optimized comparison of |
| * non-element nodes; actually it will speed up comparison only if |
| * xmlXPathOrderDocElems() was called in order to index the elements of |
| * a tree in document order; Libxslt does such an indexing, thus it will |
| * benefit from this optimization. |
| */ |
| #define XP_OPTIMIZED_NON_ELEM_COMPARISON |
| |
| /* |
| * XP_OPTIMIZED_FILTER_FIRST: |
| * If defined, this will optimize expressions like "key('foo', 'val')[b][1]" |
| * in a way, that it stop evaluation at the first node. |
| */ |
| #define XP_OPTIMIZED_FILTER_FIRST |
| |
| /* |
| * XP_DEBUG_OBJ_USAGE: |
| * Internal flag to enable tracking of how much XPath objects have been |
| * created. |
| */ |
| /* #define XP_DEBUG_OBJ_USAGE */ |
| |
| /* |
| * 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 |
| */ |
| #define XPATH_MAX_STEPS 1000000 |
| |
| /* |
| * 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 |
| */ |
| #define XPATH_MAX_STACK_DEPTH 1000000 |
| |
| /* |
| * 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. |
| */ |
| #define XPATH_MAX_NODESET_LENGTH 10000000 |
| |
| /* |
| * 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) |
| */ |
| |
| #ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON |
| /** |
| * xmlXPathCmpNodesExt: |
| * @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. |
| * |
| * 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 depth1, depth2; |
| int misc = 0, precedence1 = 0, precedence2 = 0; |
| xmlNodePtr miscNode1 = NULL, miscNode2 = NULL; |
| xmlNodePtr cur, root; |
| ptrdiff_t 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 > (ptrdiff_t) node1->content) && |
| (0 > (ptrdiff_t) node2->content) && |
| (node1->doc == node2->doc)) |
| { |
| l1 = -((ptrdiff_t) node1->content); |
| l2 = -((ptrdiff_t) 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 <= (ptrdiff_t) 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 <= (ptrdiff_t) 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 available. |
| */ |
| if ((node1->type == XML_ELEMENT_NODE) && |
| (node2->type == XML_ELEMENT_NODE) && |
| (0 > (ptrdiff_t) node1->content) && |
| (0 > (ptrdiff_t) node2->content) && |
| (node1->doc == node2->doc)) { |
| |
| l1 = -((ptrdiff_t) node1->content); |
| l2 = -((ptrdiff_t) 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->parent == node1) |
| return(1); |
| depth2++; |
| } |
| root = cur; |
| for (depth1 = 0, cur = node1; cur->parent != NULL; cur = cur->parent) { |
| if (cur->parent == 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 available. |
| */ |
| if ((node1->type == XML_ELEMENT_NODE) && |
| (node2->type == XML_ELEMENT_NODE) && |
| (0 > (ptrdiff_t) node1->content) && |
| (0 > (ptrdiff_t) node2->content) && |
| (node1->doc == node2->doc)) { |
| |
| l1 = -((ptrdiff_t) node1->content); |
| l2 = -((ptrdiff_t) 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 algorithm 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 INFINITY |
| #define INFINITY (DBL_MAX * DBL_MAX) |
| #endif |
| |
| #ifndef NAN |
| #define NAN (INFINITY / INFINITY) |
| #endif |
| |
| double xmlXPathNAN; |
| double xmlXPathPINF; |
| double xmlXPathNINF; |
| |
| /** |
| * xmlXPathInit: |
| * |
| * Initialize the XPath environment |
| */ |
| void |
| xmlXPathInit(void) { |
| xmlXPathNAN = NAN; |
| xmlXPathPINF = INFINITY; |
| xmlXPathNINF = -INFINITY; |
| } |
| |
| /** |
| * xmlXPathIsNaN: |
| * @val: a double value |
| * |
| * Returns 1 if the value is a NaN, 0 otherwise |
| */ |
| int |
| xmlXPathIsNaN(double val) { |
| #ifdef isnan |
| return isnan(val); |
| #else |
| return !(val == val); |
| #endif |
| } |
| |
| /** |
| * xmlXPathIsInf: |
| * @val: a double value |
| * |
| * Returns 1 if the value is +Infinite, -1 if -Infinite, 0 otherwise |
| */ |
| int |
| xmlXPathIsInf(double val) { |
| #ifdef isinf |
| return isinf(val) ? (val > 0 ? 1 : -1) : 0; |
| #else |
| if (val >= INFINITY) |
| return 1; |
| if (val <= -INFINITY) |
| return -1; |
| return 0; |
| #endif |
| } |
| |
| #endif /* SCHEMAS or XPATH */ |
| |
| #ifdef LIBXML_XPATH_ENABLED |
| |
| /* |
| * 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", |
| "Operation limit exceeded\n", |
| "Recursion limit exceeded\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 information |
| * |
| * Handle a redefinition of attribute error |
| */ |
| static void |
| xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra) |
| { |
| if (ctxt != NULL) { |
| xmlResetError(&ctxt->lastError); |
| if (extra) { |
| xmlChar buf[200]; |
| |
| xmlStrPrintf(buf, 200, |
| "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 information |
| * |
| * 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) { |
| __xmlRaiseError(NULL, NULL, NULL, |
| NULL, NULL, XML_FROM_XPATH, |
| error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
| XML_ERR_ERROR, NULL, 0, |
| (const char *) ctxt->base, NULL, NULL, |
| ctxt->cur - ctxt->base, 0, |
| "%s", xmlXPathErrorMessages[error]); |
| return; |
| } |
| |
| /* cleanup current last error */ |
| xmlResetError(&ctxt->context->lastError); |
| |
| ctxt->context->lastError.domain = XML_FROM_XPATH; |
| ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK - |
| XPATH_EXPRESSION_OK; |
| ctxt->context->lastError.level = XML_ERR_ERROR; |
| ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base); |
| ctxt->context->lastError.int1 = ctxt->cur - ctxt->base; |
| ctxt->context->lastError.node = ctxt->context->debugNode; |
| if (ctxt->context->error != NULL) { |
| ctxt->context->error(ctxt->context->userData, |
| &ctxt->context->lastError); |
| } else { |
| __xmlRaiseError(NULL, NULL, NULL, |
| NULL, ctxt->context->debugNode, XML_FROM_XPATH, |
| error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK, |
| XML_ERR_ERROR, NULL, 0, |
| (const char *) ctxt->base, NULL, NULL, |
| ctxt->cur - ctxt->base, 0, |
| "%s", xmlXPathErrorMessages[error]); |
| } |
| |
| } |
| |
| /** |
| * xmlXPatherror: |
| * @ctxt: the XPath Parser context |
| * @file: the file name |
| * @line: the line number |
| * @no: the error number |
| * |
| * Formats an error message. |
| */ |
| void |
| xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED, |
| int line ATTRIBUTE_UNUSED, int no) { |
| xmlXPathErr(ctxt, no); |
| } |
| |
| /** |
| * xmlXPathCheckOpLimit: |
| * @ctxt: the XPath Parser context |
| * @opCount: the number of operations to be added |
| * |
| * Adds opCount to the running total of operations and returns -1 if the |
| * operation limit is exceeded. Returns 0 otherwise. |
| */ |
| static int |
| xmlXPathCheckOpLimit(xmlXPathParserContextPtr ctxt, unsigned long opCount) { |
| xmlXPathContextPtr xpctxt = ctxt->context; |
| |
| if ((opCount > xpctxt->opLimit) || |
| (xpctxt->opCount > xpctxt->opLimit - opCount)) { |
| xpctxt->opCount = xpctxt->opLimit; |
| xmlXPathErr(ctxt, XPATH_OP_LIMIT_EXCEEDED); |
| return(-1); |
| } |
| |
| xpctxt->opCount += opCount; |
| return(0); |
| } |
| |
| #define OP_LIMIT_EXCEEDED(ctxt, n) \ |
| ((ctxt->context->opLimit != 0) && (xmlXPathCheckOpLimit(ctxt, n) < 0)) |
| |
| /************************************************************************ |
| * * |
| * Utilities * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xsltPointerList: |
| * |
| * Pointer-list for various purposes. |
| */ |
| typedef struct _xmlPointerList xmlPointerList; |
| typedef xmlPointerList *xmlPointerListPtr; |
| struct _xmlPointerList { |
| void **items; |
| int number; |
| int size; |
| }; |
| /* |
| * TODO: Since such a list-handling is used in xmlschemas.c and libxslt |
| * and here, we should make the functions public. |
| */ |
| static int |
| xmlPointerListAddSize(xmlPointerListPtr list, |
| void *item, |
| int initialSize) |
| { |
| if (list->items == NULL) { |
| if (initialSize <= 0) |
| initialSize = 1; |
| list->items = (void **) xmlMalloc(initialSize * sizeof(void *)); |
| if (list->items == NULL) { |
| xmlXPathErrMemory(NULL, |
| "xmlPointerListCreate: allocating item\n"); |
| return(-1); |
| } |
| 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, |
| "xmlPointerListAddSize: re-allocating item\n"); |
| list->size = 0; |
| return(-1); |
| } |
| } |
| list->items[list->number++] = item; |
| return(0); |
| } |
| |
| /** |
| * xsltPointerListCreate: |
| * |
| * Creates an xsltPointerList structure. |
| * |
| * Returns a xsltPointerList structure or NULL in case of an error. |
| */ |
| static xmlPointerListPtr |
| xmlPointerListCreate(int initialSize) |
| { |
| xmlPointerListPtr ret; |
| |
| ret = xmlMalloc(sizeof(xmlPointerList)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, |
| "xmlPointerListCreate: allocating item\n"); |
| return (NULL); |
| } |
| memset(ret, 0, sizeof(xmlPointerList)); |
| if (initialSize > 0) { |
| xmlPointerListAddSize(ret, NULL, initialSize); |
| ret->number = 0; |
| } |
| return (ret); |
| } |
| |
| /** |
| * xsltPointerListFree: |
| * |
| * Frees the xsltPointerList structure. This does not free |
| * the content of the list. |
| */ |
| static void |
| xmlPointerListFree(xmlPointerListPtr list) |
| { |
| if (list == NULL) |
| return; |
| if (list->items != NULL) |
| xmlFree(list->items); |
| xmlFree(list); |
| } |
| |
| /************************************************************************ |
| * * |
| * Parser Types * |
| * * |
| ************************************************************************/ |
| |
| /* |
| * Types are private: |
| */ |
| |
| typedef enum { |
| XPATH_OP_END=0, |
| XPATH_OP_AND, |
| XPATH_OP_OR, |
| XPATH_OP_EQUAL, |
| XPATH_OP_CMP, |
| XPATH_OP_PLUS, |
| XPATH_OP_MULT, |
| XPATH_OP_UNION, |
| XPATH_OP_ROOT, |
| XPATH_OP_NODE, |
| XPATH_OP_COLLECT, |
| XPATH_OP_VALUE, /* 11 */ |
| XPATH_OP_VARIABLE, |
| XPATH_OP_FUNCTION, |
| XPATH_OP_ARG, |
| XPATH_OP_PREDICATE, |
| XPATH_OP_FILTER, /* 16 */ |
| XPATH_OP_SORT /* 17 */ |
| #ifdef LIBXML_XPTR_ENABLED |
| ,XPATH_OP_RANGETO |
| #endif |
| } xmlXPathOp; |
| |
| typedef enum { |
| AXIS_ANCESTOR = 1, |
| AXIS_ANCESTOR_OR_SELF, |
| AXIS_ATTRIBUTE, |
| AXIS_CHILD, |
| AXIS_DESCENDANT, |
| AXIS_DESCENDANT_OR_SELF, |
| AXIS_FOLLOWING, |
| AXIS_FOLLOWING_SIBLING, |
| AXIS_NAMESPACE, |
| AXIS_PARENT, |
| AXIS_PRECEDING, |
| AXIS_PRECEDING_SIBLING, |
| AXIS_SELF |
| } xmlXPathAxisVal; |
| |
| typedef enum { |
| NODE_TEST_NONE = 0, |
| NODE_TEST_TYPE = 1, |
| NODE_TEST_PI = 2, |
| NODE_TEST_ALL = 3, |
| NODE_TEST_NS = 4, |
| NODE_TEST_NAME = 5 |
| } xmlXPathTestVal; |
| |
| typedef enum { |
| NODE_TYPE_NODE = 0, |
| NODE_TYPE_COMMENT = XML_COMMENT_NODE, |
| NODE_TYPE_TEXT = XML_TEXT_NODE, |
| NODE_TYPE_PI = XML_PI_NODE |
| } xmlXPathTypeVal; |
| |
| typedef struct _xmlXPathStepOp xmlXPathStepOp; |
| typedef xmlXPathStepOp *xmlXPathStepOpPtr; |
| struct _xmlXPathStepOp { |
| xmlXPathOp op; /* The identifier of the operation */ |
| int ch1; /* First child */ |
| int ch2; /* Second child */ |
| int value; |
| int value2; |
| int value3; |
| void *value4; |
| void *value5; |
| xmlXPathFunction cache; |
| void *cacheURI; |
| }; |
| |
| struct _xmlXPathCompExpr { |
| int nbStep; /* Number of steps in this expression */ |
| int maxStep; /* Maximum number of steps allocated */ |
| xmlXPathStepOp *steps; /* ops for computation of this expression */ |
| int last; /* index of last step in expression */ |
| xmlChar *expr; /* the expression being computed */ |
| xmlDictPtr dict; /* the dictionary to use if any */ |
| #ifdef DEBUG_EVAL_COUNTS |
| int nb; |
| xmlChar *string; |
| #endif |
| #ifdef XPATH_STREAMING |
| xmlPatternPtr stream; |
| #endif |
| }; |
| |
| /************************************************************************ |
| * * |
| * Forward declarations * |
| * * |
| ************************************************************************/ |
| static void |
| xmlXPathFreeValueTree(xmlNodeSetPtr obj); |
| static void |
| xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj); |
| static int |
| xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt, |
| xmlXPathStepOpPtr op, xmlNodePtr *first); |
| static int |
| xmlXPathCompOpEvalToBoolean(xmlXPathParserContextPtr ctxt, |
| xmlXPathStepOpPtr op, |
| int isPredicate); |
| static void |
| xmlXPathFreeObjectEntry(void *obj, const xmlChar *name); |
| |
| /************************************************************************ |
| * * |
| * Parser Type functions * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlXPathNewCompExpr: |
| * |
| * Create a new Xpath component |
| * |
| * Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error |
| */ |
| static xmlXPathCompExprPtr |
| xmlXPathNewCompExpr(void) { |
| xmlXPathCompExprPtr cur; |
| |
| cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr)); |
| if (cur == NULL) { |
| xmlXPathErrMemory(NULL, "allocating component\n"); |
| return(NULL); |
| } |
| memset(cur, 0, sizeof(xmlXPathCompExpr)); |
| cur->maxStep = 10; |
| cur->nbStep = 0; |
| cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep * |
| sizeof(xmlXPathStepOp)); |
| if (cur->steps == NULL) { |
| xmlXPathErrMemory(NULL, "allocating steps\n"); |
| xmlFree(cur); |
| return(NULL); |
| } |
| memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp)); |
| cur->last = -1; |
| #ifdef DEBUG_EVAL_COUNTS |
| cur->nb = 0; |
| #endif |
| return(cur); |
| } |
| |
| /** |
| * xmlXPathFreeCompExpr: |
| * @comp: an XPATH comp |
| * |
| * Free up the memory allocated by @comp |
| */ |
| void |
| xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp) |
| { |
| xmlXPathStepOpPtr op; |
| int i; |
| |
| if (comp == NULL) |
| return; |
| if (comp->dict == NULL) { |
| for (i = 0; i < comp->nbStep; i++) { |
| op = &comp->steps[i]; |
| if (op->value4 != NULL) { |
| if (op->op == XPATH_OP_VALUE) |
| xmlXPathFreeObject(op->value4); |
| else |
| xmlFree(op->value4); |
| } |
| if (op->value5 != NULL) |
| xmlFree(op->value5); |
| } |
| } else { |
| for (i = 0; i < comp->nbStep; i++) { |
| op = &comp->steps[i]; |
| if (op->value4 != NULL) { |
| if (op->op == XPATH_OP_VALUE) |
| xmlXPathFreeObject(op->value4); |
| } |
| } |
| xmlDictFree(comp->dict); |
| } |
| if (comp->steps != NULL) { |
| xmlFree(comp->steps); |
| } |
| #ifdef DEBUG_EVAL_COUNTS |
| if (comp->string != NULL) { |
| xmlFree(comp->string); |
| } |
| #endif |
| #ifdef XPATH_STREAMING |
| if (comp->stream != NULL) { |
| xmlFreePatternList(comp->stream); |
| } |
| #endif |
| if (comp->expr != NULL) { |
| xmlFree(comp->expr); |
| } |
| |
| xmlFree(comp); |
| } |
| |
| /** |
| * xmlXPathCompExprAdd: |
| * @comp: the compiled expression |
| * @ch1: first child index |
| * @ch2: second child index |
| * @op: an op |
| * @value: the first int value |
| * @value2: the second int value |
| * @value3: the third int value |
| * @value4: the first string value |
| * @value5: the second string value |
| * |
| * Add a step to an XPath Compiled Expression |
| * |
| * Returns -1 in case of failure, the index otherwise |
| */ |
| static int |
| xmlXPathCompExprAdd(xmlXPathParserContextPtr ctxt, int ch1, int ch2, |
| xmlXPathOp op, int value, |
| int value2, int value3, void *value4, void *value5) { |
| xmlXPathCompExprPtr comp = ctxt->comp; |
| if (comp->nbStep >= comp->maxStep) { |
| xmlXPathStepOp *real; |
| |
| if (comp->maxStep >= XPATH_MAX_STEPS) { |
| xmlXPathPErrMemory(ctxt, "adding step\n"); |
| return(-1); |
| } |
| comp->maxStep *= 2; |
| real = (xmlXPathStepOp *) xmlRealloc(comp->steps, |
| comp->maxStep * sizeof(xmlXPathStepOp)); |
| if (real == NULL) { |
| comp->maxStep /= 2; |
| xmlXPathPErrMemory(ctxt, "adding step\n"); |
| return(-1); |
| } |
| comp->steps = real; |
| } |
| comp->last = comp->nbStep; |
| comp->steps[comp->nbStep].ch1 = ch1; |
| comp->steps[comp->nbStep].ch2 = ch2; |
| comp->steps[comp->nbStep].op = op; |
| comp->steps[comp->nbStep].value = value; |
| comp->steps[comp->nbStep].value2 = value2; |
| comp->steps[comp->nbStep].value3 = value3; |
| if ((comp->dict != NULL) && |
| ((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) || |
| (op == XPATH_OP_COLLECT))) { |
| if (value4 != NULL) { |
| comp->steps[comp->nbStep].value4 = (xmlChar *) |
| (void *)xmlDictLookup(comp->dict, value4, -1); |
| xmlFree(value4); |
| } else |
| comp->steps[comp->nbStep].value4 = NULL; |
| if (value5 != NULL) { |
| comp->steps[comp->nbStep].value5 = (xmlChar *) |
| (void *)xmlDictLookup(comp->dict, value5, -1); |
| xmlFree(value5); |
| } else |
| comp->steps[comp->nbStep].value5 = NULL; |
| } else { |
| comp->steps[comp->nbStep].value4 = value4; |
| comp->steps[comp->nbStep].value5 = value5; |
| } |
| comp->steps[comp->nbStep].cache = NULL; |
| return(comp->nbStep++); |
| } |
| |
| /** |
| * xmlXPathCompSwap: |
| * @comp: the compiled expression |
| * @op: operation index |
| * |
| * Swaps 2 operations in the compiled expression |
| */ |
| static void |
| xmlXPathCompSwap(xmlXPathStepOpPtr op) { |
| int tmp; |
| |
| #ifndef LIBXML_THREAD_ENABLED |
| /* |
| * Since this manipulates possibly shared variables, this is |
| * disabled if one detects that the library is used in a multithreaded |
| * application |
| */ |
| if (xmlXPathDisableOptimizer) |
| return; |
| #endif |
| |
| tmp = op->ch1; |
| op->ch1 = op->ch2; |
| op->ch2 = tmp; |
| } |
| |
| #define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \ |
| xmlXPathCompExprAdd(ctxt, (op1), (op2), \ |
| (op), (val), (val2), (val3), (val4), (val5)) |
| #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \ |
| xmlXPathCompExprAdd(ctxt, ctxt->comp->last, -1, \ |
| (op), (val), (val2), (val3), (val4), (val5)) |
| |
| #define PUSH_LEAVE_EXPR(op, val, val2) \ |
| xmlXPathCompExprAdd(ctxt, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL) |
| |
| #define PUSH_UNARY_EXPR(op, ch, val, val2) \ |
| xmlXPathCompExprAdd(ctxt, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL) |
| |
| #define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \ |
| xmlXPathCompExprAdd(ctxt, (ch1), (ch2), (op), \ |
| (val), (val2), 0 ,NULL ,NULL) |
| |
| /************************************************************************ |
| * * |
| * XPath object cache structures * |
| * * |
| ************************************************************************/ |
| |
| /* #define XP_DEFAULT_CACHE_ON */ |
| |
| #define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL)) |
| |
| typedef struct _xmlXPathContextCache xmlXPathContextCache; |
| typedef xmlXPathContextCache *xmlXPathContextCachePtr; |
| struct _xmlXPathContextCache { |
| xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */ |
| xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */ |
| xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */ |
| xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */ |
| xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */ |
| int maxNodeset; |
| int maxString; |
| int maxBoolean; |
| int maxNumber; |
| int maxMisc; |
| #ifdef XP_DEBUG_OBJ_USAGE |
| int dbgCachedAll; |
| int dbgCachedNodeset; |
| int dbgCachedString; |
| int dbgCachedBool; |
| int dbgCachedNumber; |
| int dbgCachedPoint; |
| int dbgCachedRange; |
| int dbgCachedLocset; |
| int dbgCachedUsers; |
| int dbgCachedXSLTTree; |
| int dbgCachedUndefined; |
| |
| |
| int dbgReusedAll; |
| int dbgReusedNodeset; |
| int dbgReusedString; |
| int dbgReusedBool; |
| int dbgReusedNumber; |
| int dbgReusedPoint; |
| int dbgReusedRange; |
| int dbgReusedLocset; |
| int dbgReusedUsers; |
| int dbgReusedXSLTTree; |
| int dbgReusedUndefined; |
| |
| #endif |
| }; |
| |
| /************************************************************************ |
| * * |
| * Debugging related functions * |
| * * |
| ************************************************************************/ |
| |
| #define STRANGE \ |
| xmlGenericError(xmlGenericErrorContext, \ |
| "Internal error at %s:%d\n", \ |
| __FILE__, __LINE__); |
| |
| #ifdef LIBXML_DEBUG_ENABLED |
| static void |
| xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) { |
| int i; |
| char shift[100]; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| if (cur == NULL) { |
| fprintf(output, "%s", shift); |
| fprintf(output, "Node is NULL !\n"); |
| return; |
| |
| } |
| |
| if ((cur->type == XML_DOCUMENT_NODE) || |
| (cur->type == XML_HTML_DOCUMENT_NODE)) { |
| fprintf(output, "%s", shift); |
| fprintf(output, " /\n"); |
| } else if (cur->type == XML_ATTRIBUTE_NODE) |
| xmlDebugDumpAttr(output, (xmlAttrPtr)cur, depth); |
| else |
| xmlDebugDumpOneNode(output, cur, depth); |
| } |
| static void |
| xmlXPathDebugDumpNodeList(FILE *output, xmlNodePtr cur, int depth) { |
| xmlNodePtr tmp; |
| int i; |
| char shift[100]; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| if (cur == NULL) { |
| fprintf(output, "%s", shift); |
| fprintf(output, "Node is NULL !\n"); |
| return; |
| |
| } |
| |
| while (cur != NULL) { |
| tmp = cur; |
| cur = cur->next; |
| xmlDebugDumpOneNode(output, tmp, depth); |
| } |
| } |
| |
| static void |
| xmlXPathDebugDumpNodeSet(FILE *output, xmlNodeSetPtr cur, int depth) { |
| int i; |
| char shift[100]; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| |
| if (cur == NULL) { |
| fprintf(output, "%s", shift); |
| fprintf(output, "NodeSet is NULL !\n"); |
| return; |
| |
| } |
| |
| if (cur != NULL) { |
| fprintf(output, "Set contains %d nodes:\n", cur->nodeNr); |
| for (i = 0;i < cur->nodeNr;i++) { |
| fprintf(output, "%s", shift); |
| fprintf(output, "%d", i + 1); |
| xmlXPathDebugDumpNode(output, cur->nodeTab[i], depth + 1); |
| } |
| } |
| } |
| |
| static void |
| xmlXPathDebugDumpValueTree(FILE *output, xmlNodeSetPtr cur, int depth) { |
| int i; |
| char shift[100]; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| |
| if ((cur == NULL) || (cur->nodeNr == 0) || (cur->nodeTab[0] == NULL)) { |
| fprintf(output, "%s", shift); |
| fprintf(output, "Value Tree is NULL !\n"); |
| return; |
| |
| } |
| |
| fprintf(output, "%s", shift); |
| fprintf(output, "%d", i + 1); |
| xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1); |
| } |
| #if defined(LIBXML_XPTR_ENABLED) |
| static void |
| xmlXPathDebugDumpLocationSet(FILE *output, xmlLocationSetPtr cur, int depth) { |
| int i; |
| char shift[100]; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| |
| if (cur == NULL) { |
| fprintf(output, "%s", shift); |
| fprintf(output, "LocationSet is NULL !\n"); |
| return; |
| |
| } |
| |
| for (i = 0;i < cur->locNr;i++) { |
| fprintf(output, "%s", shift); |
| fprintf(output, "%d : ", i + 1); |
| xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1); |
| } |
| } |
| #endif /* LIBXML_XPTR_ENABLED */ |
| |
| /** |
| * xmlXPathDebugDumpObject: |
| * @output: the FILE * to dump the output |
| * @cur: the object to inspect |
| * @depth: indentation level |
| * |
| * Dump the content of the object for debugging purposes |
| */ |
| void |
| xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth) { |
| int i; |
| char shift[100]; |
| |
| if (output == NULL) return; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| |
| |
| fprintf(output, "%s", shift); |
| |
| if (cur == NULL) { |
| fprintf(output, "Object is empty (NULL)\n"); |
| return; |
| } |
| switch(cur->type) { |
| case XPATH_UNDEFINED: |
| fprintf(output, "Object is uninitialized\n"); |
| break; |
| case XPATH_NODESET: |
| fprintf(output, "Object is a Node Set :\n"); |
| xmlXPathDebugDumpNodeSet(output, cur->nodesetval, depth); |
| break; |
| case XPATH_XSLT_TREE: |
| fprintf(output, "Object is an XSLT value tree :\n"); |
| xmlXPathDebugDumpValueTree(output, cur->nodesetval, depth); |
| break; |
| case XPATH_BOOLEAN: |
| fprintf(output, "Object is a Boolean : "); |
| if (cur->boolval) fprintf(output, "true\n"); |
| else fprintf(output, "false\n"); |
| break; |
| case XPATH_NUMBER: |
| switch (xmlXPathIsInf(cur->floatval)) { |
| case 1: |
| fprintf(output, "Object is a number : Infinity\n"); |
| break; |
| case -1: |
| fprintf(output, "Object is a number : -Infinity\n"); |
| break; |
| default: |
| if (xmlXPathIsNaN(cur->floatval)) { |
| fprintf(output, "Object is a number : NaN\n"); |
| } else if (cur->floatval == 0) { |
| /* Omit sign for negative zero. */ |
| fprintf(output, "Object is a number : 0\n"); |
| } else { |
| fprintf(output, "Object is a number : %0g\n", cur->floatval); |
| } |
| } |
| break; |
| case XPATH_STRING: |
| fprintf(output, "Object is a string : "); |
| xmlDebugDumpString(output, cur->stringval); |
| fprintf(output, "\n"); |
| break; |
| case XPATH_POINT: |
| fprintf(output, "Object is a point : index %d in node", cur->index); |
| xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, depth + 1); |
| fprintf(output, "\n"); |
| break; |
| case XPATH_RANGE: |
| if ((cur->user2 == NULL) || |
| ((cur->user2 == cur->user) && (cur->index == cur->index2))) { |
| fprintf(output, "Object is a collapsed range :\n"); |
| fprintf(output, "%s", shift); |
| if (cur->index >= 0) |
| fprintf(output, "index %d in ", cur->index); |
| fprintf(output, "node\n"); |
| xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, |
| depth + 1); |
| } else { |
| fprintf(output, "Object is a range :\n"); |
| fprintf(output, "%s", shift); |
| fprintf(output, "From "); |
| if (cur->index >= 0) |
| fprintf(output, "index %d in ", cur->index); |
| fprintf(output, "node\n"); |
| xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user, |
| depth + 1); |
| fprintf(output, "%s", shift); |
| fprintf(output, "To "); |
| if (cur->index2 >= 0) |
| fprintf(output, "index %d in ", cur->index2); |
| fprintf(output, "node\n"); |
| xmlXPathDebugDumpNode(output, (xmlNodePtr) cur->user2, |
| depth + 1); |
| fprintf(output, "\n"); |
| } |
| break; |
| case XPATH_LOCATIONSET: |
| #if defined(LIBXML_XPTR_ENABLED) |
| fprintf(output, "Object is a Location Set:\n"); |
| xmlXPathDebugDumpLocationSet(output, |
| (xmlLocationSetPtr) cur->user, depth); |
| #endif |
| break; |
| case XPATH_USERS: |
| fprintf(output, "Object is user defined\n"); |
| break; |
| } |
| } |
| |
| static void |
| xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp, |
| xmlXPathStepOpPtr op, int depth) { |
| int i; |
| char shift[100]; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| |
| fprintf(output, "%s", shift); |
| if (op == NULL) { |
| fprintf(output, "Step is NULL\n"); |
| return; |
| } |
| switch (op->op) { |
| case XPATH_OP_END: |
| fprintf(output, "END"); break; |
| case XPATH_OP_AND: |
| fprintf(output, "AND"); break; |
| case XPATH_OP_OR: |
| fprintf(output, "OR"); break; |
| case XPATH_OP_EQUAL: |
| if (op->value) |
| fprintf(output, "EQUAL ="); |
| else |
| fprintf(output, "EQUAL !="); |
| break; |
| case XPATH_OP_CMP: |
| if (op->value) |
| fprintf(output, "CMP <"); |
| else |
| fprintf(output, "CMP >"); |
| if (!op->value2) |
| fprintf(output, "="); |
| break; |
| case XPATH_OP_PLUS: |
| if (op->value == 0) |
| fprintf(output, "PLUS -"); |
| else if (op->value == 1) |
| fprintf(output, "PLUS +"); |
| else if (op->value == 2) |
| fprintf(output, "PLUS unary -"); |
| else if (op->value == 3) |
| fprintf(output, "PLUS unary - -"); |
| break; |
| case XPATH_OP_MULT: |
| if (op->value == 0) |
| fprintf(output, "MULT *"); |
| else if (op->value == 1) |
| fprintf(output, "MULT div"); |
| else |
| fprintf(output, "MULT mod"); |
| break; |
| case XPATH_OP_UNION: |
| fprintf(output, "UNION"); break; |
| case XPATH_OP_ROOT: |
| fprintf(output, "ROOT"); break; |
| case XPATH_OP_NODE: |
| fprintf(output, "NODE"); break; |
| case XPATH_OP_SORT: |
| fprintf(output, "SORT"); break; |
| case XPATH_OP_COLLECT: { |
| xmlXPathAxisVal axis = (xmlXPathAxisVal)op->value; |
| xmlXPathTestVal test = (xmlXPathTestVal)op->value2; |
| xmlXPathTypeVal type = (xmlXPathTypeVal)op->value3; |
| const xmlChar *prefix = op->value4; |
| const xmlChar *name = op->value5; |
| |
| fprintf(output, "COLLECT "); |
| switch (axis) { |
| case AXIS_ANCESTOR: |
| fprintf(output, " 'ancestors' "); break; |
| case AXIS_ANCESTOR_OR_SELF: |
| fprintf(output, " 'ancestors-or-self' "); break; |
| case AXIS_ATTRIBUTE: |
| fprintf(output, " 'attributes' "); break; |
| case AXIS_CHILD: |
| fprintf(output, " 'child' "); break; |
| case AXIS_DESCENDANT: |
| fprintf(output, " 'descendant' "); break; |
| case AXIS_DESCENDANT_OR_SELF: |
| fprintf(output, " 'descendant-or-self' "); break; |
| case AXIS_FOLLOWING: |
| fprintf(output, " 'following' "); break; |
| case AXIS_FOLLOWING_SIBLING: |
| fprintf(output, " 'following-siblings' "); break; |
| case AXIS_NAMESPACE: |
| fprintf(output, " 'namespace' "); break; |
| case AXIS_PARENT: |
| fprintf(output, " 'parent' "); break; |
| case AXIS_PRECEDING: |
| fprintf(output, " 'preceding' "); break; |
| case AXIS_PRECEDING_SIBLING: |
| fprintf(output, " 'preceding-sibling' "); break; |
| case AXIS_SELF: |
| fprintf(output, " 'self' "); break; |
| } |
| switch (test) { |
| case NODE_TEST_NONE: |
| fprintf(output, "'none' "); break; |
| case NODE_TEST_TYPE: |
| fprintf(output, "'type' "); break; |
| case NODE_TEST_PI: |
| fprintf(output, "'PI' "); break; |
| case NODE_TEST_ALL: |
| fprintf(output, "'all' "); break; |
| case NODE_TEST_NS: |
| fprintf(output, "'namespace' "); break; |
| case NODE_TEST_NAME: |
| fprintf(output, "'name' "); break; |
| } |
| switch (type) { |
| case NODE_TYPE_NODE: |
| fprintf(output, "'node' "); break; |
| case NODE_TYPE_COMMENT: |
| fprintf(output, "'comment' "); break; |
| case NODE_TYPE_TEXT: |
| fprintf(output, "'text' "); break; |
| case NODE_TYPE_PI: |
| fprintf(output, "'PI' "); break; |
| } |
| if (prefix != NULL) |
| fprintf(output, "%s:", prefix); |
| if (name != NULL) |
| fprintf(output, "%s", (const char *) name); |
| break; |
| |
| } |
| case XPATH_OP_VALUE: { |
| xmlXPathObjectPtr object = (xmlXPathObjectPtr) op->value4; |
| |
| fprintf(output, "ELEM "); |
| xmlXPathDebugDumpObject(output, object, 0); |
| goto finish; |
| } |
| case XPATH_OP_VARIABLE: { |
| const xmlChar *prefix = op->value5; |
| const xmlChar *name = op->value4; |
| |
| if (prefix != NULL) |
| fprintf(output, "VARIABLE %s:%s", prefix, name); |
| else |
| fprintf(output, "VARIABLE %s", name); |
| break; |
| } |
| case XPATH_OP_FUNCTION: { |
| int nbargs = op->value; |
| const xmlChar *prefix = op->value5; |
| const xmlChar *name = op->value4; |
| |
| if (prefix != NULL) |
| fprintf(output, "FUNCTION %s:%s(%d args)", |
| prefix, name, nbargs); |
| else |
| fprintf(output, "FUNCTION %s(%d args)", name, nbargs); |
| break; |
| } |
| case XPATH_OP_ARG: fprintf(output, "ARG"); break; |
| case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break; |
| case XPATH_OP_FILTER: fprintf(output, "FILTER"); break; |
| #ifdef LIBXML_XPTR_ENABLED |
| case XPATH_OP_RANGETO: fprintf(output, "RANGETO"); break; |
| #endif |
| default: |
| fprintf(output, "UNKNOWN %d\n", op->op); return; |
| } |
| fprintf(output, "\n"); |
| finish: |
| if (op->ch1 >= 0) |
| xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch1], depth + 1); |
| if (op->ch2 >= 0) |
| xmlXPathDebugDumpStepOp(output, comp, &comp->steps[op->ch2], depth + 1); |
| } |
| |
| /** |
| * xmlXPathDebugDumpCompExpr: |
| * @output: the FILE * for the output |
| * @comp: the precompiled XPath expression |
| * @depth: the indentation level. |
| * |
| * Dumps the tree of the compiled XPath expression. |
| */ |
| void |
| xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp, |
| int depth) { |
| int i; |
| char shift[100]; |
| |
| if ((output == NULL) || (comp == NULL)) return; |
| |
| for (i = 0;((i < depth) && (i < 25));i++) |
| shift[2 * i] = shift[2 * i + 1] = ' '; |
| shift[2 * i] = shift[2 * i + 1] = 0; |
| |
| fprintf(output, "%s", shift); |
| |
| #ifdef XPATH_STREAMING |
| if (comp->stream) { |
| fprintf(output, "Streaming Expression\n"); |
| } else |
| #endif |
| { |
| fprintf(output, "Compiled Expression : %d elements\n", |
| comp->nbStep); |
| i = comp->last; |
| xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1); |
| } |
| } |
| |
| #ifdef XP_DEBUG_OBJ_USAGE |
| |
| /* |
| * XPath object usage related debugging variables. |
| */ |
| static int xmlXPathDebugObjCounterUndefined = 0; |
| static int xmlXPathDebugObjCounterNodeset = 0; |
| static int xmlXPathDebugObjCounterBool = 0; |
| static int xmlXPathDebugObjCounterNumber = 0; |
| static int xmlXPathDebugObjCounterString = 0; |
| static int xmlXPathDebugObjCounterPoint = 0; |
| static int xmlXPathDebugObjCounterRange = 0; |
| static int xmlXPathDebugObjCounterLocset = 0; |
| static int xmlXPathDebugObjCounterUsers = 0; |
| static int xmlXPathDebugObjCounterXSLTTree = 0; |
| static int xmlXPathDebugObjCounterAll = 0; |
| |
| static int xmlXPathDebugObjTotalUndefined = 0; |
| static int xmlXPathDebugObjTotalNodeset = 0; |
| static int xmlXPathDebugObjTotalBool = 0; |
| static int xmlXPathDebugObjTotalNumber = 0; |
| static int xmlXPathDebugObjTotalString = 0; |
| static int xmlXPathDebugObjTotalPoint = 0; |
| static int xmlXPathDebugObjTotalRange = 0; |
| static int xmlXPathDebugObjTotalLocset = 0; |
| static int xmlXPathDebugObjTotalUsers = 0; |
| static int xmlXPathDebugObjTotalXSLTTree = 0; |
| static int xmlXPathDebugObjTotalAll = 0; |
| |
| static int xmlXPathDebugObjMaxUndefined = 0; |
| static int xmlXPathDebugObjMaxNodeset = 0; |
| static int xmlXPathDebugObjMaxBool = 0; |
| static int xmlXPathDebugObjMaxNumber = 0; |
| static int xmlXPathDebugObjMaxString = 0; |
| static int xmlXPathDebugObjMaxPoint = 0; |
| static int xmlXPathDebugObjMaxRange = 0; |
| static int xmlXPathDebugObjMaxLocset = 0; |
| static int xmlXPathDebugObjMaxUsers = 0; |
| static int xmlXPathDebugObjMaxXSLTTree = 0; |
| static int xmlXPathDebugObjMaxAll = 0; |
| |
| /* REVISIT TODO: Make this static when committing */ |
| static void |
| xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt) |
| { |
| if (ctxt != NULL) { |
| if (ctxt->cache != NULL) { |
| xmlXPathContextCachePtr cache = |
| (xmlXPathContextCachePtr) ctxt->cache; |
| |
| cache->dbgCachedAll = 0; |
| cache->dbgCachedNodeset = 0; |
| cache->dbgCachedString = 0; |
| cache->dbgCachedBool = 0; |
| cache->dbgCachedNumber = 0; |
| cache->dbgCachedPoint = 0; |
| cache->dbgCachedRange = 0; |
| cache->dbgCachedLocset = 0; |
| cache->dbgCachedUsers = 0; |
| cache->dbgCachedXSLTTree = 0; |
| cache->dbgCachedUndefined = 0; |
| |
| cache->dbgReusedAll = 0; |
| cache->dbgReusedNodeset = 0; |
| cache->dbgReusedString = 0; |
| cache->dbgReusedBool = 0; |
| cache->dbgReusedNumber = 0; |
| cache->dbgReusedPoint = 0; |
| cache->dbgReusedRange = 0; |
| cache->dbgReusedLocset = 0; |
| cache->dbgReusedUsers = 0; |
| cache->dbgReusedXSLTTree = 0; |
| cache->dbgReusedUndefined = 0; |
| } |
| } |
| |
| xmlXPathDebugObjCounterUndefined = 0; |
| xmlXPathDebugObjCounterNodeset = 0; |
| xmlXPathDebugObjCounterBool = 0; |
| xmlXPathDebugObjCounterNumber = 0; |
| xmlXPathDebugObjCounterString = 0; |
| xmlXPathDebugObjCounterPoint = 0; |
| xmlXPathDebugObjCounterRange = 0; |
| xmlXPathDebugObjCounterLocset = 0; |
| xmlXPathDebugObjCounterUsers = 0; |
| xmlXPathDebugObjCounterXSLTTree = 0; |
| xmlXPathDebugObjCounterAll = 0; |
| |
| xmlXPathDebugObjTotalUndefined = 0; |
| xmlXPathDebugObjTotalNodeset = 0; |
| xmlXPathDebugObjTotalBool = 0; |
| xmlXPathDebugObjTotalNumber = 0; |
| xmlXPathDebugObjTotalString = 0; |
| xmlXPathDebugObjTotalPoint = 0; |
| xmlXPathDebugObjTotalRange = 0; |
| xmlXPathDebugObjTotalLocset = 0; |
| xmlXPathDebugObjTotalUsers = 0; |
| xmlXPathDebugObjTotalXSLTTree = 0; |
| xmlXPathDebugObjTotalAll = 0; |
| |
| xmlXPathDebugObjMaxUndefined = 0; |
| xmlXPathDebugObjMaxNodeset = 0; |
| xmlXPathDebugObjMaxBool = 0; |
| xmlXPathDebugObjMaxNumber = 0; |
| xmlXPathDebugObjMaxString = 0; |
| xmlXPathDebugObjMaxPoint = 0; |
| xmlXPathDebugObjMaxRange = 0; |
| xmlXPathDebugObjMaxLocset = 0; |
| xmlXPathDebugObjMaxUsers = 0; |
| xmlXPathDebugObjMaxXSLTTree = 0; |
| xmlXPathDebugObjMaxAll = 0; |
| |
| } |
| |
| static void |
| xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt, |
| xmlXPathObjectType objType) |
| { |
| int isCached = 0; |
| |
| if (ctxt != NULL) { |
| if (ctxt->cache != NULL) { |
| xmlXPathContextCachePtr cache = |
| (xmlXPathContextCachePtr) ctxt->cache; |
| |
| isCached = 1; |
| |
| cache->dbgReusedAll++; |
| switch (objType) { |
| case XPATH_UNDEFINED: |
| cache->dbgReusedUndefined++; |
| break; |
| case XPATH_NODESET: |
| cache->dbgReusedNodeset++; |
| break; |
| case XPATH_BOOLEAN: |
| cache->dbgReusedBool++; |
| break; |
| case XPATH_NUMBER: |
| cache->dbgReusedNumber++; |
| break; |
| case XPATH_STRING: |
| cache->dbgReusedString++; |
| break; |
| case XPATH_POINT: |
| cache->dbgReusedPoint++; |
| break; |
| case XPATH_RANGE: |
| cache->dbgReusedRange++; |
| break; |
| case XPATH_LOCATIONSET: |
| cache->dbgReusedLocset++; |
| break; |
| case XPATH_USERS: |
| cache->dbgReusedUsers++; |
| break; |
| case XPATH_XSLT_TREE: |
| cache->dbgReusedXSLTTree++; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| switch (objType) { |
| case XPATH_UNDEFINED: |
| if (! isCached) |
| xmlXPathDebugObjTotalUndefined++; |
| xmlXPathDebugObjCounterUndefined++; |
| if (xmlXPathDebugObjCounterUndefined > |
| xmlXPathDebugObjMaxUndefined) |
| xmlXPathDebugObjMaxUndefined = |
| xmlXPathDebugObjCounterUndefined; |
| break; |
| case XPATH_NODESET: |
| if (! isCached) |
| xmlXPathDebugObjTotalNodeset++; |
| xmlXPathDebugObjCounterNodeset++; |
| if (xmlXPathDebugObjCounterNodeset > |
| xmlXPathDebugObjMaxNodeset) |
| xmlXPathDebugObjMaxNodeset = |
| xmlXPathDebugObjCounterNodeset; |
| break; |
| case XPATH_BOOLEAN: |
| if (! isCached) |
| xmlXPathDebugObjTotalBool++; |
| xmlXPathDebugObjCounterBool++; |
| if (xmlXPathDebugObjCounterBool > |
| xmlXPathDebugObjMaxBool) |
| xmlXPathDebugObjMaxBool = |
| xmlXPathDebugObjCounterBool; |
| break; |
| case XPATH_NUMBER: |
| if (! isCached) |
| xmlXPathDebugObjTotalNumber++; |
| xmlXPathDebugObjCounterNumber++; |
| if (xmlXPathDebugObjCounterNumber > |
| xmlXPathDebugObjMaxNumber) |
| xmlXPathDebugObjMaxNumber = |
| xmlXPathDebugObjCounterNumber; |
| break; |
| case XPATH_STRING: |
| if (! isCached) |
| xmlXPathDebugObjTotalString++; |
| xmlXPathDebugObjCounterString++; |
| if (xmlXPathDebugObjCounterString > |
| xmlXPathDebugObjMaxString) |
| xmlXPathDebugObjMaxString = |
| xmlXPathDebugObjCounterString; |
| break; |
| case XPATH_POINT: |
| if (! isCached) |
| xmlXPathDebugObjTotalPoint++; |
| xmlXPathDebugObjCounterPoint++; |
| if (xmlXPathDebugObjCounterPoint > |
| xmlXPathDebugObjMaxPoint) |
| xmlXPathDebugObjMaxPoint = |
| xmlXPathDebugObjCounterPoint; |
| break; |
| case XPATH_RANGE: |
| if (! isCached) |
| xmlXPathDebugObjTotalRange++; |
| xmlXPathDebugObjCounterRange++; |
| if (xmlXPathDebugObjCounterRange > |
| xmlXPathDebugObjMaxRange) |
| xmlXPathDebugObjMaxRange = |
| xmlXPathDebugObjCounterRange; |
| break; |
| case XPATH_LOCATIONSET: |
| if (! isCached) |
| xmlXPathDebugObjTotalLocset++; |
| xmlXPathDebugObjCounterLocset++; |
| if (xmlXPathDebugObjCounterLocset > |
| xmlXPathDebugObjMaxLocset) |
| xmlXPathDebugObjMaxLocset = |
| xmlXPathDebugObjCounterLocset; |
| break; |
| case XPATH_USERS: |
| if (! isCached) |
| xmlXPathDebugObjTotalUsers++; |
| xmlXPathDebugObjCounterUsers++; |
| if (xmlXPathDebugObjCounterUsers > |
| xmlXPathDebugObjMaxUsers) |
| xmlXPathDebugObjMaxUsers = |
| xmlXPathDebugObjCounterUsers; |
| break; |
| case XPATH_XSLT_TREE: |
| if (! isCached) |
| xmlXPathDebugObjTotalXSLTTree++; |
| xmlXPathDebugObjCounterXSLTTree++; |
| if (xmlXPathDebugObjCounterXSLTTree > |
| xmlXPathDebugObjMaxXSLTTree) |
| xmlXPathDebugObjMaxXSLTTree = |
| xmlXPathDebugObjCounterXSLTTree; |
| break; |
| default: |
| break; |
| } |
| if (! isCached) |
| xmlXPathDebugObjTotalAll++; |
| xmlXPathDebugObjCounterAll++; |
| if (xmlXPathDebugObjCounterAll > |
| xmlXPathDebugObjMaxAll) |
| xmlXPathDebugObjMaxAll = |
| xmlXPathDebugObjCounterAll; |
| } |
| |
| static void |
| xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt, |
| xmlXPathObjectType objType) |
| { |
| int isCached = 0; |
| |
| if (ctxt != NULL) { |
| if (ctxt->cache != NULL) { |
| xmlXPathContextCachePtr cache = |
| (xmlXPathContextCachePtr) ctxt->cache; |
| |
| isCached = 1; |
| |
| cache->dbgCachedAll++; |
| switch (objType) { |
| case XPATH_UNDEFINED: |
| cache->dbgCachedUndefined++; |
| break; |
| case XPATH_NODESET: |
| cache->dbgCachedNodeset++; |
| break; |
| case XPATH_BOOLEAN: |
| cache->dbgCachedBool++; |
| break; |
| case XPATH_NUMBER: |
| cache->dbgCachedNumber++; |
| break; |
| case XPATH_STRING: |
| cache->dbgCachedString++; |
| break; |
| case XPATH_POINT: |
| cache->dbgCachedPoint++; |
| break; |
| case XPATH_RANGE: |
| cache->dbgCachedRange++; |
| break; |
| case XPATH_LOCATIONSET: |
| cache->dbgCachedLocset++; |
| break; |
| case XPATH_USERS: |
| cache->dbgCachedUsers++; |
| break; |
| case XPATH_XSLT_TREE: |
| cache->dbgCachedXSLTTree++; |
| break; |
| default: |
| break; |
| } |
| |
| } |
| } |
| switch (objType) { |
| case XPATH_UNDEFINED: |
| xmlXPathDebugObjCounterUndefined--; |
| break; |
| case XPATH_NODESET: |
| xmlXPathDebugObjCounterNodeset--; |
| break; |
| case XPATH_BOOLEAN: |
| xmlXPathDebugObjCounterBool--; |
| break; |
| case XPATH_NUMBER: |
| xmlXPathDebugObjCounterNumber--; |
| break; |
| case XPATH_STRING: |
| xmlXPathDebugObjCounterString--; |
| break; |
| case XPATH_POINT: |
| xmlXPathDebugObjCounterPoint--; |
| break; |
| case XPATH_RANGE: |
| xmlXPathDebugObjCounterRange--; |
| break; |
| case XPATH_LOCATIONSET: |
| xmlXPathDebugObjCounterLocset--; |
| break; |
| case XPATH_USERS: |
| xmlXPathDebugObjCounterUsers--; |
| break; |
| case XPATH_XSLT_TREE: |
| xmlXPathDebugObjCounterXSLTTree--; |
| break; |
| default: |
| break; |
| } |
| xmlXPathDebugObjCounterAll--; |
| } |
| |
| /* REVISIT TODO: Make this static when committing */ |
| static void |
| xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt) |
| { |
| int reqAll, reqNodeset, reqString, reqBool, reqNumber, |
| reqXSLTTree, reqUndefined; |
| int caAll = 0, caNodeset = 0, caString = 0, caBool = 0, |
| caNumber = 0, caXSLTTree = 0, caUndefined = 0; |
| int reAll = 0, reNodeset = 0, reString = 0, reBool = 0, |
| reNumber = 0, reXSLTTree = 0, reUndefined = 0; |
| int leftObjs = xmlXPathDebugObjCounterAll; |
| |
| reqAll = xmlXPathDebugObjTotalAll; |
| reqNodeset = xmlXPathDebugObjTotalNodeset; |
| reqString = xmlXPathDebugObjTotalString; |
| reqBool = xmlXPathDebugObjTotalBool; |
| reqNumber = xmlXPathDebugObjTotalNumber; |
| reqXSLTTree = xmlXPathDebugObjTotalXSLTTree; |
| reqUndefined = xmlXPathDebugObjTotalUndefined; |
| |
| printf("# XPath object usage:\n"); |
| |
| if (ctxt != NULL) { |
| if (ctxt->cache != NULL) { |
| xmlXPathContextCachePtr cache = |
| (xmlXPathContextCachePtr) ctxt->cache; |
| |
| reAll = cache->dbgReusedAll; |
| reqAll += reAll; |
| reNodeset = cache->dbgReusedNodeset; |
| reqNodeset += reNodeset; |
| reString = cache->dbgReusedString; |
| reqString += reString; |
| reBool = cache->dbgReusedBool; |
| reqBool += reBool; |
| reNumber = cache->dbgReusedNumber; |
| reqNumber += reNumber; |
| reXSLTTree = cache->dbgReusedXSLTTree; |
| reqXSLTTree += reXSLTTree; |
| reUndefined = cache->dbgReusedUndefined; |
| reqUndefined += reUndefined; |
| |
| caAll = cache->dbgCachedAll; |
| caBool = cache->dbgCachedBool; |
| caNodeset = cache->dbgCachedNodeset; |
| caString = cache->dbgCachedString; |
| caNumber = cache->dbgCachedNumber; |
| caXSLTTree = cache->dbgCachedXSLTTree; |
| caUndefined = cache->dbgCachedUndefined; |
| |
| if (cache->nodesetObjs) |
| leftObjs -= cache->nodesetObjs->number; |
| if (cache->stringObjs) |
| leftObjs -= cache->stringObjs->number; |
| if (cache->booleanObjs) |
| leftObjs -= cache->booleanObjs->number; |
| if (cache->numberObjs) |
| leftObjs -= cache->numberObjs->number; |
| if (cache->miscObjs) |
| leftObjs -= cache->miscObjs->number; |
| } |
| } |
| |
| printf("# all\n"); |
| printf("# total : %d\n", reqAll); |
| printf("# left : %d\n", leftObjs); |
| printf("# created: %d\n", xmlXPathDebugObjTotalAll); |
| printf("# reused : %d\n", reAll); |
| printf("# max : %d\n", xmlXPathDebugObjMaxAll); |
| |
| printf("# node-sets\n"); |
| printf("# total : %d\n", reqNodeset); |
| printf("# created: %d\n", xmlXPathDebugObjTotalNodeset); |
| printf("# reused : %d\n", reNodeset); |
| printf("# max : %d\n", xmlXPathDebugObjMaxNodeset); |
| |
| printf("# strings\n"); |
| printf("# total : %d\n", reqString); |
| printf("# created: %d\n", xmlXPathDebugObjTotalString); |
| printf("# reused : %d\n", reString); |
| printf("# max : %d\n", xmlXPathDebugObjMaxString); |
| |
| printf("# booleans\n"); |
| printf("# total : %d\n", reqBool); |
| printf("# created: %d\n", xmlXPathDebugObjTotalBool); |
| printf("# reused : %d\n", reBool); |
| printf("# max : %d\n", xmlXPathDebugObjMaxBool); |
| |
| printf("# numbers\n"); |
| printf("# total : %d\n", reqNumber); |
| printf("# created: %d\n", xmlXPathDebugObjTotalNumber); |
| printf("# reused : %d\n", reNumber); |
| printf("# max : %d\n", xmlXPathDebugObjMaxNumber); |
| |
| printf("# XSLT result tree fragments\n"); |
| printf("# total : %d\n", reqXSLTTree); |
| printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree); |
| printf("# reused : %d\n", reXSLTTree); |
| printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree); |
| |
| printf("# undefined\n"); |
| printf("# total : %d\n", reqUndefined); |
| printf("# created: %d\n", xmlXPathDebugObjTotalUndefined); |
| printf("# reused : %d\n", reUndefined); |
| printf("# max : %d\n", xmlXPathDebugObjMaxUndefined); |
| |
| } |
| |
| #endif /* XP_DEBUG_OBJ_USAGE */ |
| |
| #endif /* LIBXML_DEBUG_ENABLED */ |
| |
| /************************************************************************ |
| * * |
| * XPath object caching * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * xmlXPathNewCache: |
| * |
| * Create a new object cache |
| * |
| * Returns the xmlXPathCache just allocated. |
| */ |
| static xmlXPathContextCachePtr |
| xmlXPathNewCache(void) |
| { |
| xmlXPathContextCachePtr ret; |
| |
| ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache)); |
| if (ret == NULL) { |
| xmlXPathErrMemory(NULL, "creating object cache\n"); |
| return(NULL); |
| } |
| memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache)); |
| ret->maxNodeset = 100; |
| ret->maxString = 100; |
| ret->maxBoolean = 100; |
| ret->maxNumber = 100; |
| ret->maxMisc = 100; |
| return(ret); |
| } |
| |
| static void |
| xmlXPathCacheFreeObjectList(xmlPointerListPtr list) |
| { |
| int i; |
| xmlXPathObjectPtr obj; |
| |
| if (list == NULL) |
| return; |
| |
| for (i = 0; i < list->number; i++) { |
| obj = list->items[i]; |
| /* |
| * Note that it is already assured that we don't need to |
| * look out for namespace nodes in the node-set. |
| */ |
| if (obj->nodesetval != NULL) { |
| if (obj->nodesetval->nodeTab != NULL) |
| xmlFree(obj->nodesetval->nodeTab); |
| xmlFree(obj->nodesetval); |
| } |
| xmlFree(obj); |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjCounterAll--; |
| #endif |
| } |
| xmlPointerListFree(list); |
| } |
| |
| static void |
| xmlXPathFreeCache(xmlXPathContextCachePtr cache) |
| { |
| if (cache == NULL) |
| return; |
| if (cache->nodesetObjs) |
| xmlXPathCacheFreeObjectList(cache->nodesetObjs); |
| if (cache->stringObjs) |
| xmlXPathCacheFreeObjectList(cache->stringObjs); |
| if (cache->booleanObjs) |
| xmlXPathCacheFreeObjectList(cache->booleanObjs); |
| if (cache->numberObjs) |
| xmlXPathCacheFreeObjectList(cache->numberObjs); |
| if (cache->miscObjs) |
| xmlXPathCacheFreeObjectList(cache->miscObjs); |
| xmlFree(cache); |
| } |
| |
| /** |
| * xmlXPathContextSetCache: |
| * |
| * @ctxt: the XPath context |
| * @active: enables/disables (creates/frees) the cache |
| * @value: a value with semantics dependent on @options |
| * @options: options (currently only the value 0 is used) |
| * |
| * Creates/frees an object cache on the XPath context. |
| * If activates XPath objects (xmlXPathObject) will be cached internally |
| * to be reused. |
| * @options: |
| * 0: This will set the XPath object caching: |
| * @value: |
| * This will set the maximum number of XPath objects |
| * to be cached per slot |
| * There are 5 slots for: node-set, string, number, boolean, and |
| * misc objects. Use <0 for the default number (100). |
| * Other values for @options have currently no effect. |
| * |
| * Returns 0 if the setting succeeded, and -1 on API or internal errors. |
| */ |
| int |
| xmlXPathContextSetCache(xmlXPathContextPtr ctxt, |
| int active, |
| int value, |
| int options) |
| { |
| if (ctxt == NULL) |
| return(-1); |
| if (active) { |
| xmlXPathContextCachePtr cache; |
| |
| if (ctxt->cache == NULL) { |
| ctxt->cache = xmlXPathNewCache(); |
| if (ctxt->cache == NULL) |
| return(-1); |
| } |
| cache = (xmlXPathContextCachePtr) ctxt->cache; |
| if (options == 0) { |
| if (value < 0) |
| value = 100; |
| cache->maxNodeset = value; |
| cache->maxString = value; |
| cache->maxNumber = value; |
| cache->maxBoolean = value; |
| cache->maxMisc = value; |
| } |
| } else if (ctxt->cache != NULL) { |
| xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache); |
| ctxt->cache = NULL; |
| } |
| return(0); |
| } |
| |
| /** |
| * xmlXPathCacheWrapNodeSet: |
| * @ctxt: the XPath context |
| * @val: the NodePtr value |
| * |
| * This is the cached version of xmlXPathWrapNodeSet(). |
| * Wrap the Nodeset @val in a new xmlXPathObjectPtr |
| * |
| * Returns the created or reused object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val) |
| { |
| if ((ctxt != NULL) && (ctxt->cache != NULL)) { |
| xmlXPathContextCachePtr cache = |
| (xmlXPathContextCachePtr) ctxt->cache; |
| |
| if ((cache->miscObjs != NULL) && |
| (cache->miscObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->miscObjs->items[--cache->miscObjs->number]; |
| ret->type = XPATH_NODESET; |
| ret->nodesetval = val; |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); |
| #endif |
| return(ret); |
| } |
| } |
| |
| return(xmlXPathWrapNodeSet(val)); |
| |
| } |
| |
| /** |
| * xmlXPathCacheWrapString: |
| * @ctxt: the XPath context |
| * @val: the xmlChar * value |
| * |
| * This is the cached version of xmlXPathWrapString(). |
| * Wraps the @val string into an XPath object. |
| * |
| * Returns the created or reused object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val) |
| { |
| if ((ctxt != NULL) && (ctxt->cache != NULL)) { |
| xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; |
| |
| if ((cache->stringObjs != NULL) && |
| (cache->stringObjs->number != 0)) |
| { |
| |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->stringObjs->items[--cache->stringObjs->number]; |
| ret->type = XPATH_STRING; |
| ret->stringval = val; |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); |
| #endif |
| return(ret); |
| } else if ((cache->miscObjs != NULL) && |
| (cache->miscObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| /* |
| * Fallback to misc-cache. |
| */ |
| ret = (xmlXPathObjectPtr) |
| cache->miscObjs->items[--cache->miscObjs->number]; |
| |
| ret->type = XPATH_STRING; |
| ret->stringval = val; |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); |
| #endif |
| return(ret); |
| } |
| } |
| return(xmlXPathWrapString(val)); |
| } |
| |
| /** |
| * xmlXPathCacheNewNodeSet: |
| * @ctxt: the XPath context |
| * @val: the NodePtr value |
| * |
| * This is the cached version of xmlXPathNewNodeSet(). |
| * Acquire an xmlXPathObjectPtr of type NodeSet and initialize |
| * it with the single Node @val |
| * |
| * Returns the created or reused object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val) |
| { |
| if ((ctxt != NULL) && (ctxt->cache)) { |
| xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; |
| |
| if ((cache->nodesetObjs != NULL) && |
| (cache->nodesetObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| /* |
| * Use the nodeset-cache. |
| */ |
| ret = (xmlXPathObjectPtr) |
| cache->nodesetObjs->items[--cache->nodesetObjs->number]; |
| ret->type = XPATH_NODESET; |
| ret->boolval = 0; |
| if (val) { |
| if ((ret->nodesetval->nodeMax == 0) || |
| (val->type == XML_NAMESPACE_DECL)) |
| { |
| /* TODO: Check memory error. */ |
| xmlXPathNodeSetAddUnique(ret->nodesetval, val); |
| } else { |
| ret->nodesetval->nodeTab[0] = val; |
| ret->nodesetval->nodeNr = 1; |
| } |
| } |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET); |
| #endif |
| return(ret); |
| } else if ((cache->miscObjs != NULL) && |
| (cache->miscObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| /* |
| * Fallback to misc-cache. |
| */ |
| |
| ret = (xmlXPathObjectPtr) |
| cache->miscObjs->items[--cache->miscObjs->number]; |
| |
| 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 |
| return(ret); |
| } |
| } |
| return(xmlXPathNewNodeSet(val)); |
| } |
| |
| /** |
| * xmlXPathCacheNewCString: |
| * @ctxt: the XPath context |
| * @val: the char * value |
| * |
| * This is the cached version of xmlXPathNewCString(). |
| * Acquire an xmlXPathObjectPtr of type string and of value @val |
| * |
| * Returns the created or reused object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val) |
| { |
| if ((ctxt != NULL) && (ctxt->cache)) { |
| xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; |
| |
| if ((cache->stringObjs != NULL) && |
| (cache->stringObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->stringObjs->items[--cache->stringObjs->number]; |
| |
| ret->type = XPATH_STRING; |
| ret->stringval = xmlStrdup(BAD_CAST val); |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); |
| #endif |
| return(ret); |
| } else if ((cache->miscObjs != NULL) && |
| (cache->miscObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->miscObjs->items[--cache->miscObjs->number]; |
| |
| ret->type = XPATH_STRING; |
| ret->stringval = xmlStrdup(BAD_CAST val); |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); |
| #endif |
| return(ret); |
| } |
| } |
| return(xmlXPathNewCString(val)); |
| } |
| |
| /** |
| * xmlXPathCacheNewString: |
| * @ctxt: the XPath context |
| * @val: the xmlChar * value |
| * |
| * This is the cached version of xmlXPathNewString(). |
| * Acquire an xmlXPathObjectPtr of type string and of value @val |
| * |
| * Returns the created or reused object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val) |
| { |
| if ((ctxt != NULL) && (ctxt->cache)) { |
| xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; |
| |
| if ((cache->stringObjs != NULL) && |
| (cache->stringObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->stringObjs->items[--cache->stringObjs->number]; |
| ret->type = XPATH_STRING; |
| if (val != NULL) |
| ret->stringval = xmlStrdup(val); |
| else |
| ret->stringval = xmlStrdup((const xmlChar *)""); |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); |
| #endif |
| return(ret); |
| } else if ((cache->miscObjs != NULL) && |
| (cache->miscObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->miscObjs->items[--cache->miscObjs->number]; |
| |
| ret->type = XPATH_STRING; |
| if (val != NULL) |
| ret->stringval = xmlStrdup(val); |
| else |
| ret->stringval = xmlStrdup((const xmlChar *)""); |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING); |
| #endif |
| return(ret); |
| } |
| } |
| return(xmlXPathNewString(val)); |
| } |
| |
| /** |
| * xmlXPathCacheNewBoolean: |
| * @ctxt: the XPath context |
| * @val: the boolean value |
| * |
| * This is the cached version of xmlXPathNewBoolean(). |
| * Acquires an xmlXPathObjectPtr of type boolean and of value @val |
| * |
| * Returns the created or reused object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val) |
| { |
| if ((ctxt != NULL) && (ctxt->cache)) { |
| xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; |
| |
| if ((cache->booleanObjs != NULL) && |
| (cache->booleanObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->booleanObjs->items[--cache->booleanObjs->number]; |
| ret->type = XPATH_BOOLEAN; |
| ret->boolval = (val != 0); |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN); |
| #endif |
| return(ret); |
| } else if ((cache->miscObjs != NULL) && |
| (cache->miscObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->miscObjs->items[--cache->miscObjs->number]; |
| |
| ret->type = XPATH_BOOLEAN; |
| ret->boolval = (val != 0); |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN); |
| #endif |
| return(ret); |
| } |
| } |
| return(xmlXPathNewBoolean(val)); |
| } |
| |
| /** |
| * xmlXPathCacheNewFloat: |
| * @ctxt: the XPath context |
| * @val: the double value |
| * |
| * This is the cached version of xmlXPathNewFloat(). |
| * Acquires an xmlXPathObjectPtr of type double and of value @val |
| * |
| * Returns the created or reused object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val) |
| { |
| if ((ctxt != NULL) && (ctxt->cache)) { |
| xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache; |
| |
| if ((cache->numberObjs != NULL) && |
| (cache->numberObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->numberObjs->items[--cache->numberObjs->number]; |
| ret->type = XPATH_NUMBER; |
| ret->floatval = val; |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER); |
| #endif |
| return(ret); |
| } else if ((cache->miscObjs != NULL) && |
| (cache->miscObjs->number != 0)) |
| { |
| xmlXPathObjectPtr ret; |
| |
| ret = (xmlXPathObjectPtr) |
| cache->miscObjs->items[--cache->miscObjs->number]; |
| |
| ret->type = XPATH_NUMBER; |
| ret->floatval = val; |
| #ifdef XP_DEBUG_OBJ_USAGE |
| xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER); |
| #endif |
| return(ret); |
| } |
| } |
| return(xmlXPathNewFloat(val)); |
| } |
| |
| /** |
| * xmlXPathCacheConvertString: |
| * @ctxt: the XPath context |
| * @val: an XPath object |
| * |
| * This is the cached version of xmlXPathConvertString(). |
| * Converts an existing object to its string() equivalent |
| * |
| * Returns a created or reused object, the old one is freed (cached) |
| * (or the operation is done directly on @val) |
| */ |
| |
| static xmlXPathObjectPtr |
| xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { |
| xmlChar *res = NULL; |
| |
| if (val == NULL) |
| return(xmlXPathCacheNewCString(ctxt, "")); |
| |
| switch (val->type) { |
| case XPATH_UNDEFINED: |
| #ifdef DEBUG_EXPR |
| xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n"); |
| #endif |
| break; |
| case XPATH_NODESET: |
| case XPATH_XSLT_TREE: |
| res = xmlXPathCastNodeSetToString(val->nodesetval); |
| break; |
| case XPATH_STRING: |
| return(val); |
| case XPATH_BOOLEAN: |
| res = xmlXPathCastBooleanToString(val->boolval); |
| break; |
| case XPATH_NUMBER: |
| res = xmlXPathCastNumberToString(val->floatval); |
| break; |
| case XPATH_USERS: |
| case XPATH_POINT: |
| case XPATH_RANGE: |
| case XPATH_LOCATIONSET: |
| TODO; |
| break; |
| } |
| xmlXPathReleaseObject(ctxt, val); |
| if (res == NULL) |
| return(xmlXPathCacheNewCString(ctxt, "")); |
| return(xmlXPathCacheWrapString(ctxt, res)); |
| } |
| |
| /** |
| * xmlXPathCacheObjectCopy: |
| * @ctxt: the XPath context |
| * @val: the original object |
| * |
| * This is the cached version of xmlXPathObjectCopy(). |
| * Acquire a copy of a given object |
| * |
| * Returns a created or reused created object. |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) |
| { |
| if (val == NULL) |
| return(NULL); |
| |
| if (XP_HAS_CACHE(ctxt)) { |
| switch (val->type) { |
| case XPATH_NODESET: |
| return(xmlXPathCacheWrapNodeSet(ctxt, |
| xmlXPathNodeSetMerge(NULL, val->nodesetval))); |
| case XPATH_STRING: |
| return(xmlXPathCacheNewString(ctxt, val->stringval)); |
| case XPATH_BOOLEAN: |
| return(xmlXPathCacheNewBoolean(ctxt, val->boolval)); |
| case XPATH_NUMBER: |
| return(xmlXPathCacheNewFloat(ctxt, val->floatval)); |
| default: |
| break; |
| } |
| } |
| return(xmlXPathObjectCopy(val)); |
| } |
| |
| /** |
| * xmlXPathCacheConvertBoolean: |
| * @ctxt: the XPath context |
| * @val: an XPath object |
| * |
| * This is the cached version of xmlXPathConvertBoolean(). |
| * Converts an existing object to its boolean() equivalent |
| * |
| * Returns a created or reused object, the old one is freed (or the operation |
| * is done directly on @val) |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { |
| xmlXPathObjectPtr ret; |
| |
| if (val == NULL) |
| return(xmlXPathCacheNewBoolean(ctxt, 0)); |
| if (val->type == XPATH_BOOLEAN) |
| return(val); |
| ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val)); |
| xmlXPathReleaseObject(ctxt, val); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathCacheConvertNumber: |
| * @ctxt: the XPath context |
| * @val: an XPath object |
| * |
| * This is the cached version of xmlXPathConvertNumber(). |
| * Converts an existing object to its number() equivalent |
| * |
| * Returns a created or reused object, the old one is freed (or the operation |
| * is done directly on @val) |
| */ |
| static xmlXPathObjectPtr |
| xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) { |
| xmlXPathObjectPtr ret; |
| |
| if (val == NULL) |
| return(xmlXPathCacheNewFloat(ctxt, 0.0)); |
| if (val->type == XPATH_NUMBER) |
| return(val); |
| ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val)); |
| xmlXPathReleaseObject(ctxt, val); |
| return(ret); |
| } |
| |
| /************************************************************************ |
| * * |
| * Parser stacks related functions and macros * |
| * * |
| ************************************************************************/ |
| |
| /** |
| * 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 |
| * |
| * Pops the top XPath object from the value stack |
| * |
| * Returns the XPath object just removed |
| */ |
| xmlXPathObjectPtr |
| valuePop(xmlXPathParserContextPtr ctxt) |
| { |
| xmlXPathObjectPtr ret; |
| |
| 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]; |
| else |
| ctxt->value = NULL; |
| ret = ctxt->valueTab[ctxt->valueNr]; |
| ctxt->valueTab[ctxt->valueNr] = NULL; |
| return (ret); |
| } |
| /** |
| * valuePush: |
| * @ctxt: an XPath evaluation context |
| * @value: the XPath object |
| * |
| * Pushes a new XPath object on top of the value stack. If value is NULL, |
| * a memory error is recorded in the parser context. |
| * |
| * Returns the number of items on the value stack, or -1 in case of error. |
| */ |
| int |
| valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value) |
| { |
| if (ctxt == NULL) return(-1); |
| if (value == NULL) { |
| /* |
| * A NULL value typically indicates that a memory allocation failed, |
| * so we set ctxt->error here to propagate the error. |
| */ |
| ctxt->error = XPATH_MEMORY_ERROR; |
| return(-1); |
| } |
| if (ctxt->valueNr >= ctxt->valueMax) { |
| xmlXPathObjectPtr *tmp; |
| |
| if (ctxt->valueMax >= XPATH_MAX_STACK_DEPTH) { |
| xmlXPathPErrMemory(ctxt, "XPath stack depth limit reached\n"); |
| return (-1); |
| } |
| tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab, |
| 2 * ctxt->valueMax * |
| sizeof(ctxt->valueTab[0])); |
| if (tmp == NULL) { |
| xmlXPathPErrMemory(ctxt, "pushing value\n"); |
| return (-1); |
| } |
| ctxt->valueMax *= 2; |
| ctxt->valueTab = tmp; |
| } |
| ctxt->valueTab[ctxt->valueNr] = value; |
| ctxt->value = value; |
| return (ctxt->valueNr++); |
| } |
| |
| /** |
| * xmlXPathPopBoolean: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a boolean from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the boolean |
| */ |
| int |
| xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| int ret; |
| |
| obj = valuePop(ctxt); |
| if (obj == NULL) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(0); |
| } |
| if (obj->type != XPATH_BOOLEAN) |
| ret = xmlXPathCastToBoolean(obj); |
| else |
| ret = obj->boolval; |
| xmlXPathReleaseObject(ctxt->context, obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopNumber: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a number from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the number |
| */ |
| double |
| xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| double ret; |
| |
| obj = valuePop(ctxt); |
| if (obj == NULL) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(0); |
| } |
| if (obj->type != XPATH_NUMBER) |
| ret = xmlXPathCastToNumber(obj); |
| else |
| ret = obj->floatval; |
| xmlXPathReleaseObject(ctxt->context, obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopString: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a string from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the string |
| */ |
| xmlChar * |
| xmlXPathPopString (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| xmlChar * ret; |
| |
| obj = valuePop(ctxt); |
| if (obj == NULL) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(NULL); |
| } |
| ret = xmlXPathCastToString(obj); /* this does required strdup */ |
| /* TODO: needs refactoring somewhere else */ |
| if (obj->stringval == ret) |
| obj->stringval = NULL; |
| xmlXPathReleaseObject(ctxt->context, obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopNodeSet: |
| * @ctxt: an XPath parser context |
| * |
| * Pops a node-set from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the node-set |
| */ |
| xmlNodeSetPtr |
| xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| xmlNodeSetPtr ret; |
| |
| if (ctxt == NULL) return(NULL); |
| if (ctxt->value == NULL) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(NULL); |
| } |
| if (!xmlXPathStackIsNodeSet(ctxt)) { |
| xmlXPathSetTypeError(ctxt); |
| return(NULL); |
| } |
| obj = valuePop(ctxt); |
| ret = obj->nodesetval; |
| #if 0 |
| /* to fix memory leak of not clearing obj->user */ |
| if (obj->boolval && obj->user != NULL) |
| xmlFreeNodeList((xmlNodePtr) obj->user); |
| #endif |
| obj->nodesetval = NULL; |
| xmlXPathReleaseObject(ctxt->context, obj); |
| return(ret); |
| } |
| |
| /** |
| * xmlXPathPopExternal: |
| * @ctxt: an XPath parser context |
| * |
| * Pops an external object from the stack, handling conversion if needed. |
| * Check error with #xmlXPathCheckError. |
| * |
| * Returns the object |
| */ |
| void * |
| xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) { |
| xmlXPathObjectPtr obj; |
| void * ret; |
| |
| if ((ctxt == NULL) || (ctxt->value == NULL)) { |
| xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND); |
| return(NULL); |
| } |
| if (ctxt->value->type != XPATH_USERS) { |
| xmlXPathSetTypeError(ctxt); |
| return(NULL); |
| } |
| obj = valuePop(ctxt); |
| ret = obj->user; |
| obj->user = NULL; |
| xmlXPathReleaseObject(ctxt->context, obj); |
| return(ret); |
| } |
| |
| /* |
| * 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 xmlChar to be parsed. |
| * CUR returns the current xmlChar value, i.e. a 8 bit value |
| * in ISO-Latin or UTF-8. |
| * 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 xmlChar. Same as CUR is should be used only |
| * to compare on ASCII based substring. |
| * SKIP(n) Skip n xmlChar, 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 xmlChar. |
| */ |
| |
| #define CUR (*ctxt->cur) |
| #define SKIP(val) ctxt->cur += (val) |
| #define NXT(val) ctxt->cur[(val)] |
| #define CUR_PTR ctxt->cur |
| #define CUR_CHAR(l) xmlXPathCurrentChar(ctxt, &l) |
| |
| #define COPY_BUF(l,b,i,v) \ |
| if (l == 1) b[i++] = (xmlChar) v; \ |
| else i += xmlCopyChar(l,&b[i],v) |
| |
| #define NEXTL(l) ctxt->cur += l |
| |
| #define SKIP_BLANKS \ |
| while (IS_BLANK_CH(*(ctxt->cur))) NEXT |
| |
| #define CURRENT (*ctxt->cur) |
| #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur) |
| |
| |
| #ifndef DBL_DIG |
| #define DBL_DIG 16 |
| #endif |
| #ifndef DBL_EPSILON |
| #define DBL_EPSILON 1E-9 |
| #endif |
| |
| #define UPPER_DOUBLE 1E9 |
| #define LOWER_DOUBLE 1E-5 |
| #define LOWER_DOUBLE_EXP 5 |
| |
| #define INTEGER_DIGITS DBL_DIG |
| #define FRACTION_DIGITS (DBL_DIG + 1 + (LOWER_DOUBLE_EXP)) |
| #define EXPONENT_DIGITS (3 + 2) |
| |
| /** |
| * xmlXPathFormatNumber: |
| * @number: number to format |
| * @buffer: output buffer |
| * @buffersize: size of output buffer |
| * |
| * Convert the number into a string representation. |
| */ |
| static void |
| xmlXPathFormatNumber(double number, char buffer[], int buffersize) |
| { |
| switch (xmlXPathIsInf(number)) { |
| case 1: |
| if (buffersize > (int)sizeof("Infinity")) |
| snprintf(buffer, buffersize, "Infinity"); |
| break; |
| case -1: |
| if (buffersize > (int)sizeof("-Infinity")) |
| snprintf(buffer, buffersize, "-Infinity"); |
| break; |
| default: |
| if (xmlXPathIsNaN(number)) { |
| if (buffersize > (int)sizeof("NaN")) |
| snprintf(buffer, buffersize, "NaN"); |
| } else if (number == 0) { |
| /* Omit sign for negative zero. */ |
| snprintf(buffer, buffersize, "0"); |
| } else if ((number > INT_MIN) && (number < INT_MAX) && |
| (number == (int) number)) { |
| char work[30]; |
| char *ptr, *cur; |
| int value = (int) number; |
| |
| ptr = &buffer[0]; |
| if (value == 0) { |
| *ptr++ = '0'; |
| } else { |
| snprintf(work, 29, "%d", value); |
| cur = &work[0]; |
| while ((*cur) && (ptr - buffer < buffersize)) { |
| *ptr++ = *cur++; |
| } |
| } |
| if (ptr - buffer < buffersize) { |
| *ptr = 0; |
|