blob: 51ba5994ed0d6c0432c1a3d83e49f1caddf929e5 [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@w3.org
*
* 14 Nov 2000 ht - truncated declaration of xmlXPathEvalRelativeLocationPath
* for VMS
*/
#include "libxml.h"
#ifdef LIBXML_XPATH_ENABLED
#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_IEEEFP_H
#include <ieeefp.h>
#endif
#ifdef HAVE_NAN_H
#include <nan.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.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>
/* #define DEBUG */
/* #define DEBUG_STEP */
/* #define DEBUG_EXPR */
void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
double xmlXPathStringEvalNumber(const xmlChar *str);
/************************************************************************
* *
* Floating point stuff *
* *
************************************************************************/
/*
* The lack of portability of this section of the libc is annoying !
*/
double xmlXPathNAN = 0;
double xmlXPathPINF = 1;
double xmlXPathNINF = -1;
#ifndef isinf
#ifndef HAVE_ISINF
#if HAVE_FPCLASS
int isinf(double d) {
fpclass_t type = fpclass(d);
switch (type) {
case FP_NINF:
return(-1);
case FP_PINF:
return(1);
}
return(0);
}
#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
#if HAVE_FP_CLASS_H
#include <fp_class.h>
#endif
int isinf(double d) {
#if HAVE_FP_CLASS
int fpclass = fp_class(d);
#else
int fpclass = fp_class_d(d);
#endif
if (fpclass == FP_POS_INF)
return(1);
if (fpclass == FP_NEG_INF)
return(-1);
return(0);
}
#elif defined(HAVE_CLASS)
int isinf(double d) {
int fpclass = class(d);
if (fpclass == FP_PLUS_INF)
return(1);
if (fpclass == FP_MINUS_INF)
return(-1);
return(0);
}
#elif defined(finite) || defined(HAVE_FINITE)
int isinf(double x) { return !finite(x) && x==x; }
#elif defined(HUGE_VAL)
int isinf(double x)
{
if (x == HUGE_VAL)
return(1);
if (x == -HUGE_VAL)
return(-1);
return(0);
}
#endif
#endif /* ! HAVE_ISINF */
#endif /* ! defined(isinf) */
#ifndef isnan
#ifndef HAVE_ISNAN
#ifdef HAVE_ISNAND
#define isnan(f) isnand(f)
#endif /* HAVE_iSNAND */
#endif /* ! HAVE_iSNAN */
#endif /* ! defined(isnan) */
/**
* xmlXPathInit:
*
* Initialize the XPath environment
*/
void
xmlXPathInit(void) {
static int initialized = 0;
if (initialized) return;
xmlXPathNAN = 0.0 / 0.0;
xmlXPathPINF = 1 / 0.0;
xmlXPathNINF = -1 / 0.0;
initialized = 1;
}
/************************************************************************
* *
* 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,
XPATH_OP_COLLECT,
XPATH_OP_VALUE,
XPATH_OP_VARIABLE,
XPATH_OP_FUNCTION,
XPATH_OP_ARG,
XPATH_OP_PREDICATE,
XPATH_OP_FILTER,
XPATH_OP_SORT
#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;
int ch1;
int ch2;
int value;
int value2;
int value3;
void *value4;
void *value5;
};
struct _xmlXPathCompExpr {
int nbStep;
int maxStep;
xmlXPathStepOp *steps; /* ops for computation */
int last;
};
/************************************************************************
* *
* 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) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewCompExpr : malloc failed\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) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewCompExpr : malloc failed\n");
xmlFree(cur);
return(NULL);
}
memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
cur->last = -1;
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;
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);
}
if (comp->steps != NULL) {
xmlFree(comp->steps);
}
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 an 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;
xmlGenericError(xmlGenericErrorContext,
"xmlXPathCompExprAdd : realloc failed\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;
comp->steps[comp->nbStep].value4 = value4;
comp->steps[comp->nbStep].value5 = value5;
return(comp->nbStep++);
}
#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)
/************************************************************************
* *
* Debugging related functions *
* *
************************************************************************/
#define TODO \
xmlGenericError(xmlGenericErrorContext, \
"Unimplemented block at %s:%d\n", \
__FILE__, __LINE__);
#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, shift);
fprintf(output, "Node is NULL !\n");
return;
}
if ((cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE)) {
fprintf(output, 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, 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, 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, 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, shift);
fprintf(output, "Value Tree is NULL !\n");
return;
}
fprintf(output, shift);
fprintf(output, "%d", i + 1);
xmlXPathDebugDumpNodeList(output, cur->nodeTab[0]->children, depth + 1);
}
#if defined(LIBXML_XPTR_ENABLED)
void xmlXPathDebugDumpObject(FILE *output, xmlXPathObjectPtr cur, int depth);
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, shift);
fprintf(output, "LocationSet is NULL !\n");
return;
}
for (i = 0;i < cur->locNr;i++) {
fprintf(output, shift);
fprintf(output, "%d : ", i + 1);
xmlXPathDebugDumpObject(output, cur->locTab[i], depth + 1);
}
}
#endif
/**
* 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];
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, 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:
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, 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, 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, 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, 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 = op->value;
xmlXPathTestVal test = op->value2;
xmlXPathTypeVal type = 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", 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);
}
void
xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
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, shift);
if (comp == NULL) {
fprintf(output, "Compiled Expression is NULL\n");
return;
}
fprintf(output, "Compiled Expression : %d elements\n",
comp->nbStep);
i = comp->last;
xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
}
#endif
/************************************************************************
* *
* Parser stacks related functions and macros *
* *
************************************************************************/
/*
* Generic function for accessing stacks in the Parser Context
*/
#define PUSH_AND_POP(type, name) \
extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
if (ctxt->name##Nr >= ctxt->name##Max) { \
ctxt->name##Max *= 2; \
ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
if (ctxt->name##Tab == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"realloc failed !\n"); \
return(0); \
} \
} \
ctxt->name##Tab[ctxt->name##Nr] = value; \
ctxt->name = value; \
return(ctxt->name##Nr++); \
} \
extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
type ret; \
if (ctxt->name##Nr <= 0) return(0); \
ctxt->name##Nr--; \
if (ctxt->name##Nr > 0) \
ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
else \
ctxt->name = NULL; \
ret = ctxt->name##Tab[ctxt->name##Nr]; \
ctxt->name##Tab[ctxt->name##Nr] = 0; \
return(ret); \
} \
PUSH_AND_POP(xmlXPathObjectPtr, value)
/*
* Macros for accessing the content. Those should be used only by the parser,
* and not exported.
*
* Dirty macros, i.e. one need to make assumption on the context to use them
*
* CUR_PTR return the current pointer to the 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 SKIP_BLANKS \
while (IS_BLANK(*(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 INTEGER_DIGITS DBL_DIG
#define FRACTION_DIGITS (DBL_DIG + 1)
#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 (isinf(number)) {
case 1:
if (buffersize > (int)sizeof("+Infinity"))
sprintf(buffer, "+Infinity");
break;
case -1:
if (buffersize > (int)sizeof("-Infinity"))
sprintf(buffer, "-Infinity");
break;
default:
if (isnan(number)) {
if (buffersize > (int)sizeof("NaN"))
sprintf(buffer, "NaN");
} else {
/* 3 is sign, decimal point, and terminating zero */
char work[DBL_DIG + EXPONENT_DIGITS + 3];
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;
snprintf(work, sizeof(work),"%*.*e",
integer_place, fraction_place, number);
after_fraction = strchr(work + DBL_DIG, 'e');
}
else {
/* Use regular notation */
integer_place = 1 + (int)log10(absolute_value);
fraction_place = (integer_place > 0)
? DBL_DIG - integer_place
: DBL_DIG;
size = snprintf(work, sizeof(work), "%0.*f",
fraction_place, number);
after_fraction = work + size;
}
/* Remove fractional trailing zeroes */
ptr = after_fraction;
while (*(--ptr) == '0')
;
if (*ptr != '.')
ptr++;
strcpy(ptr, after_fraction);
/* Finally copy result back to caller */
size = strlen(work) + 1;
if (size > buffersize) {
work[buffersize - 1] = 0;
size = buffersize;
}
memcpy(buffer, work, size);
}
break;
}
}
/************************************************************************
* *
* Error handling routines *
* *
************************************************************************/
const char *xmlXPathErrorMessages[] = {
"Ok",
"Number encoding",
"Unfinished litteral",
"Start of litteral",
"Expected $ for variable reference",
"Undefined variable",
"Invalid predicate",
"Invalid expression",
"Missing closing curly brace",
"Unregistered function",
"Invalid operand",
"Invalid type",
"Invalid number of arguments",
"Invalid context size",
"Invalid context position",
"Memory allocation error",
"Syntax error",
"Resource error",
"Sub resource error",
"Undefined namespace prefix"
};
/**
* xmlXPathError:
* @ctxt: the XPath Parser context
* @file: the file name
* @line: the line number
* @no: the error number
*
* Create a new xmlNodeSetPtr of type double and of value @val
*
* Returns the newly created object.
*/
void
xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
int line, int no) {
int n;
const xmlChar *cur;
const xmlChar *base;
xmlGenericError(xmlGenericErrorContext,
"Error %s:%d: %s\n", file, line,
xmlXPathErrorMessages[no]);
cur = ctxt->cur;
base = ctxt->base;
if ((cur == NULL) || (base == NULL))
return;
while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
cur--;
}
n = 0;
while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
cur--;
if ((*cur == '\n') || (*cur == '\r')) cur++;
base = cur;
n = 0;
while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
xmlGenericError(xmlGenericErrorContext, "%c", (unsigned char) *cur++);
n++;
}
xmlGenericError(xmlGenericErrorContext, "\n");
cur = ctxt->cur;
while ((*cur == '\n') || (*cur == '\r'))
cur--;
n = 0;
while ((cur != base) && (n++ < 80)) {
xmlGenericError(xmlGenericErrorContext, " ");
base++;
}
xmlGenericError(xmlGenericErrorContext,"^\n");
}
/************************************************************************
* *
* Routines to handle NodeSets *
* *
************************************************************************/
/**
* 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
* that's the same node, -1 otherwise
*/
int
xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
int depth1, depth2;
xmlNodePtr cur, root;
if ((node1 == NULL) || (node2 == NULL))
return(-2);
/*
* a couple of optimizations which will avoid computations in most cases
*/
if (node1 == node2)
return(0);
if (node1 == node2->prev)
return(1);
if (node1 == node2->next)
return(-1);
/*
* compute depth to root
*/
for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
if (cur == node1)
return(1);
depth2++;
}
root = cur;
for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
if (cur == node2)
return(-1);
depth1++;
}
/*
* Distinct document (or distinct entities :-( ) case.
*/
if (root != cur) {
return(-2);
}
/*
* get the nearest common ancestor.
*/
while (depth1 > depth2) {
depth1--;
node1 = node1->parent;
}
while (depth2 > depth1) {
depth2--;
node2 = node2->parent;
}
while (node1->parent != node2->parent) {
node1 = node1->parent;
node2 = node2->parent;
/* should not happen but just in case ... */
if ((node1 == NULL) || (node2 == NULL))
return(-2);
}
/*
* Find who's first.
*/
if (node1 == node2->next)
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 */
}
/**
* xmlXPathNodeSetSort:
* @set: the node set
*
* Sort the node set in document order
*/
void
xmlXPathNodeSetSort(xmlNodeSetPtr set) {
int i, j, incr, len;
xmlNodePtr tmp;
if (set == NULL)
return;
/* Use Shell's sort to sort the node-set */
len = set->nodeNr;
for (incr = len / 2; incr > 0; incr /= 2) {
for (i = incr; i < len; i++) {
j = i - incr;
while (j >= 0) {
if (xmlXPathCmpNodes(set->nodeTab[j],
set->nodeTab[j + incr]) == -1) {
tmp = set->nodeTab[j];
set->nodeTab[j] = set->nodeTab[j + incr];
set->nodeTab[j + incr] = tmp;
j -= incr;
} else
break;
}
}
}
}
#define XML_NODESET_DEFAULT 10
/**
* xmlXPathNodeSetCreate:
* @val: an initial xmlNodePtr, or NULL
*
* Create a new xmlNodeSetPtr of type double and of value @val
*
* Returns the newly created object.
*/
xmlNodeSetPtr
xmlXPathNodeSetCreate(xmlNodePtr val) {
xmlNodeSetPtr ret;
ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewNodeSet: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
if (val != NULL) {
ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (ret->nodeTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewNodeSet: out of memory\n");
return(NULL);
}
memset(ret->nodeTab, 0 ,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
ret->nodeMax = XML_NODESET_DEFAULT;
ret->nodeTab[ret->nodeNr++] = val;
}
return(ret);
}
/**
* xmlXPathNodeSetAdd:
* @cur: the initial node set
* @val: a new xmlNodePtr
*
* add a new xmlNodePtr ot an existing NodeSet
*/
void
xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
int i;
if (val == NULL) return;
/*
* check against doublons
*/
for (i = 0;i < cur->nodeNr;i++)
if (cur->nodeTab[i] == val) return;
/*
* grow the nodeTab if needed
*/
if (cur->nodeMax == 0) {
cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (cur->nodeTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetAdd: out of memory\n");
return;
}
memset(cur->nodeTab, 0 ,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
cur->nodeMax = XML_NODESET_DEFAULT;
} else if (cur->nodeNr == cur->nodeMax) {
xmlNodePtr *temp;
cur->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetAdd: out of memory\n");
return;
}
cur->nodeTab = temp;
}
cur->nodeTab[cur->nodeNr++] = val;
}
/**
* xmlXPathNodeSetAddUnique:
* @cur: the initial node set
* @val: a new xmlNodePtr
*
* add a new xmlNodePtr ot an existing NodeSet, optimized version
* when we are sure the node is not already in the set.
*/
void
xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
if (val == NULL) return;
/*
* grow the nodeTab if needed
*/
if (cur->nodeMax == 0) {
cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (cur->nodeTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetAddUnique: out of memory\n");
return;
}
memset(cur->nodeTab, 0 ,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
cur->nodeMax = XML_NODESET_DEFAULT;
} else if (cur->nodeNr == cur->nodeMax) {
xmlNodePtr *temp;
cur->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetAddUnique: out of memory\n");
return;
}
cur->nodeTab = temp;
}
cur->nodeTab[cur->nodeNr++] = val;
}
/**
* xmlXPathNodeSetMerge:
* @val1: the first NodeSet or NULL
* @val2: the second NodeSet
*
* Merges two nodesets, all nodes from @val2 are added to @val1
* if @val1 is NULL, a new set is created and copied from @val2
*
* Returns val1 once extended or NULL in case of error.
*/
xmlNodeSetPtr
xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
int i, j, initNr, skip;
if (val2 == NULL) return(val1);
if (val1 == NULL) {
val1 = xmlXPathNodeSetCreate(NULL);
}
initNr = val1->nodeNr;
for (i = 0;i < val2->nodeNr;i++) {
/*
* check against doublons
*/
skip = 0;
for (j = 0; j < initNr; j++) {
if (val1->nodeTab[j] == val2->nodeTab[i]) {
skip = 1;
break;
}
}
if (skip)
continue;
/*
* grow the nodeTab if needed
*/
if (val1->nodeMax == 0) {
val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (val1->nodeTab == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetMerge: out of memory\n");
return(NULL);
}
memset(val1->nodeTab, 0 ,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
val1->nodeMax = XML_NODESET_DEFAULT;
} else if (val1->nodeNr == val1->nodeMax) {
xmlNodePtr *temp;
val1->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetMerge: out of memory\n");
return(NULL);
}
val1->nodeTab = temp;
}
val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
}
return(val1);
}
/**
* xmlXPathNodeSetDel:
* @cur: the initial node set
* @val: an xmlNodePtr
*
* Removes an xmlNodePtr from an existing NodeSet
*/
void
xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
int i;
if (cur == NULL) return;
if (val == NULL) return;
/*
* check against doublons
*/
for (i = 0;i < cur->nodeNr;i++)
if (cur->nodeTab[i] == val) break;
if (i >= cur->nodeNr) {
#ifdef DEBUG
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
val->name);
#endif
return;
}
cur->nodeNr--;
for (;i < cur->nodeNr;i++)
cur->nodeTab[i] = cur->nodeTab[i + 1];
cur->nodeTab[cur->nodeNr] = NULL;
}
/**
* xmlXPathNodeSetRemove:
* @cur: the initial node set
* @val: the index to remove
*
* Removes an entry from an existing NodeSet list.
*/
void
xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
if (cur == NULL) return;
if (val >= cur->nodeNr) return;
cur->nodeNr--;
for (;val < cur->nodeNr;val++)
cur->nodeTab[val] = cur->nodeTab[val + 1];
cur->nodeTab[cur->nodeNr] = NULL;
}
/**
* xmlXPathFreeNodeSet:
* @obj: the xmlNodeSetPtr to free
*
* Free the NodeSet compound (not the actual nodes !).
*/
void
xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
if (obj == NULL) return;
if (obj->nodeTab != NULL) {
xmlFree(obj->nodeTab);
}
xmlFree(obj);
}
/**
* xmlXPathFreeValueTree:
* @obj: the xmlNodeSetPtr to free
*
* Free the NodeSet compound and the actual tree, this is different
* from xmlXPathFreeNodeSet()
*/
static void
xmlXPathFreeValueTree(xmlNodeSetPtr obj) {
int i;
if (obj == NULL) return;
for (i = 0;i < obj->nodeNr;i++)
if (obj->nodeTab[i] != NULL)
xmlFreeNodeList(obj->nodeTab[i]);
if (obj->nodeTab != NULL) {
xmlFree(obj->nodeTab);
}
xmlFree(obj);
}
#if defined(DEBUG) || defined(DEBUG_STEP)
/**
* xmlGenericErrorContextNodeSet:
* @output: a FILE * for the output
* @obj: the xmlNodeSetPtr to free
*
* Quick display of a NodeSet
*/
void
xmlGenericErrorContextNodeSet(FILE *output, xmlNodeSetPtr obj) {
int i;
if (output == NULL) output = xmlGenericErrorContext;
if (obj == NULL) {
fprintf(output, "NodeSet == NULL !\n");
return;
}
if (obj->nodeNr == 0) {
fprintf(output, "NodeSet is empty\n");
return;
}
if (obj->nodeTab == NULL) {
fprintf(output, " nodeTab == NULL !\n");
return;
}
for (i = 0; i < obj->nodeNr; i++) {
if (obj->nodeTab[i] == NULL) {
fprintf(output, " NULL !\n");
return;
}
if ((obj->nodeTab[i]->type == XML_DOCUMENT_NODE) ||
(obj->nodeTab[i]->type == XML_HTML_DOCUMENT_NODE))
fprintf(output, " /");
else if (obj->nodeTab[i]->name == NULL)
fprintf(output, " noname!");
else fprintf(output, " %s", obj->nodeTab[i]->name);
}
fprintf(output, "\n");
}
#endif
/**
* xmlXPathNewNodeSet:
* @val: the NodePtr value
*
* Create a new xmlXPathObjectPtr of type NodeSet and initialize
* it with the single Node @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewNodeSet(xmlNodePtr val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewNodeSet: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_NODESET;
ret->boolval = 0;
ret->nodesetval = xmlXPathNodeSetCreate(val);
return(ret);
}
/**
* xmlXPathNewValueTree:
* @val: the NodePtr value
*
* Create a new xmlXPathObjectPtr of type Value Tree (XSLT) and initialize
* it with the tree root @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewValueTree(xmlNodePtr val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewNodeSet: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_XSLT_TREE;
ret->nodesetval = xmlXPathNodeSetCreate(val);
return(ret);
}
/**
* xmlXPathNewNodeSetList:
* @val: an existing NodeSet
*
* Create a new xmlXPathObjectPtr of type NodeSet and initialize
* it with the Nodeset @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewNodeSetList(xmlNodeSetPtr val) {
xmlXPathObjectPtr ret;
int i;
if (val == NULL)
ret = NULL;
else if (val->nodeTab == NULL)
ret = xmlXPathNewNodeSet(NULL);
else
{
ret = xmlXPathNewNodeSet(val->nodeTab[0]);
for (i = 1; i < val->nodeNr; ++i)
xmlXPathNodeSetAddUnique(ret->nodesetval, val->nodeTab[i]);
}
return(ret);
}
/**
* xmlXPathWrapNodeSet:
* @val: the NodePtr value
*
* Wrap the Nodeset @val in a new xmlXPathObjectPtr
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathWrapNodeSet: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_NODESET;
ret->nodesetval = val;
return(ret);
}
/**
* xmlXPathFreeNodeSetList:
* @obj: an existing NodeSetList object
*
* Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
* the list contrary to xmlXPathFreeObject().
*/
void
xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
if (obj == NULL) return;
xmlFree(obj);
}
/************************************************************************
* *
* Routines to handle extra functions *
* *
************************************************************************/
/**
* xmlXPathRegisterFunc:
* @ctxt: the XPath context
* @name: the function name
* @f: the function implementation or NULL
*
* Register a new function. If @f is NULL it unregisters the function
*
* Returns 0 in case of success, -1 in case of error
*/
int
xmlXPathRegisterFunc(xmlXPathContextPtr ctxt, const xmlChar *name,
xmlXPathFunction f) {
return(xmlXPathRegisterFuncNS(ctxt, name, NULL, f));
}
/**
* xmlXPathRegisterFuncNS:
* @ctxt: the XPath context
* @name: the function name
* @ns_uri: the function namespace URI
* @f: the function implementation or NULL
*
* Register a new function. If @f is NULL it unregisters the function
*
* Returns 0 in case of success, -1 in case of error
*/
int
xmlXPathRegisterFuncNS(xmlXPathContextPtr ctxt, const xmlChar *name,
const xmlChar *ns_uri, xmlXPathFunction f) {
if (ctxt == NULL)
return(-1);
if (name == NULL)
return(-1);
if (ctxt->funcHash == NULL)
ctxt->funcHash = xmlHashCreate(0);
if (ctxt->funcHash == NULL)
return(-1);
return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
}
/**
* xmlXPathFunctionLookup:
* @ctxt: the XPath context
* @name: the function name
*
* Search in the Function array of the context for the given
* function.
*
* Returns the xmlXPathFunction or NULL if not found
*/
xmlXPathFunction
xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
}
/**
* xmlXPathFunctionLookupNS:
* @ctxt: the XPath context
* @name: the function name
* @ns_uri: the function namespace URI
*
* Search in the Function array of the context for the given
* function.
*
* Returns the xmlXPathFunction or NULL if not found
*/
xmlXPathFunction
xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
const xmlChar *ns_uri) {
if (ctxt == NULL)
return(NULL);
if (ctxt->funcHash == NULL)
return(NULL);
if (name == NULL)
return(NULL);
return((xmlXPathFunction) xmlHashLookup2(ctxt->funcHash, name, ns_uri));
}
/**
* xmlXPathRegisteredFuncsCleanup:
* @ctxt: the XPath context
*
* Cleanup the XPath context data associated to registered functions
*/
void
xmlXPathRegisteredFuncsCleanup(xmlXPathContextPtr ctxt) {
if (ctxt == NULL)
return;
xmlHashFree(ctxt->funcHash, NULL);
ctxt->funcHash = NULL;
}
/************************************************************************
* *
* Routines to handle Variable *
* *
************************************************************************/
/**
* xmlXPathRegisterVariable:
* @ctxt: the XPath context
* @name: the variable name
* @value: the variable value or NULL
*
* Register a new variable value. If @value is NULL it unregisters
* the variable
*
* Returns 0 in case of success, -1 in case of error
*/
int
xmlXPathRegisterVariable(xmlXPathContextPtr ctxt, const xmlChar *name,
xmlXPathObjectPtr value) {
return(xmlXPathRegisterVariableNS(ctxt, name, NULL, value));
}
/**
* xmlXPathRegisterVariableNS:
* @ctxt: the XPath context
* @name: the variable name
* @ns_uri: the variable namespace URI
* @value: the variable value or NULL
*
* Register a new variable value. If @value is NULL it unregisters
* the variable
*
* Returns 0 in case of success, -1 in case of error
*/
int
xmlXPathRegisterVariableNS(xmlXPathContextPtr ctxt, const xmlChar *name,
const xmlChar *ns_uri,
xmlXPathObjectPtr value) {
if (ctxt == NULL)
return(-1);
if (name == NULL)
return(-1);
if (ctxt->varHash == NULL)
ctxt->varHash = xmlHashCreate(0);
if (ctxt->varHash == NULL)
return(-1);
return(xmlHashUpdateEntry2(ctxt->varHash, name, ns_uri,
(void *) value,
(xmlHashDeallocator)xmlXPathFreeObject));
}
/**
* xmlXPathRegisterVariableLookup:
* @ctxt: the XPath context
* @f: the lookup function
* @data: the lookup data
*
* register an external mechanism to do variable lookup
*/
void
xmlXPathRegisterVariableLookup(xmlXPathContextPtr ctxt,
xmlXPathVariableLookupFunc f, void *data) {
if (ctxt == NULL)
return;
ctxt->varLookupFunc = (void *) f;
ctxt->varLookupData = data;
}
/**
* xmlXPathVariableLookup:
* @ctxt: the XPath context
* @name: the variable name
*
* Search in the Variable array of the context for the given
* variable value.
*
* Returns the value or NULL if not found
*/
xmlXPathObjectPtr
xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
if (ctxt == NULL)
return(NULL);
if (ctxt->varLookupFunc != NULL) {
xmlXPathObjectPtr ret;
ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
(ctxt->varLookupData, name, NULL);
if (ret != NULL) return(ret);
}
return(xmlXPathVariableLookupNS(ctxt, name, NULL));
}
/**
* xmlXPathVariableLookupNS:
* @ctxt: the XPath context
* @name: the variable name
* @ns_uri: the variable namespace URI
*
* Search in the Variable array of the context for the given
* variable value.
*
* Returns the value or NULL if not found
*/
xmlXPathObjectPtr
xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
const xmlChar *ns_uri) {
if (ctxt == NULL)
return(NULL);
if (ctxt->varLookupFunc != NULL) {
xmlXPathObjectPtr ret;
ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
(ctxt->varLookupData, name, ns_uri);
if (ret != NULL) return(ret);
}
if (ctxt->varHash == NULL)
return(NULL);
if (name == NULL)
return(NULL);
return((xmlXPathObjectPtr) xmlHashLookup2(ctxt->varHash, name, ns_uri));
}
/**
* xmlXPathRegisteredVariablesCleanup:
* @ctxt: the XPath context
*
* Cleanup the XPath context data associated to registered variables
*/
void
xmlXPathRegisteredVariablesCleanup(xmlXPathContextPtr ctxt) {
if (ctxt == NULL)
return;
xmlHashFree(ctxt->varHash, NULL);
ctxt->varHash = NULL;
}
/**
* xmlXPathRegisterNs:
* @ctxt: the XPath context
* @prefix: the namespace prefix
* @ns_uri: the namespace name
*
* Register a new namespace. If @ns_uri is NULL it unregisters
* the namespace
*
* Returns 0 in case of success, -1 in case of error
*/
int
xmlXPathRegisterNs(xmlXPathContextPtr ctxt, const xmlChar *prefix,
const xmlChar *ns_uri) {
if (ctxt == NULL)
return(-1);
if (prefix == NULL)
return(-1);
if (ctxt->nsHash == NULL)
ctxt->nsHash = xmlHashCreate(10);
if (ctxt->nsHash == NULL)
return(-1);
return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) ns_uri,
(xmlHashDeallocator)xmlFree));
}
/**
* xmlXPathNsLookup:
* @ctxt: the XPath context
* @prefix: the namespace prefix value
*
* Search in the namespace declaration array of the context for the given
* namespace name associated to the given prefix
*
* Returns the value or NULL if not found
*/
const xmlChar *
xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
if (ctxt == NULL)
return(NULL);
if (prefix == NULL)
return(NULL);
#ifdef XML_XML_NAMESPACE
if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
return(XML_XML_NAMESPACE);
#endif
if (ctxt->nsHash == NULL)
return(NULL);
return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
}
/**
* xmlXPathRegisteredVariablesCleanup:
* @ctxt: the XPath context
*
* Cleanup the XPath context data associated to registered variables
*/
void
xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
if (ctxt == NULL)
return;
xmlHashFree(ctxt->nsHash, NULL);
ctxt->nsHash = NULL;
}
/************************************************************************
* *
* Routines to handle Values *
* *
************************************************************************/
/* Allocations are terrible, one need to optimize all this !!! */
/**
* xmlXPathNewFloat:
* @val: the double value
*
* Create a new xmlXPathObjectPtr of type double and of value @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewFloat(double val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewFloat: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_NUMBER;
ret->floatval = val;
return(ret);
}
/**
* xmlXPathNewBoolean:
* @val: the boolean value
*
* Create a new xmlXPathObjectPtr of type boolean and of value @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewBoolean(int val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewBoolean: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_BOOLEAN;
ret->boolval = (val != 0);
return(ret);
}
/**
* xmlXPathNewString:
* @val: the xmlChar * value
*
* Create a new xmlXPathObjectPtr of type string and of value @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewString(const xmlChar *val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewString: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_STRING;
if (val != NULL)
ret->stringval = xmlStrdup(val);
else
ret->stringval = xmlStrdup((const xmlChar *)"");
return(ret);
}
/**
* xmlXPathNewCString:
* @val: the char * value
*
* Create a new xmlXPathObjectPtr of type string and of value @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewCString(const char *val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewCString: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_STRING;
ret->stringval = xmlStrdup(BAD_CAST val);
return(ret);
}
/**
* xmlXPathObjectCopy:
* @val: the original object
*
* allocate a new copy of a given object
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathObjectCopy(xmlXPathObjectPtr val) {
xmlXPathObjectPtr ret;
if (val == NULL)
return(NULL);
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathObjectCopy: out of memory\n");
return(NULL);
}
memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
switch (val->type) {
case XPATH_BOOLEAN:
case XPATH_NUMBER:
case XPATH_POINT:
case XPATH_RANGE:
break;
case XPATH_STRING:
ret->stringval = xmlStrdup(val->stringval);
break;
case XPATH_XSLT_TREE:
if ((val->nodesetval != NULL) &&
(val->nodesetval->nodeTab != NULL))
ret->nodesetval = xmlXPathNodeSetCreate(
xmlCopyNode(val->nodesetval->nodeTab[0], 1));
else
ret->nodesetval = xmlXPathNodeSetCreate(NULL);
break;
case XPATH_NODESET:
ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
break;
case XPATH_LOCATIONSET:
#ifdef LIBXML_XPTR_ENABLED
{
xmlLocationSetPtr loc = val->user;
ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
break;
}
#endif
case XPATH_UNDEFINED:
case XPATH_USERS:
xmlGenericError(xmlGenericErrorContext,
"xmlXPathObjectCopy: unsupported type %d\n",
val->type);
break;
}
return(ret);
}
/**
* xmlXPathFreeObject:
* @obj: the object to free
*
* Free up an xmlXPathObjectPtr object.
*/
void
xmlXPathFreeObject(xmlXPathObjectPtr obj) {
if (obj == NULL) return;
if (obj->type == XPATH_NODESET) {
if (obj->boolval) {
obj->type = XPATH_XSLT_TREE;
if (obj->nodesetval != NULL)
xmlXPathFreeValueTree(obj->nodesetval);
} else {
if (obj->nodesetval != NULL)
xmlXPathFreeNodeSet(obj->nodesetval);
}
#ifdef LIBXML_XPTR_ENABLED
} else if (obj->type == XPATH_LOCATIONSET) {
if (obj->user != NULL)
xmlXPtrFreeLocationSet(obj->user);
#endif
} else if (obj->type == XPATH_STRING) {
if (obj->stringval != NULL)
xmlFree(obj->stringval);
} else if (obj->type == XPATH_XSLT_TREE) {
if (obj->nodesetval != NULL)
xmlXPathFreeValueTree(obj->nodesetval);
}
xmlFree(obj);
}
/************************************************************************
* *
* Routines to handle XPath contexts *
* *
************************************************************************/
/**
* xmlXPathNewContext:
* @doc: the XML document
*
* Create a new xmlXPathContext
*
* Returns the xmlXPathContext just allocated.
*/
xmlXPathContextPtr
xmlXPathNewContext(xmlDocPtr doc) {
xmlXPathContextPtr ret;
ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewContext: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
ret->doc = doc;
ret->node = NULL;
ret->varHash = NULL;
ret->nb_types = 0;
ret->max_types = 0;
ret->types = NULL;
ret->funcHash = xmlHashCreate(0);
ret->nb_axis = 0;
ret->max_axis = 0;
ret->axis = NULL;
ret->nsHash = NULL;
ret->user = NULL;
ret->contextSize = -1;
ret->proximityPosition = -1;
xmlXPathRegisterAllFunctions(ret);
return(ret);
}
/**
* xmlXPathFreeContext:
* @ctxt: the context to free
*
* Free up an xmlXPathContext
*/
void
xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
xmlXPathRegisteredNsCleanup(ctxt);
xmlXPathRegisteredFuncsCleanup(ctxt);
xmlXPathRegisteredVariablesCleanup(ctxt);
xmlFree(ctxt);
}
/************************************************************************
* *
* Routines to handle XPath parser contexts *
* *
************************************************************************/
#define CHECK_CTXT(ctxt) \
if (ctxt == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"%s:%d Internal error: ctxt == NULL\n", \
__FILE__, __LINE__); \
} \
#define CHECK_CONTEXT(ctxt) \
if (ctxt == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"%s:%d Internal error: no context\n", \
__FILE__, __LINE__); \
} \
else if (ctxt->doc == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"%s:%d Internal error: no document\n", \
__FILE__, __LINE__); \
} \
else if (ctxt->doc->children == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
"%s:%d Internal error: document without root\n", \
__FILE__, __LINE__); \
} \
/**
* xmlXPathNewParserContext:
* @str: the XPath expression
* @ctxt: the XPath context
*
* Create a new xmlXPathParserContext
*
* Returns the xmlXPathParserContext just allocated.
*/
xmlXPathParserContextPtr
xmlXPathNewParserContext(const xmlChar *str, xmlXPathContextPtr ctxt) {
xmlXPathParserContextPtr ret;
ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewParserContext: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
ret->cur = ret->base = str;
ret->context = ctxt;
ret->comp = xmlXPathNewCompExpr();
if (ret->comp == NULL) {
xmlFree(ret->valueTab);
xmlFree(ret);
return(NULL);
}
return(ret);
}
/**
* xmlXPathCompParserContext:
* @comp: the XPath compiled expression
* @ctxt: the XPath context
*
* Create a new xmlXPathParserContext when processing a compiled expression
*
* Returns the xmlXPathParserContext just allocated.
*/
static xmlXPathParserContextPtr
xmlXPathCompParserContext(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctxt) {
xmlXPathParserContextPtr ret;
ret = (xmlXPathParserContextPtr) xmlMalloc(sizeof(xmlXPathParserContext));
if (ret == NULL) {
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewParserContext: out of memory\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
/* Allocate the value stack */
ret->valueTab = (xmlXPathObjectPtr *)
xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
if (ret->valueTab == NULL) {
xmlFree(ret);
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNewParserContext: out of memory\n");
return(NULL);
}
ret->valueNr = 0;
ret->valueMax = 10;
ret->value = NULL;
ret->context = ctxt;
ret->comp = comp;
return(ret);
}
/**
* xmlXPathFreeParserContext:
* @ctxt: the context to free
*
* Free up an xmlXPathParserContext
*/
void
xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
if (ctxt->valueTab != NULL) {
xmlFree(ctxt->valueTab);
}
if (ctxt->comp)
xmlXPathFreeCompExpr(ctxt->comp);
xmlFree(ctxt);
}
/************************************************************************
* *
* The implicit core function library *
* *
************************************************************************/
/*
* Auto-pop and cast to a number
*/
void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
#define POP_FLOAT \
arg = valuePop(ctxt); \
if (arg == NULL) { \
XP_ERROR(XPATH_INVALID_OPERAND); \
} \
if (arg->type != XPATH_NUMBER) { \
valuePush(ctxt, arg); \
xmlXPathNumberFunction(ctxt, 1); \
arg = valuePop(ctxt); \
}
/**
* xmlXPathCompareNodeSetFloat:
* @ctxt: the XPath Parser context
* @inf: less than (1) or greater than (0)
* @strict: is the comparison strict
* @arg: the node set
* @f: the value
*
* Implement the compare operation between a nodeset and a number
* @ns < @val (1, 1, ...
* @ns <= @val (1, 0, ...
* @ns > @val (0, 1, ...
* @ns >= @val (0, 0, ...
*
* If one object to be compared is a node-set and the other is a number,
* then the comparison will be true if and only if there is a node in the
* node-set such that the result of performing the comparison on the number
* to be compared and on the result of converting the string-value of that
* node to a number using the number function is true.
*
* Returns 0 or 1 depending on the results of the test.
*/
static int
xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
xmlXPathObjectPtr arg, xmlXPathObjectPtr f) {
int i, ret = 0;
xmlNodeSetPtr ns;
xmlChar *str2;
if ((f == NULL) || (arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
xmlXPathFreeObject(arg);
xmlXPathFreeObject(f);
return(0);
}
ns = arg->nodesetval;
if (ns != NULL) {
for (i = 0;i < ns->nodeNr;i++) {
str2 = xmlNodeGetContent(ns->nodeTab[i]);
if (str2 != NULL) {
valuePush(ctxt,
xmlXPathNewString(str2));
xmlFree(str2);
xmlXPathNumberFunction(ctxt, 1);
valuePush(ctxt, xmlXPathObjectCopy(f));
ret = xmlXPathCompareValues(ctxt, inf, strict);
if (ret)
break;
}
}
}
xmlXPathFreeObject(arg);
xmlXPathFreeObject(f);
return(ret);
}
/**
* xmlXPathCompareNodeSetString:
* @ctxt: the XPath Parser context
* @inf: less than (1) or greater than (0)
* @strict: is the comparison strict
* @arg: the node set
* @s: the value
*
* Implement the compare operation between a nodeset and a string
* @ns < @val (1, 1, ...
* @ns <= @val (1, 0, ...
* @ns > @val (0, 1, ...
* @ns >= @val (0, 0, ...
*
* If one object to be compared is a node-set and the other is a string,
* then the comparison will be true if and only if there is a node in
* the node-set such that the result of performing the comparison on the
* string-value of the node and the other string is true.
*
* Returns 0 or 1 depending on the results of the test.
*/
static int
xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
xmlXPathObjectPtr arg, xmlXPathObjectPtr s) {
int i, ret = 0;
xmlNodeSetPtr ns;
xmlChar *str2;
if ((s == NULL) || (arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
xmlXPathFreeObject(arg);
xmlXPathFreeObject(s);
return(0);
}
ns = arg->nodesetval;
if (ns != NULL) {
for (i = 0;i < ns->nodeNr;i++) {
str2 = xmlNodeGetContent(ns->nodeTab[i]);
if (str2 != NULL) {
valuePush(ctxt,
xmlXPathNewString(str2));
xmlFree(str2);
valuePush(ctxt, xmlXPathObjectCopy(s));
ret = xmlXPathCompareValues(ctxt, inf, strict);
if (ret)
break;
}
}
}
xmlXPathFreeObject(arg);
xmlXPathFreeObject(s);
return(ret);
}
/**
* xmlXPathCompareNodeSets:
* @op: less than (-1), equal (0) or greater than (1)
* @strict: is the comparison strict
* @arg1: the fist node set object
* @arg2: the second node set object
*
* Implement the compare operation on nodesets:
*
* If both objects to be compared are node-sets, then the comparison
* will be true if and only if there is a node in the first node-set
* and a node in the second node-set such that the result of performing
* the comparison on the string-values of the two nodes is true.
* ....
* When neither object to be compared is a node-set and the operator
* is <=, <, >= or >, then the objects are compared by converting both
* objects to numbers and comparing the numbers according to IEEE 754.
* ....
* The number function converts its argument to a number as follows:
* - a string that consists of optional whitespace followed by an
* optional minus sign followed by a Number followed by whitespace
* is converted to the IEEE 754 number that is nearest (according
* to the IEEE 754 round-to-nearest rule) to the mathematical value
* represented by the string; any other string is converted to NaN
*
* Conclusion all nodes need to be converted first to their string value
* and then the comparison must be done when possible
*/
static int
xmlXPathCompareNodeSets(int inf, int strict,
xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
int i, j, init = 0;
double val1;
double *values2;
int ret = 0;
xmlChar *str;
xmlNodeSetPtr ns1;
xmlNodeSetPtr ns2;
if ((arg1 == NULL) ||
((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE))) {
xmlXPathFreeObject(arg2);
return(0);
}
if ((arg2 == NULL) ||
((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE))) {
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(0);
}
ns1 = arg1->nodesetval;
ns2 = arg2->nodesetval;
if ((ns1 == NULL) || (ns1->nodeNr <= 0)) {
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(0);
}
if ((ns2 == NULL) || (ns2->nodeNr <= 0)) {
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(0);
}
values2 = (double *) xmlMalloc(ns2->nodeNr * sizeof(double));
if (values2 == NULL) {
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(0);
}
for (i = 0;i < ns1->nodeNr;i++) {
str = xmlNodeGetContent(ns1->nodeTab[i]);
if (str == NULL)
continue;
val1 = xmlXPathStringEvalNumber(str);
xmlFree(str);
if (isnan(val1))
continue;
for (j = 0;j < ns2->nodeNr;j++) {
if (init == 0) {
str = xmlNodeGetContent(ns2->nodeTab[j]);
if (str == NULL) {
values2[j] = xmlXPathNAN;
} else {
values2[j] = xmlXPathStringEvalNumber(str);
xmlFree(str);
}
}
if (isnan(values2[j]))
continue;
if (inf && strict)
ret = (val1 < values2[j]);
else if (inf && !strict)
ret = (val1 <= values2[j]);
else if (!inf && strict)
ret = (val1 > values2[j]);
else if (!inf && !strict)
ret = (val1 >= values2[j]);
if (ret)
break;
}
if (ret)
break;
init = 1;
}
xmlFree(values2);
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(ret);
return(0);
}
/**
* xmlXPathCompareNodeSetValue:
* @ctxt: the XPath Parser context
* @inf: less than (1) or greater than (0)
* @strict: is the comparison strict
* @arg: the node set
* @val: the value
*
* Implement the compare operation between a nodeset and a value
* @ns < @val (1, 1, ...
* @ns <= @val (1, 0, ...
* @ns > @val (0, 1, ...
* @ns >= @val (0, 0, ...
*
* If one object to be compared is a node-set and the other is a boolean,
* then the comparison will be true if and only if the result of performing
* the comparison on the boolean and on the result of converting
* the node-set to a boolean using the boolean function is true.
*
* Returns 0 or 1 depending on the results of the test.
*/
static int
xmlXPathCompareNodeSetValue(xmlXPathParserContextPtr ctxt, int inf, int strict,
xmlXPathObjectPtr arg, xmlXPathObjectPtr val) {
if ((val == NULL) || (arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
return(0);
switch(val->type) {
case XPATH_NUMBER:
return(xmlXPathCompareNodeSetFloat(ctxt, inf, strict, arg, val));
case XPATH_NODESET:
case XPATH_XSLT_TREE:
return(xmlXPathCompareNodeSets(inf, strict, arg, val));
case XPATH_STRING:
return(xmlXPathCompareNodeSetString(ctxt, inf, strict, arg, val));
case XPATH_BOOLEAN:
valuePush(ctxt, arg);
xmlXPathBooleanFunction(ctxt, 1);
valuePush(ctxt, val);
return(xmlXPathCompareValues(ctxt, inf, strict));
default:
TODO
return(0);
}
return(0);
}
/**
* xmlXPathEqualNodeSetString
* @arg: the nodeset object argument
* @str: the string to compare to.
*
* Implement the equal operation on XPath objects content: @arg1 == @arg2
* If one object to be compared is a node-set and the other is a string,
* then the comparison will be true if and only if there is a node in
* the node-set such that the result of performing the comparison on the
* string-value of the node and the other string is true.
*
* Returns 0 or 1 depending on the results of the test.
*/
static int
xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
int i;
xmlNodeSetPtr ns;
xmlChar *str2;
if ((str == NULL) || (arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
return(0);
ns = arg->nodesetval;
if (ns == NULL)
return(0);
if (ns->nodeNr <= 0)
return(0);
for (i = 0;i < ns->nodeNr;i++) {
str2 = xmlNodeGetContent(ns->nodeTab[i]);
if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
xmlFree(str2);
return(1);
}
if (str2 != NULL)
xmlFree(str2);
}
return(0);
}
/**
* xmlXPathEqualNodeSetFloat
* @arg: the nodeset object argument
* @f: the float to compare to
*
* Implement the equal operation on XPath objects content: @arg1 == @arg2
* If one object to be compared is a node-set and the other is a number,
* then the comparison will be true if and only if there is a node in
* the node-set such that the result of performing the comparison on the
* number to be compared and on the result of converting the string-value
* of that node to a number using the number function is true.
*
* Returns 0 or 1 depending on the results of the test.
*/
static int
xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, double f) {
char buf[100] = "";
if ((arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
return(0);
xmlXPathFormatNumber(f, buf, sizeof(buf));
return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
}
/**
* xmlXPathEqualNodeSets
* @arg1: first nodeset object argument
* @arg2: second nodeset object argument
*
* Implement the equal operation on XPath nodesets: @arg1 == @arg2
* If both objects to be compared are node-sets, then the comparison
* will be true if and only if there is a node in the first node-set and
* a node in the second node-set such that the result of performing the
* comparison on the string-values of the two nodes is true.
*
* (needless to say, this is a costly operation)
*
* Returns 0 or 1 depending on the results of the test.
*/
static int
xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
int i, j;
xmlChar **values1;
xmlChar **values2;
int ret = 0;
xmlNodeSetPtr ns1;
xmlNodeSetPtr ns2;
if ((arg1 == NULL) ||
((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)))
return(0);
if ((arg2 == NULL) ||
((arg2->type != XPATH_NODESET) && (arg2->type != XPATH_XSLT_TREE)))
return(0);
ns1 = arg1->nodesetval;
ns2 = arg2->nodesetval;
if ((ns1 == NULL) || (ns1->nodeNr <= 0))
return(0);
if ((ns2 == NULL) || (ns2->nodeNr <= 0))
return(0);
/*
* check if there is a node pertaining to both sets
*/
for (i = 0;i < ns1->nodeNr;i++)
for (j = 0;j < ns2->nodeNr;j++)
if (ns1->nodeTab[i] == ns2->nodeTab[j])
return(1);
values1 = (xmlChar **) xmlMalloc(ns1->nodeNr * sizeof(xmlChar *));
if (values1 == NULL)
return(0);
memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
if (values2 == NULL) {
xmlFree(values1);
return(0);
}
memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
for (i = 0;i < ns1->nodeNr;i++) {
values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
for (j = 0;j < ns2->nodeNr;j++) {
if (i == 0)
values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
ret = xmlStrEqual(values1[i], values2[j]);
if (ret)
break;
}
if (ret)
break;
}
for (i = 0;i < ns1->nodeNr;i++)
if (values1[i] != NULL)
xmlFree(values1[i]);
for (j = 0;j < ns2->nodeNr;j++)
if (values2[j] != NULL)
xmlFree(values2[j]);
xmlFree(values1);
xmlFree(values2);
return(ret);
}
/**
* xmlXPathEqualValues:
* @ctxt: the XPath Parser context
*
* Implement the equal operation on XPath objects content: @arg1 == @arg2
*
* Returns 0 or 1 depending on the results of the test.
*/
int
xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
xmlXPathObjectPtr arg1, arg2;
int ret = 0;
arg1 = valuePop(ctxt);
if (arg1 == NULL)
XP_ERROR0(XPATH_INVALID_OPERAND);
arg2 = valuePop(ctxt);
if (arg2 == NULL) {
xmlXPathFreeObject(arg1);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
if (arg1 == arg2) {
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: by pointer\n");
#endif
return(1);
}
switch (arg1->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_XSLT_TREE:
case XPATH_NODESET:
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_XSLT_TREE:
case XPATH_NODESET:
ret = xmlXPathEqualNodeSets(arg1, arg2);
break;
case XPATH_BOOLEAN:
if ((arg1->nodesetval == NULL) ||
(arg1->nodesetval->nodeNr == 0)) ret = 0;
else
ret = 1;
ret = (ret == arg2->boolval);
break;
case XPATH_NUMBER:
ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
break;
case XPATH_STRING:
ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
}
break;
case XPATH_BOOLEAN:
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
if ((arg2->nodesetval == NULL) ||
(arg2->nodesetval->nodeNr == 0)) ret = 0;
else
ret = 1;
break;
case XPATH_BOOLEAN:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: %d boolean %d \n",
arg1->boolval, arg2->boolval);
#endif
ret = (arg1->boolval == arg2->boolval);
break;
case XPATH_NUMBER:
if (arg2->floatval) ret = 1;
else ret = 0;
ret = (arg1->boolval == ret);
break;
case XPATH_STRING:
if ((arg2->stringval == NULL) ||
(arg2->stringval[0] == 0)) ret = 0;
else
ret = 1;
ret = (arg1->boolval == ret);
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
}
break;
case XPATH_NUMBER:
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
break;
case XPATH_BOOLEAN:
if (arg1->floatval) ret = 1;
else ret = 0;
ret = (arg2->boolval == ret);
break;
case XPATH_STRING:
valuePush(ctxt, arg2);
xmlXPathNumberFunction(ctxt, 1);
arg2 = valuePop(ctxt);
/* no break on purpose */
case XPATH_NUMBER:
ret = (arg1->floatval == arg2->floatval);
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
}
break;
case XPATH_STRING:
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
break;
case XPATH_BOOLEAN:
if ((arg1->stringval == NULL) ||
(arg1->stringval[0] == 0)) ret = 0;
else
ret = 1;
ret = (arg2->boolval == ret);
break;
case XPATH_STRING:
ret = xmlStrEqual(arg1->stringval, arg2->stringval);
break;
case XPATH_NUMBER:
valuePush(ctxt, arg1);
xmlXPathNumberFunction(ctxt, 1);
arg1 = valuePop(ctxt);
ret = (arg1->floatval == arg2->floatval);
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
}
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
}
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(ret);
}
/**
* xmlXPathCompareValues:
* @ctxt: the XPath Parser context
* @inf: less than (1) or greater than (0)
* @strict: is the comparison strict
*
* Implement the compare operation on XPath objects:
* @arg1 < @arg2 (1, 1, ...
* @arg1 <= @arg2 (1, 0, ...
* @arg1 > @arg2 (0, 1, ...
* @arg1 >= @arg2 (0, 0, ...
*
* When neither object to be compared is a node-set and the operator is
* <=, <, >=, >, then the objects are compared by converted both objects
* to numbers and comparing the numbers according to IEEE 754. The <
* comparison will be true if and only if the first number is less than the
* second number. The <= comparison will be true if and only if the first
* number is less than or equal to the second number. The > comparison
* will be true if and only if the first number is greater than the second
* number. The >= comparison will be true if and only if the first number
* is greater than or equal to the second number.
*
* Returns 1 if the comparaison succeeded, 0 if it failed
*/
int
xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
int ret = 0;
xmlXPathObjectPtr arg1, arg2;
arg2 = valuePop(ctxt);
if (arg2 == NULL) {
XP_ERROR0(XPATH_INVALID_OPERAND);
}
arg1 = valuePop(ctxt);
if (arg1 == NULL) {
xmlXPathFreeObject(arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
if ((arg2->type == XPATH_NODESET) || (arg1->type == XPATH_NODESET)) {
if ((arg2->type == XPATH_NODESET) && (arg1->type == XPATH_NODESET)) {
ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
} else {
if (arg1->type == XPATH_NODESET) {
ret = xmlXPathCompareNodeSetValue(ctxt, inf, strict,
arg1, arg2);
} else {
ret = xmlXPathCompareNodeSetValue(ctxt, !inf, strict,
arg2, arg1);
}
}
return(ret);
}
if (arg1->type != XPATH_NUMBER) {
valuePush(ctxt, arg1);
xmlXPathNumberFunction(ctxt, 1);
arg1 = valuePop(ctxt);
}
if (arg1->type != XPATH_NUMBER) {
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
if (arg2->type != XPATH_NUMBER) {
valuePush(ctxt, arg2);
xmlXPathNumberFunction(ctxt, 1);
arg2 = valuePop(ctxt);
}
if (arg2->type != XPATH_NUMBER) {
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
/*
* Add tests for infinity and nan
* => feedback on 3.4 for Inf and NaN
*/
if (inf && strict)
ret = (arg1->floatval < arg2->floatval);
else if (inf && !strict)
ret = (arg1->floatval <= arg2->floatval);
else if (!inf && strict)
ret = (arg1->floatval > arg2->floatval);
else if (!inf && !strict)
ret = (arg1->floatval >= arg2->floatval);
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(ret);
}
/**
* xmlXPathValueFlipSign:
* @ctxt: the XPath Parser context
*
* Implement the unary - operation on an XPath object
* The numeric operators convert their operands to numbers as if
* by calling the number function.
*/
void
xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
xmlXPathObjectPtr arg;
POP_FLOAT
arg->floatval = -arg->floatval;
valuePush(ctxt, arg);
}
/**
* xmlXPathAddValues:
* @ctxt: the XPath Parser context
*
* Implement the add operation on XPath objects:
* The numeric operators convert their operands to numbers as if
* by calling the number function.
*/
void
xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
xmlXPathObjectPtr arg;
double val;
POP_FLOAT
val = arg->floatval;
xmlXPathFreeObject(arg);
POP_FLOAT
arg->floatval += val;
valuePush(ctxt, arg);
}
/**
* xmlXPathSubValues:
* @ctxt: the XPath Parser context
*
* Implement the substraction operation on XPath objects:
* The numeric operators convert their operands to numbers as if
* by calling the number function.
*/
void
xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
xmlXPathObjectPtr arg;
double val;
POP_FLOAT
val = arg->floatval;
xmlXPathFreeObject(arg);
POP_FLOAT
arg->floatval -= val;
valuePush(ctxt, arg);
}
/**
* xmlXPathMultValues:
* @ctxt: the XPath Parser context
*
* Implement the multiply operation on XPath objects:
* The numeric operators convert their operands to numbers as if
* by calling the number function.
*/
void
xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
xmlXPathObjectPtr arg;
double val;
POP_FLOAT
val = arg->floatval;
xmlXPathFreeObject(arg);
POP_FLOAT
arg->floatval *= val;
valuePush(ctxt, arg);
}
/**
* xmlXPathDivValues:
* @ctxt: the XPath Parser context
*
* Implement the div operation on XPath objects @arg1 / @arg2:
* The numeric operators convert their operands to numbers as if
* by calling the number function.
*/
void
xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
xmlXPathObjectPtr arg;
double val;
POP_FLOAT
val = arg->floatval;
xmlXPathFreeObject(arg);
POP_FLOAT
arg->floatval /= val;
valuePush(ctxt, arg);
}
/**
* xmlXPathModValues:
* @ctxt: the XPath Parser context
*
* Implement the mod operation on XPath objects: @arg1 / @arg2
* The numeric operators convert their operands to numbers as if
* by calling the number function.
*/
void
xmlXPathModValues(xmlXPathParserContextPtr ctxt) {