blob: 8482c40b958c2def92e4de1533c05332abefe06a [file] [log] [blame] [edit]
/*
* 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
*f
* 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
*
*/
#define IN_LIBXML
#include "libxml.h"
#include <string.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
#ifdef LIBXML_PATTERN_ENABLED
#define XPATH_STREAMING
#endif
#define TODO \
xmlGenericError(xmlGenericErrorContext, \
"Unimplemented block at %s:%d\n", \
__FILE__, __LINE__);
/*
* 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 */
/*
* 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)
*/
#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
/************************************************************************
* *
* Floating point stuff *
* *
************************************************************************/
#ifndef TRIO_REPLACE_STDIO
#define TRIO_PUBLIC static
#endif
#include "trionan.c"
/*
* The lack of portability of this section of the libc is annoying !
*/
double xmlXPathNAN = 0;
double xmlXPathPINF = 1;
double xmlXPathNINF = -1;
static double xmlXPathNZERO = 0; /* not exported from headers */
static int xmlXPathInitialized = 0;
/**
* xmlXPathInit:
*
* Initialize the XPath environment
*/
void
xmlXPathInit(void) {
if (xmlXPathInitialized) return;
xmlXPathPINF = trio_pinf();
xmlXPathNINF = trio_ninf();
xmlXPathNAN = trio_nan();
xmlXPathNZERO = trio_nzero();
xmlXPathInitialized = 1;
}
/**
* xmlXPathIsNaN:
* @val: a double value
*
* Provides a portable isnan() function to detect whether a double
* is a NotaNumber. Based on trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 if the value is a NaN, 0 otherwise
*/
int
xmlXPathIsNaN(double val) {
return(trio_isnan(val));
}
/**
* xmlXPathIsInf:
* @val: a double value
*
* Provides a portable isinf() function to detect whether a double
* is a +Infinite or -Infinite. Based on trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
*/
int
xmlXPathIsInf(double val) {
return(trio_isinf(val));
}
#endif /* SCHEMAS or XPATH */
#ifdef LIBXML_XPATH_ENABLED
/**
* xmlXPathGetSign:
* @val: a double value
*
* Provides a portable function to detect the sign of a double
* Modified from trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 if the value is Negative, 0 if positive
*/
static int
xmlXPathGetSign(double val) {
return(trio_signbit(val));
}
/*
* TODO: when compatibility allows remove all "fake node libxslt" strings
* the test should just be name[0] = ' '
*/
#ifdef DEBUG_XPATH_EXPRESSION
#define DEBUG_STEP
#define DEBUG_EXPR
#define DEBUG_EVAL_COUNTS
#endif
static xmlNs xmlXPathXMLNamespaceStruct = {
NULL,
XML_NAMESPACE_DECL,
XML_XML_NAMESPACE,
BAD_CAST "xml",
NULL,
NULL
};
static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
#ifndef LIBXML_THREAD_ENABLED
/*
* Optimizer is disabled only when threaded apps are detected while
* the library ain't compiled for thread safety.
*/
static int xmlXPathDisableOptimizer = 0;
#endif
/************************************************************************
* *
* Error handling routines *
* *
************************************************************************/
/**
* XP_ERRORNULL:
* @X: the error code
*
* Macro to raise an XPath error and return NULL.
*/
#define XP_ERRORNULL(X) \
{ xmlXPathErr(ctxt, X); return(NULL); }
/*
* The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
*/
static const char *xmlXPathErrorMessages[] = {
"Ok\n",
"Number encoding\n",
"Unfinished literal\n",
"Start of literal\n",
"Expected $ for variable reference\n",
"Undefined variable\n",
"Invalid predicate\n",
"Invalid expression\n",
"Missing closing curly brace\n",
"Unregistered function\n",
"Invalid operand\n",
"Invalid type\n",
"Invalid number of arguments\n",
"Invalid context size\n",
"Invalid context position\n",
"Memory allocation error\n",
"Syntax error\n",
"Resource error\n",
"Sub resource error\n",
"Undefined namespace prefix\n",
"Encoding error\n",
"Char out of XML range\n",
"Invalid or incomplete context\n",
"?? Unknown error ??\n" /* Must be last in the list! */
};
#define MAXERRNO ((int)(sizeof(xmlXPathErrorMessages) / \
sizeof(xmlXPathErrorMessages[0])) - 1)
/**
* xmlXPathErrMemory:
* @ctxt: an XPath context
* @extra: extra informations
*
* Handle a redefinition of attribute error
*/
static void
xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
{
if (ctxt != NULL) {
if (extra) {
xmlChar buf[200];
xmlStrPrintf(buf, 200,
BAD_CAST "Memory allocation failed : %s\n",
extra);
ctxt->lastError.message = (char *) xmlStrdup(buf);
} else {
ctxt->lastError.message = (char *)
xmlStrdup(BAD_CAST "Memory allocation failed\n");
}
ctxt->lastError.domain = XML_FROM_XPATH;
ctxt->lastError.code = XML_ERR_NO_MEMORY;
if (ctxt->error != NULL)
ctxt->error(ctxt->userData, &ctxt->lastError);
} else {
if (extra)
__xmlRaiseError(NULL, NULL, NULL,
NULL, NULL, XML_FROM_XPATH,
XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
extra, NULL, NULL, 0, 0,
"Memory allocation failed : %s\n", extra);
else
__xmlRaiseError(NULL, NULL, NULL,
NULL, NULL, XML_FROM_XPATH,
XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
NULL, NULL, NULL, 0, 0,
"Memory allocation failed\n");
}
}
/**
* xmlXPathPErrMemory:
* @ctxt: an XPath parser context
* @extra: extra informations
*
* Handle a redefinition of attribute error
*/
static void
xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
{
if (ctxt == NULL)
xmlXPathErrMemory(NULL, extra);
else {
ctxt->error = XPATH_MEMORY_ERROR;
xmlXPathErrMemory(ctxt->context, extra);
}
}
/**
* xmlXPathErr:
* @ctxt: a XPath parser context
* @error: the error code
*
* Handle an XPath error
*/
void
xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
{
if ((error < 0) || (error > MAXERRNO))
error = MAXERRNO;
if (ctxt == NULL) {
__xmlRaiseError(NULL, NULL, NULL,
NULL, NULL, XML_FROM_XPATH,
error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
XML_ERR_ERROR, NULL, 0,
NULL, NULL, NULL, 0, 0,
"%s", xmlXPathErrorMessages[error]);
return;
}
ctxt->error = error;
if (ctxt->context == NULL) {
__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);
}
/************************************************************************
* *
* 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) {
list->size *= 2;
list->items = (void **) xmlRealloc(list->items,
list->size * sizeof(void *));
if (list->items == NULL) {
xmlXPathErrMemory(NULL,
"xmlPointerListCreate: 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_RESET, /* 10 */
XPATH_OP_COLLECT,
XPATH_OP_VALUE, /* 12 */
XPATH_OP_VARIABLE,
XPATH_OP_FUNCTION,
XPATH_OP_ARG,
XPATH_OP_PREDICATE,
XPATH_OP_FILTER, /* 17 */
XPATH_OP_SORT /* 18 */
#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;
#define XP_REWRITE_DOS_CHILD_ELEM 1
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;
void *cache;
void *cacheURI;
int rewriteType;
};
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 dictionnary 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);
/************************************************************************
* *
* 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(xmlXPathCompExprPtr comp, int ch1, int ch2,
xmlXPathOp op, int value,
int value2, int value3, void *value4, void *value5) {
if (comp->nbStep >= comp->maxStep) {
xmlXPathStepOp *real;
comp->maxStep *= 2;
real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
comp->maxStep * sizeof(xmlXPathStepOp));
if (real == NULL) {
comp->maxStep /= 2;
xmlXPathErrMemory(NULL, "adding step\n");
return(-1);
}
comp->steps = real;
}
comp->last = comp->nbStep;
comp->steps[comp->nbStep].rewriteType = 0;
comp->steps[comp->nbStep].ch1 = ch1;
comp->steps[comp->nbStep].ch2 = ch2;
comp->steps[comp->nbStep].op = op;
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->comp, (op1), (op2), \
(op), (val), (val2), (val3), (val4), (val5))
#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
(op), (val), (val2), (val3), (val4), (val5))
#define PUSH_LEAVE_EXPR(op, val, val2) \
xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
#define PUSH_UNARY_EXPR(op, ch, val, val2) \
xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
xmlXPathCompExprAdd(ctxt->comp, (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 && xmlXPathGetSign(cur->floatval) != 0) {
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_RESET:
fprintf(output, "RESET"); 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);
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 dependant 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 nodset-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))
{
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);
#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 *
* *
************************************************************************/
/**
* 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);
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
*
* returns the number of items on the value stack
*/
int
valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
{
if ((ctxt == NULL) || (value == NULL)) return(-1);
if (ctxt->valueNr >= ctxt->valueMax) {
xmlXPathObjectPtr *tmp;
tmp = (xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
2 * ctxt->valueMax *
sizeof(ctxt->valueTab[0]));
if (tmp == NULL) {
xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
return (0);
}
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 && xmlXPathGetSign(number) != 0) {
snprintf(buffer, buffersize, "0");
} else if (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;
} else if (buffersize > 0) {
ptr--;
*ptr = 0;
}
} else {
/*
For the dimension of work,
DBL_DIG is number of significant digits
EXPONENT is only needed for "scientific notation"
3 is sign, decimal point, and terminating zero
LOWER_DOUBLE_EXP is max number of leading zeroes in fraction
Note that this dimension is slightly (a few characters)
larger than actually necessary.
*/
char work[DBL_DIG + EXPONENT_DIGITS + 3 + LOWER_DOUBLE_EXP];
int integer_place, fraction_place;
char *ptr;
char *after_fraction;
double absolute_value;
int size;
absolute_value = fabs(number);
/*
* First choose format - scientific or regular floating point.
* In either case, result is in work, and after_fraction points
* just past the fractional part.
*/
if ( ((absolute_value > UPPER_DOUBLE) ||
(absolute_value < LOWER_DOUBLE)) &&
(absolute_value != 0.0) ) {
/* Use scientific notation */
integer_place = DBL_DIG + EXPONENT_DIGITS + 1;
fraction_place = DBL_DIG - 1;
size = snprintf(work, sizeof(work),"%*.*e",
integer_place, fraction_place, number);
while ((size > 0) && (work[size] != 'e')) size--;
}
else {
/* Use regular notation */
if (absolute_value > 0.0) {
integer_place = (int)log10(absolute_value);
if (integer_place > 0)
fraction_place = DBL_DIG - integer_place - 1;
else
fraction_place = DBL_DIG - integer_place;
} else {
fraction_place = 1;
}
size = snprintf(work, sizeof(work), "%0.*f",
fraction_place, number);
}
/* Remove fractional trailing zeroes */
after_fraction = work + size;
ptr = after_fraction;
while (*(--ptr) == '0')
;
if (*ptr != '.')
ptr++;
while ((*ptr++ = *after_fraction++) != 0);
/* Finally copy result back to caller */
size = strlen(work) + 1;
if (size > buffersize) {
work[buffersize - 1] = 0;
size = buffersize;
}
memmove(buffer, work, size);
}
break;
}
}
/************************************************************************
* *
* Routines to handle NodeSets *
* *
************************************************************************/
/**
* xmlXPathOrderDocElems:
* @doc: an input document
*
* Call this routine to speed up XPath computation on static documents.
* This stamps all the element nodes with the document order
* Like for line information, the order is kept in the element->content
* field, the value stored is actually - the node number (starting at -1)
* to be able to differentiate from line numbers.
*
* Returns the number of elements found in the document or -1 in case
* of error.
*/
long
xmlXPathOrderDocElems(xmlDocPtr doc) {
long count = 0;
xmlNodePtr cur;
if (doc == NULL)
return(-1);
cur = doc->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
cur->content = (void *) (-(++count));
if (cur->children != NULL) {
cur = cur->children;
continue;
}
}
if (cur->next != NULL) {
cur = cur->next;
continue;
}
do {
cur = cur->parent;
if (cur == NULL)
break;
if (cur == (xmlNodePtr) doc) {
cur = NULL;
break;
}
if (cur->next != NULL) {
cur = cur->next;
break;
}
} while (cur != NULL);
}
return(count);
}
/**
* xmlXPathCmpNodes:
* @node1: the first node
* @node2: the second node
*
* Compare two nodes w.r.t document order
*
* Returns -2 in case of error 1 if first point < second point, 0 if
* it's the same node, -1 otherwise
*/
int
xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
int depth1, depth2;
int attr1 = 0, attr2 = 0;
xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
xmlNodePtr cur, root;
if ((node1 == NULL) || (node2 == NULL))
return(-2);
/*
* a couple of optimizations which will avoid computations in most cases
*/
if (node1 == node2) /* trivial case */
return(0);
if (node1->type == XML_ATTRIBUTE_NODE) {
attr1 = 1;
attrNode1 = node1;
node1 = node1->parent;
}
if (node2->type == XML_ATTRIBUTE_NODE) {
attr2 = 1;
attrNode2 = node2;
node2 = node2->parent;
}
if (node1 == node2) {
if (attr1 == attr2) {
/* not required, but we keep attributes in order */
if (attr1 != 0) {
cur = attrNode2->prev;
while (cur != NULL) {
if (cur == attrNode1)
return (1);
cur = cur->prev;
}
return (-1);
}
return(0);
}
if (attr2 == 1)
return(1);
return(-1);
}
if ((node1->type == XML_NAMESPACE_DECL) ||
(node2->type == XML_NAMESPACE_DECL))
return(1);
if (node1 == node2->prev)
return(1);
if (node1 == node2->next)
return(-1);
/*
* Speedup using document order if availble.
*/
if ((node1->type == XML_ELEMENT_NODE) &&
(node2->type == XML_ELEMENT_NODE) &&
(0 > (long) node1->content) &&
(0 > (long) node2->content) &&
(node1->doc == node2->doc)) {
long l1, l2;
l1 = -((long) node1->content);
l2 = -((long) node2->content);
if (l1 < l2)
return(1);
if (l1 > l2)
return(-1);
}
/*
* compute depth to root
*/
for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
if (cur == node1)
return(1);
depth2++;
}
root = cur;
for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
if (cur == node2)
return(-1);
depth1++;
}
/*
* Distinct document (or distinct entities :-( ) case.
*/
if (root != cur) {
return(-2);
}
/*
* get the nearest common ancestor.
*/
while (depth1 > depth2) {
depth1--;
node1 = node1->parent;
}
while (depth2 > depth1) {
depth2--;
node2 = node2->parent;
}
while (node1->parent != node2->parent) {
node1 = node1->parent;
node2 = node2->parent;
/* should not happen but just in case ... */
if ((node1 == NULL) || (node2 == NULL))
return(-2);
}
/*
* Find who's first.
*/
if (node1 == node2->prev)
return(1);
if (node1 == node2->next)
return(-1);
/*
* Speedup using document order if availble.
*/
if ((node1->type == XML_ELEMENT_NODE) &&
(node2->type == XML_ELEMENT_NODE) &&
(0 > (long) node1->content) &&
(0 > (long) node2->content) &&
(node1->doc == node2->doc)) {
long l1, l2;
l1 = -((long) node1->content);
l2 = -((long) node2->content);
if (l1 < l2)
return(1);
if (l1 > l2)
return(-1);
}
for (cur = node1->next;cur != NULL;cur = cur->next)
if (cur == node2)
return(1);
return(-1); /* assume there is no sibling list corruption */
}
#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;
long l1, l2;
if ((node1 == NULL) || (node2 == NULL))
return(-2);
if (node1 == node2)
return(0);
/*
* a couple of optimizations which will avoid computations in most cases
*/
switch (node1->type) {
case XML_ELEMENT_NODE:
if (node2->type == XML_ELEMENT_NODE) {
if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */
(0 > (long) node2->content) &&
(node1->doc == node2->doc))
{
l1 = -((long) node1->content);
l2 = -((long) node2->content);
if (l1 < l2)
return(1);
if (l1 > l2)
return(-1);
} else
goto turtle_comparison;
}
break;
case XML_ATTRIBUTE_NODE:
precedence1 = 1; /* element is owner */
miscNode1 = node1;
node1 = node1->parent;
misc = 1;
break;
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_COMMENT_NODE:
case XML_PI_NODE: {
miscNode1 = node1;
/*
* Find nearest element node.
*/
if (node1->prev != NULL) {
do {
node1 = node1->prev;
if (node1->type == XML_ELEMENT_NODE) {
precedence1 = 3; /* element in prev-sibl axis */
break;
}
if (node1->prev == NULL) {
precedence1 = 2; /* element is parent */
/*
* URGENT TODO: Are there any cases, where the
* parent of such a node is not an element node?
*/
node1 = node1->parent;
break;
}
} while (1);
} else {
precedence1 = 2; /* element is parent */
node1 = node1->parent;
}
if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE) ||
(0 <= (long) node1->content)) {
/*
* Fallback for whatever case.
*/
node1 = miscNode1;
precedence1 = 0;
} else
misc = 1;
}
break;
case XML_NAMESPACE_DECL:
/*
* TODO: why do we return 1 for namespace nodes?
*/
return(1);
default:
break;
}
switch (node2->type) {
case XML_ELEMENT_NODE:
break;
case XML_ATTRIBUTE_NODE:
precedence2 = 1; /* element is owner */
miscNode2 = node2;
node2 = node2->parent;
misc = 1;
break;
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_COMMENT_NODE:
case XML_PI_NODE: {
miscNode2 = node2;
if (node2->prev != NULL) {
do {
node2 = node2->prev;
if (node2->type == XML_ELEMENT_NODE) {
precedence2 = 3; /* element in prev-sibl axis */
break;
}
if (node2->prev == NULL) {
precedence2 = 2; /* element is parent */
node2 = node2->parent;
break;
}
} while (1);
} else {
precedence2 = 2; /* element is parent */
node2 = node2->parent;
}
if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
(0 <= (long) node1->content))
{
node2 = miscNode2;
precedence2 = 0;
} else
misc = 1;
}
break;
case XML_NAMESPACE_DECL:
return(1);
default:
break;
}
if (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(