blob: 0d5aa68ba5227004db123a40c73c2b7b00ec4617 [file] [log] [blame]
/*
* 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;