/*
* xpath.c: XML Path Language implementation
* XPath is a language for addressing parts of an XML document,
* designed to be used by both XSLT and XPointer
*
* Reference: W3C Recommendation 16 November 1999
* http://www.w3.org/TR/1999/REC-xpath-19991116
* Public reference:
* http://www.w3.org/TR/xpath
*
* See Copyright for the status of this software
*
* Author: daniel@veillard.com
*
*/
#define IN_LIBXML
#include "XmlEnglibxml.h"
#include <string.h>
// TODO: XE Cleanup
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_MATH_H
#include <math.h>
#endif
#if defined(HAVE_CTYPE_H)
#include <ctype.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include "Libxml2_globals.h"
//#include "Libxml2_xmlmemory.h"
//#include "Libxml2_xpath.h"
//#include "Libxml2_tree.h"
//#include "Libxml2_valid.h"
#include "Libxml2_xpathInternals.h"
#include "Libxml2_parserInternals.h"
//#include "Libxml2_hash.h"
#ifdef LIBXML_XPTR_ENABLED
#include "Libxml2_xpointer.h"
#endif
#ifdef LIBXML_DEBUG_ENABLED
#include <debugXML.h>
#endif
//#include "Libxml2_xmlerror.h"
//#include "Libxml2_threads.h"
// TODO: remove/rename TODO
#define TODO \
xmlGenericError(xmlGenericErrorContext, \
EMBED_ERRTXT("Unimplemented block at %s:%d\n"), \
__FILE__, __LINE__);
#if defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XPATH_ENABLED)
/************************************************************************
* *
* Floating point stuff *
* *
************************************************************************/
#ifndef TRIO_REPLACE_STDIO
//#define TRIO_PUBLIC static
#endif
#include "Libxml2_trionan.inc"
/**
* xmlXPathInit:
*
* Initialize the XPath environment
*
* OOM: never (so far -- soon hash tables will be created here)
*/
void
xmlXPathInit(void) {
if (xmlXPathInitialized) return;
xmlXPathPINF = trio_pinf();
xmlXPathNINF = trio_ninf();
xmlXPathNAN = trio_nan();
xmlXPathNZERO = trio_nzero();
xmlXPathInitialized = 1;
// TODO: Register all XPath functions in reusable hash table here
}
/**
* xeXPathCleanup:
*
* Free any reusable by XPath module resources
*/
void xeXPathCleanup()
{
#ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED
if (xmlXPathDefaultFunctionsHash)
xmlHashFree(xmlXPathDefaultFunctionsHash, NULL);
xmlXPathDefaultFunctionsHash = NULL;
if (xmlXPathIntermediaryExtensionFunctionsHash)
xmlHashFree(xmlXPathIntermediaryExtensionFunctionsHash, NULL);
xmlXPathIntermediaryExtensionFunctionsHash = NULL;
#endif
}
// DONE: OPTIMIZE: Define as macro or inliner
/**
* 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
*/
/*
inline int xmlXPathIsNaN(double val) {
return(trio_isnan(val));
}*/
//#define xmlXPathIsNaN(val) trio_isnan(val)
//#define xmlXPathIsNaN(val) trio_isnan(val)
// DONE: OPTIMIZE: Define as macro or inliner
/**
* 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
*/
/*
inline int xmlXPathIsInf(double val) {
return(trio_isinf(val));
}
*/
#define xmlXPathIsInf(val) trio_isinf(val)
#endif /* SCHEMAS or XPATH */
#ifdef LIBXML_XPATH_ENABLED
// TODO: OPTIMIZE: Define as macro or inliner
/**
* 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
*/
/*
inline int xmlXPathGetSign(double val) {
return(trio_signbit(val));
}
*/
#define xmlXPathGetSign(val) trio_signbit(val)
/*
* TODO: when compatibility allows remove all "fake node libxslt" strings
* the test should just be name[0] = ' '
*/
/* #define DEBUG */
/* #define DEBUG_STEP */
/* #define DEBUG_STEP_NTH */
/* #define DEBUG_EXPR */
/* #define DEBUG_EVAL_COUNTS */
static const xmlNs xmlXPathXMLNamespaceStruct = {
NULL,
XML_NAMESPACE_DECL,
XML_XML_NAMESPACE,
BAD_CAST "xml",
NULL
};
static const xmlNsPtr xmlXPathXMLNamespace = (xmlNsPtr)&xmlXPathXMLNamespaceStruct;
/************************************************************************
* *
* Error handling routines *
* *
************************************************************************/
/*
* The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
*/
const char *const xmlXPathErrorMessages[] = {
EMBED_ERRTXT("Ok\n"),
EMBED_ERRTXT("Number encoding\n"),
EMBED_ERRTXT("Unfinished literal\n"),
EMBED_ERRTXT("Start of literal\n"),
EMBED_ERRTXT("Expected $ for variable reference\n"),
EMBED_ERRTXT("Undefined variable\n"),
EMBED_ERRTXT("Invalid predicate\n"),
EMBED_ERRTXT("Invalid expression\n"),
EMBED_ERRTXT("Missing closing curly brace\n"),
EMBED_ERRTXT("Unregistered function\n"),
EMBED_ERRTXT("Invalid operand\n"),
EMBED_ERRTXT("Invalid type\n"),
EMBED_ERRTXT("Invalid number of arguments\n"),
EMBED_ERRTXT("Invalid context size\n"),
EMBED_ERRTXT("Invalid context position\n"),
EMBED_ERRTXT("Memory allocation error\n"),
EMBED_ERRTXT("Syntax error\n"),
EMBED_ERRTXT("Resource error\n"),
EMBED_ERRTXT("Sub resource error\n"),
EMBED_ERRTXT("Undefined namespace prefix\n"),
EMBED_ERRTXT("Encoding error\n"),
EMBED_ERRTXT("Char out of XML range\n")
};
// TODO: OPTIMIZATION: Not required for Symbian
/**
* 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 EMBED_ERRTXT("Memory allocation failed : %s\n"), extra);
ctxt->lastError.message = (char *) xmlStrdup(buf);
} else {
ctxt->lastError.message = (char *)
xmlStrdup(BAD_CAST EMBED_ERRTXT("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,
EMBED_ERRTXT("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,
EMBED_ERRTXT("Memory allocation failed\n"));
}
}
/**
* xmlXPathErrMemory:
* @ctxt: an XPath parser context
* @extra: extra informations
*
* Handle a redefinition of attribute error
*/
// TODO: Obviously, something wrong here: ctxt!=NULL for sure
static void
xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt, const char *extra)
{
ctxt->error = XPATH_MEMORY_ERROR;
if (ctxt == NULL)
xmlXPathErrMemory(NULL, extra);
else
xmlXPathErrMemory(ctxt->context, extra);
}
/**
* xmlXPathErr:
* @ctxt: a XPath parser context
* @error: the error code
*
* Handle a Relax NG Parsing error
*/
void
xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
{
// TODO: OPTIMIZE: Combine two (or even all three) IF blocks into one
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,
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,
xmlXPathErrorMessages[error]);
return;
}
// TODO: OPTIMIZE: make a proxy for 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;
if (ctxt->context->lastError.str1) // agathe added
xmlFree(ctxt->context->lastError.str1);
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,
xmlXPathErrorMessages[error]);
}
}
// DONE: OPTIMIZE: Use macro instead of function call
// Macro added in xpathInternals.h
/**
* 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);
//}
/************************************************************************
* *
* Parser Types *
* *
************************************************************************/
/*
* Types are private:
*/
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;
/************************************************************************
* *
* 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, EMBED_ERRTXT("allocating component\n"));
return(NULL);
}
memset(cur, 0, sizeof(xmlXPathCompExpr));
cur->maxStep = 10; /* TODO: XPath context can be configured by maxStep parameter */
cur->nbStep = 0;
cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep * sizeof(xmlXPathStepOp));
if (cur->steps == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("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((xmlXPathObjectPtr)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((xmlXPathObjectPtr)op->value4);
}
}
xmlDictFree(comp->dict);
}
if (comp->steps != NULL) {
xmlFree(comp->steps);
}
#ifdef DEBUG_EVAL_COUNTS
if (comp->string != NULL) {
xmlFree(comp->string);
}
#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)
{
xmlXPathStepOpPtr step;
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, EMBED_ERRTXT("adding step\n"));
return(-1);
}
comp->steps = real;
}
// DONE: OPTIMIZE: make a proxy for comp->steps[comp->nbStep]
step = &(comp->steps[comp->nbStep]);
//
comp->last = comp->nbStep;
step->ch1 = ch1;
step->ch2 = ch2;
step->op = op;
step->value = value;
step->value2 = value2;
step->value3 = value3;
if ((comp->dict != NULL) &&
((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
(op == XPATH_OP_COLLECT)))
{
if (value4 != NULL) {
step->value4 = (xmlChar*)
(void*)xmlDictLookup(comp->dict, (xmlChar*) value4, -1);
xmlFree(value4);
} else
step->value4 = NULL;
if (value5 != NULL) {
step->value5 = (xmlChar*)
(void*)xmlDictLookup(comp->dict, (xmlChar*)value5, -1);
xmlFree(value5);
}else
step->value5 = NULL;
}
else
{
step->value4 = value4;
step->value5 = value5;
}
step->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)
/************************************************************************
* *
* Debugging related functions *
* *
************************************************************************/
// TODO: OPTIMIZE: Create a function and do function call in macro
#define STRANGE \
xmlGenericError(xmlGenericErrorContext, \
EMBED_ERRTXT("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)
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 /* 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];
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:
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, 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 = (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];
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 /* LIBXML_DEBUG_ENABLED */
/************************************************************************
* *
* 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
*/
extern xmlXPathObjectPtr
valuePop(xmlXPathParserContextPtr ctxt)
{
xmlXPathObjectPtr ret;
if (ctxt->valueNr <= 0)
return (0);
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] = 0;
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
*
* OOM: possible --> returns 0
*/
int
valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
{
if (ctxt->valueNr >= ctxt->valueMax)
{
xmlXPathObjectPtr* tmp;
// DONE: Fix xmlRealloc
tmp = (xmlXPathObjectPtr*)xmlRealloc(ctxt->valueTab,
ctxt->valueMax *
2 * sizeof(ctxt->valueTab[0]));
if (!tmp) {
xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("realloc failed !\n"));
return (0);
}
ctxt->valueTab = tmp;
ctxt->valueMax *= 2;
}
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;
xmlXPathFreeObject(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;
xmlXPathFreeObject(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;
xmlXPathFreeObject(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->value == NULL) {
xmlXPathSetError(ctxt, XPATH_INVALID_OPERAND);
return(NULL);
}
if (!xmlXPathStackIsNodeSet(ctxt)) {
xmlXPathSetTypeError(ctxt);
return(NULL);
}
obj = valuePop(ctxt);
ret = obj->nodesetval;
/* to fix memory leak of not clearing obj->user */
if (obj->boolval && obj->user != NULL)
xmlFreeNodeList((xmlNodePtr) obj->user);
xmlXPathFreeNodeSetList(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->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;
xmlXPathFreeObject(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(len,b,i,v) \
if (len == 1) b[i++] = (xmlChar) v; \
else i += xmlCopyChar(len,&b[i],v)
#define NEXTL(k) ctxt->cur += k
// XMLENGINE: NEXT was replaced with ctxt->cur++ since *ctxt->cur always !=0 there
// OOM: never
#define SKIP_BLANKS while (IS_BLANK_CH(*(ctxt->cur))) ctxt->cur++
#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)
#if (_MSC_VER >= 1300) && (WINVER < 0x0500)
//VC7 or later, building with pre-VC7 runtime libraries
//extern "C" long _ftol( double ); //defined by VC6 C libs
//extern "C" long _ftol2( double dblSource ) { return _ftol( dblSource ); }
#endif
/**
* xmlXPathFormatNumber:
* @number: number to format
* @buffer: output buffer
* @buffersize: size of output buffer
*
* Convert the number into a string representation.
*
* OOM: never
*/
static void
xmlXPathFormatNumber(double number, char buffer[], int buffersize)
{ // TODO: use IFs for clearer code / combine calls to snprintf()
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 res, value = (int) number;
ptr = &buffer[0];
if (value < 0) {
*ptr++ = '-';
value = -value;
}
if (value == 0) {
*ptr++ = '0';
} else {
cur = &work[0];
while (value != 0) {
res = value % 10;
value = value / 10;
*cur++ = '0' + res;
}
cur--;
while ((cur >= &work[0]) && (ptr - buffer < buffersize)) {
*ptr++ = *cur--;
}
}
if (ptr - buffer < buffersize) {
*ptr = 0;
} else if (buffersize > 0) {
ptr--;
*ptr = 0;
}
} 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 */
if (absolute_value > 0.0)
integer_place = 1 + (int)log10(absolute_value);
else
integer_place = 0;
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') {}// EMPTY LOOP
if (*ptr != '.')
ptr++;
while ((*ptr++ = *after_fraction++) != 0){} // EMPTY LOOP
/* 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 = (xmlChar*)((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
*
* OOM: never
*/
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->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 */
}
/**
* xmlXPathNodeSetSort:
* @set: the node set
*
* Sort the node set in document order
*
* OOM: never
*/
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
/**
* xmlXPathNodeSetDupNs:
* @node: the parent node of the namespace XPath node
* @ns: the libxml namespace declaration node.
*
* Namespace node in libxml don't match the XPath semantic. In a node set
* the namespace nodes are duplicated and the next pointer is set to the
* parent node in the XPath semantic.
*
* Returns the newly created object.
*
* OOM: possible --> returns NULL for valid arguments, sets OOM flag
*/
static xmlNodePtr
xmlXPathNodeSetDupNs(xmlNodePtr node, xmlNsPtr ns) {
xmlNsPtr cur;
if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
return(NULL);
if ((node == NULL) || (node->type == XML_NAMESPACE_DECL))
return((xmlNodePtr) ns);
/*
* Allocate a new Namespace and fill the fields.
*/
cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
if (cur == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("duplicating namespace\n"));
return(NULL);
}
memset(cur, 0, sizeof(xmlNs));
cur->type = XML_NAMESPACE_DECL;
// TODO: Check OOM conditions..
if (ns->href != NULL)
if(! (cur->href = xmlStrdup(ns->href)))
goto OOM;;
if (ns->prefix != NULL)
if(! (cur->prefix = xmlStrdup(ns->prefix)))
goto OOM;
cur->next = (xmlNsPtr) node;
return((xmlNodePtr) cur);
//---------------------
OOM:
xmlFree(cur);
return NULL;
}
/**
* xmlXPathNodeSetFreeNs:
* @ns: the XPath namespace node found in a nodeset.
*
* Namespace nodes in libxml don't match the XPath semantic. In a node set
* the namespace nodes are duplicated and the next pointer is set to the
* parent node in the XPath semantic. Check if such a node needs to be freed
*
* OOM: never
*/
void
xmlXPathNodeSetFreeNs(xmlNsPtr ns) {
if ((ns == NULL) || (ns->type != XML_NAMESPACE_DECL))
return;
if ((ns->next != NULL) && (ns->next->type != XML_NAMESPACE_DECL)) {
if (ns->href != NULL)
xmlFree((xmlChar *)ns->href);
if (ns->prefix != NULL)
xmlFree((xmlChar *)ns->prefix);
xmlFree(ns);
}
}
/**
* xmlXPathNodeSetCreate:
* @val: an initial xmlNodePtr, or NULL
*
* Create a new xmlNodeSetPtr of type double and of value @val
*
* Returns the newly created object.
*
* OOM: possible --> returns NULL and sets OOM flag
*/
xmlNodeSetPtr
xmlXPathNodeSetCreate(xmlNodePtr val) {
xmlNodeSetPtr ret;
ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
if (ret == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
if (val != NULL) {
xmlNodePtr nval;
// TODO: Optimize or make adjustable the allocation size of NodeSet
ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
if (ret->nodeTab == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n"));
goto OOM;
}
memset(ret->nodeTab, 0, XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
ret->nodeMax = XML_NODESET_DEFAULT;
if (val->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns = (xmlNsPtr) val;
nval = xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
// TODO: OOM: Chrck OOM
if(OOM_FLAG)
goto OOM;
} else {
nval = val;
}
ret->nodeTab[ret->nodeNr++] = nval;
}
return(ret);
//-------------------
OOM:
xmlFree(ret);
return(NULL);
}
/**
* xmlXPathNodeSetContains:
* @cur: the node-set
* @val: the node
*
* checks whether @cur contains @val
*
* Returns true (1) if @cur contains @val, false (0) otherwise
*
* OOM: never
*/
int
xmlXPathNodeSetContains (xmlNodeSetPtr cur, xmlNodePtr val) {
int i;
if (val->type == XML_NAMESPACE_DECL) {
for (i = 0; i < cur->nodeNr; i++) {
if (cur->nodeTab[i]->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns1, ns2;
ns1 = (xmlNsPtr) val;
ns2 = (xmlNsPtr) cur->nodeTab[i];
if (ns1 == ns2)
return(1);
if ((ns1->next != NULL) &&
(ns2->next == ns1->next) &&
(xmlStrEqual(ns1->prefix, ns2->prefix)))
return(1);
}
}
} else {
for (i = 0; i < cur->nodeNr; i++) {
if (cur->nodeTab[i] == val)
return(1);
}
}
return(0);
}
/*
TODO: OPTIMIZATION: xmlXPathNodeSetAddNs, xmlXPathNodeSetAdd and xmlXPathNodeSetAddUnique are the same code
*/
/**
* xmlXPathNodeSetAddNs:
* @cur: the initial node set
* @node: the hosting node
* @ns: a the namespace node
*
* add a new namespace node to an existing NodeSet
*/
void
xmlXPathNodeSetAddNs(xmlNodeSetPtr cur, xmlNodePtr node, xmlNsPtr ns) {
int i;
if ((ns == NULL) || (node == NULL) || (ns->type != XML_NAMESPACE_DECL) ||
(node->type != XML_ELEMENT_NODE))
return;
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
/*
* prevent duplicates
*/
for (i = 0;i < cur->nodeNr;i++) {
if ((cur->nodeTab[i] != NULL) &&
(cur->nodeTab[i]->type == XML_NAMESPACE_DECL) &&
(((xmlNsPtr)cur->nodeTab[i])->next == (xmlNsPtr) node) &&
(xmlStrEqual(ns->prefix, ((xmlNsPtr)cur->nodeTab[i])->prefix)))
return;
}
/*
* grow the nodeTab if needed
*/
if (cur->nodeMax == 0) {
cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (cur->nodeTab == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
return;
}
cur->nodeTab = temp;
}
cur->nodeTab[cur->nodeNr++] = xmlXPathNodeSetDupNs(node, ns);
}
/**
* xmlXPathNodeSetAdd:
* @cur: the initial node set
* @val: a new xmlNodePtr
*
* add a new xmlNodePtr to an existing NodeSet
*/
void
xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
int i;
if (val == NULL) return;
#if 0
if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
return; /* an XSLT fake node */
#endif
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
/*
* prevent duplcates
*/
for (i = 0;i < cur->nodeNr;i++)
if (cur->nodeTab[i] == val) return;
/*
* grow the nodeTab if needed
*/
if (cur->nodeMax == 0) {
cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (cur->nodeTab == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\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;
// TODO: Realocation policy may be managed
cur->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
return;
}
cur->nodeTab = temp;
}
if (val->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns = (xmlNsPtr) val;
cur->nodeTab[cur->nodeNr++] =
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
} else
cur->nodeTab[cur->nodeNr++] = val;
}
/**
* xmlXPathNodeSetAddUnique:
* @cur: the initial node set
* @val: a new xmlNodePtr
*
* add a new xmlNodePtr to 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;
// TODO: Rudiments of XSLT - find out how these affect the XPath implementation
#if 0
if ((val->type == XML_ELEMENT_NODE) && (val->name[0] == ' '))
return; /* an XSLT fake node */
#endif
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
/*
* grow the nodeTab if needed
*/
if (cur->nodeMax == 0) {
cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT * sizeof(xmlNodePtr));
if (cur->nodeTab == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("growing nodeset\n"));
return;
}
cur->nodeTab = temp;
}
if (val->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns = (xmlNsPtr) val;
cur->nodeTab[cur->nodeNr++] =
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
} else
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);
}
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
initNr = val1->nodeNr;
for (i = 0;i < val2->nodeNr;i++) {
/*
* check against duplicates
*/
skip = 0;
for (j = 0; j < initNr; j++) {
if (val1->nodeTab[j] == val2->nodeTab[i]) {
skip = 1;
break;
} else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) &&
(val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) {
xmlNsPtr ns1, ns2;
ns1 = (xmlNsPtr) val1->nodeTab[j];
ns2 = (xmlNsPtr) val2->nodeTab[i];
if ((ns1->next == ns2->next) &&
(xmlStrEqual(ns1->prefix, ns2->prefix))) {
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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
return(NULL);
}
memset(val1->nodeTab, 0 ,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
val1->nodeMax = XML_NODESET_DEFAULT;
} else if (val1->nodeNr == val1->nodeMax) {
xmlNodePtr *temp;
val1->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
return(NULL);
}
val1->nodeTab = temp;
}
if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
val1->nodeTab[val1->nodeNr++] =
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
} else
val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
}
return(val1);
}
/**
* xmlXPathNodeSetMergeUnique:
* @val1: the first NodeSet or NULL
* @val2: the second NodeSet
*
* Merges two nodesets, all nodes from @val2 are added to @val1
* if @val1 is NULL, a new set is created and copied from @val2
*
* Returns @val1 once extended or NULL in case of error.
*/
static xmlNodeSetPtr
xmlXPathNodeSetMergeUnique(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
int i;
if (val2 == NULL) return(val1);
if (val1 == NULL) {
val1 = xmlXPathNodeSetCreate(NULL);
}
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
for (i = 0;i < val2->nodeNr;i++) {
/*
* grow the nodeTab if needed
*/
if (val1->nodeMax == 0) {
val1->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (val1->nodeTab == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
return(NULL);
}
memset(val1->nodeTab, 0 ,
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
val1->nodeMax = XML_NODESET_DEFAULT;
} else if (val1->nodeNr == val1->nodeMax) {
xmlNodePtr *temp;
val1->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(val1->nodeTab, val1->nodeMax *
sizeof(xmlNodePtr));
if (temp == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("merging nodeset\n"));
return(NULL);
}
val1->nodeTab = temp;
}
if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
val1->nodeTab[val1->nodeNr++] =
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
} else
val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
}
return(val1);
}
/**
* 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;
/*
* find node in nodeTab
*/
for (i = 0;i < cur->nodeNr;i++)
if (cur->nodeTab[i] == val) break;
if (i >= cur->nodeNr) { /* not found */
#ifdef DEBUG
xmlGenericError(xmlGenericErrorContext,
"xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
val->name);
#endif
return;
}
if ((cur->nodeTab[i] != NULL) &&
(cur->nodeTab[i]->type == XML_NAMESPACE_DECL))
xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[i]);
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;
if ((cur->nodeTab[val] != NULL) &&
(cur->nodeTab[val]->type == XML_NAMESPACE_DECL))
xmlXPathNodeSetFreeNs((xmlNsPtr) cur->nodeTab[val]);
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) {
int i;
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
for (i = 0;i < obj->nodeNr;i++)
if ((obj->nodeTab[i] != NULL) &&
(obj->nodeTab[i]->type == XML_NAMESPACE_DECL))
xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
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;
if (obj->nodeTab != NULL) {
for (i = 0;i < obj->nodeNr;i++) {
if (obj->nodeTab[i] != NULL) {
if (obj->nodeTab[i]->type == XML_NAMESPACE_DECL) {
xmlXPathNodeSetFreeNs((xmlNsPtr) obj->nodeTab[i]);
} else {
xmlFreeNodeList(obj->nodeTab[i]);
}
}
}
xmlFree(obj->nodeTab);
}
xmlFree(obj);
}
#if defined(DEBUG) || defined(DEBUG_STEP)
/**
* xmlGenericErrorContextNodeSet:
* @output: a FILE * for the output
* @obj: the xmlNodeSetPtr to display
*
* 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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating nodeset\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_NODESET;
ret->boolval = 0;
ret->nodesetval = xmlXPathNodeSetCreate(val);
if(OOM_FLAG)
{
xmlXPathFreeObject(ret);
return(NULL);
}
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating result value tree\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_XSLT_TREE;
ret->boolval = 1;
ret->user = (void *) val;
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;
// TODO: CHECK OOM everywhere
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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating node set object\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);
}
/**
* xmlXPathDifference:
* @nodes1: a node-set
* @nodes2: a node-set
*
* Implements the EXSLT - Sets difference() function:
* node-set set:difference (node-set, node-set)
*
* Returns the difference between the two node sets, or nodes1 if
* nodes2 is empty
*/
xmlNodeSetPtr
xmlXPathDifference (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
xmlNodeSetPtr ret;
int i, l1;
xmlNodePtr cur;
if (xmlXPathNodeSetIsEmpty(nodes2))
return(nodes1);
ret = xmlXPathNodeSetCreate(NULL);
if (xmlXPathNodeSetIsEmpty(nodes1))
return(ret);
l1 = xmlXPathNodeSetGetLength(nodes1);
for (i = 0; i < l1; i++) {
cur = xmlXPathNodeSetItem(nodes1, i);
if (!xmlXPathNodeSetContains(nodes2, cur))
xmlXPathNodeSetAddUnique(ret, cur);
}
return(ret);
}
/**
* xmlXPathIntersection:
* @nodes1: a node-set
* @nodes2: a node-set
*
* Implements the EXSLT - Sets intersection() function:
* node-set set:intersection (node-set, node-set)
*
* Returns a node set comprising the nodes that are within both the
* node sets passed as arguments
*/
xmlNodeSetPtr
xmlXPathIntersection (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
xmlNodeSetPtr ret = xmlXPathNodeSetCreate(NULL);
int i, l1;
xmlNodePtr cur;
if (xmlXPathNodeSetIsEmpty(nodes1))
return(ret);
if (xmlXPathNodeSetIsEmpty(nodes2))
return(ret);
l1 = xmlXPathNodeSetGetLength(nodes1);
for (i = 0; i < l1; i++) {
cur = xmlXPathNodeSetItem(nodes1, i);
if (xmlXPathNodeSetContains(nodes2, cur))
xmlXPathNodeSetAddUnique(ret, cur);
}
return(ret);
}
/**
* xmlXPathDistinctSorted:
* @nodes: a node-set, sorted by document order
*
* Implements the EXSLT - Sets distinct() function:
* node-set set:distinct (node-set)
*
* Returns a subset of the nodes contained in @nodes, or @nodes if
* it is empty
*/
xmlNodeSetPtr
xmlXPathDistinctSorted (xmlNodeSetPtr nodes) {
xmlNodeSetPtr ret;
xmlHashTablePtr hash;
int i, l;
xmlChar * strval;
xmlNodePtr cur;
if (xmlXPathNodeSetIsEmpty(nodes))
return(nodes);
ret = xmlXPathNodeSetCreate(NULL);
l = xmlXPathNodeSetGetLength(nodes);
hash = xmlHashCreate (l);
for (i = 0; i < l; i++) {
cur = xmlXPathNodeSetItem(nodes, i);
strval = xmlXPathCastNodeToString(cur);
if (xmlHashLookup(hash, strval) == NULL) {
xmlHashAddEntry(hash, strval, strval);
xmlXPathNodeSetAddUnique(ret, cur);
} else {
xmlFree(strval);
}
}
xmlHashFree(hash, (xmlHashDeallocator) xmlFree);
return(ret);
}
/**
* xmlXPathDistinct:
* @nodes: a node-set
*
* Implements the EXSLT - Sets distinct() function:
* node-set set:distinct (node-set)
* @nodes is sorted by document order, then #exslSetsDistinctSorted
* is called with the sorted node-set
*
* Returns a subset of the nodes contained in @nodes, or @nodes if
* it is empty
*/
xmlNodeSetPtr
xmlXPathDistinct (xmlNodeSetPtr nodes) {
if (xmlXPathNodeSetIsEmpty(nodes))
return(nodes);
xmlXPathNodeSetSort(nodes);
return(xmlXPathDistinctSorted(nodes));
}
/**
* xmlXPathHasSameNodes:
* @nodes1: a node-set
* @nodes2: a node-set
*
* Implements the EXSLT - Sets has-same-nodes function:
* boolean set:has-same-node(node-set, node-set)
*
* Returns true (1) if @nodes1 shares any node with @nodes2, false (0)
* otherwise
*/
int
xmlXPathHasSameNodes (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
int i, l;
xmlNodePtr cur;
if (xmlXPathNodeSetIsEmpty(nodes1) ||
xmlXPathNodeSetIsEmpty(nodes2))
return(0);
l = xmlXPathNodeSetGetLength(nodes1);
for (i = 0; i < l; i++) {
cur = xmlXPathNodeSetItem(nodes1, i);
if (xmlXPathNodeSetContains(nodes2, cur))
return(1);
}
return(0);
}
/**
* xmlXPathNodeLeadingSorted:
* @nodes: a node-set, sorted by document order
* @node: a node
*
* Implements the EXSLT - Sets leading() function:
* node-set set:leading (node-set, node-set)
*
* Returns the nodes in @nodes that precede @node in document order,
* @nodes if @node is NULL or an empty node-set if @nodes
* doesn't contain @node
*/
xmlNodeSetPtr
xmlXPathNodeLeadingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
int i, l;
xmlNodePtr cur;
xmlNodeSetPtr ret;
if (node == NULL)
return(nodes);
ret = xmlXPathNodeSetCreate(NULL);
if (xmlXPathNodeSetIsEmpty(nodes) ||
(!xmlXPathNodeSetContains(nodes, node)))
return(ret);
l = xmlXPathNodeSetGetLength(nodes);
for (i = 0; i < l; i++) {
cur = xmlXPathNodeSetItem(nodes, i);
if (cur == node)
break;
xmlXPathNodeSetAddUnique(ret, cur);
}
return(ret);
}
/**
* xmlXPathNodeLeading:
* @nodes: a node-set
* @node: a node
*
* Implements the EXSLT - Sets leading() function:
* node-set set:leading (node-set, node-set)
* @nodes is sorted by document order, then #exslSetsNodeLeadingSorted
* is called.
*
* Returns the nodes in @nodes that precede @node in document order,
* @nodes if @node is NULL or an empty node-set if @nodes
* doesn't contain @node
*/
xmlNodeSetPtr
xmlXPathNodeLeading (xmlNodeSetPtr nodes, xmlNodePtr node) {
xmlXPathNodeSetSort(nodes);
return(xmlXPathNodeLeadingSorted(nodes, node));
}
/**
* xmlXPathLeadingSorted:
* @nodes1: a node-set, sorted by document order
* @nodes2: a node-set, sorted by document order
*
* Implements the EXSLT - Sets leading() function:
* node-set set:leading (node-set, node-set)
*
* Returns the nodes in @nodes1 that precede the first node in @nodes2
* in document order, @nodes1 if @nodes2 is NULL or empty or
* an empty node-set if @nodes1 doesn't contain @nodes2
*/
xmlNodeSetPtr
xmlXPathLeadingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
if (xmlXPathNodeSetIsEmpty(nodes2))
return(nodes1);
return(xmlXPathNodeLeadingSorted(nodes1,
xmlXPathNodeSetItem(nodes2, 1)));
}
/**
* xmlXPathLeading:
* @nodes1: a node-set
* @nodes2: a node-set
*
* Implements the EXSLT - Sets leading() function:
* node-set set:leading (node-set, node-set)
* @nodes1 and @nodes2 are sorted by document order, then
* #exslSetsLeadingSorted is called.
*
* Returns the nodes in @nodes1 that precede the first node in @nodes2
* in document order, @nodes1 if @nodes2 is NULL or empty or
* an empty node-set if @nodes1 doesn't contain @nodes2
*/
xmlNodeSetPtr
xmlXPathLeading (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
if (xmlXPathNodeSetIsEmpty(nodes2))
return(nodes1);
if (xmlXPathNodeSetIsEmpty(nodes1))
return(xmlXPathNodeSetCreate(NULL));
xmlXPathNodeSetSort(nodes1);
xmlXPathNodeSetSort(nodes2);
return(xmlXPathNodeLeadingSorted(nodes1,
xmlXPathNodeSetItem(nodes2, 1)));
}
/**
* xmlXPathNodeTrailingSorted:
* @nodes: a node-set, sorted by document order
* @node: a node
*
* Implements the EXSLT - Sets trailing() function:
* node-set set:trailing (node-set, node-set)
*
* Returns the nodes in @nodes that follow @node in document order,
* @nodes if @node is NULL or an empty node-set if @nodes
* doesn't contain @node
*/
xmlNodeSetPtr
xmlXPathNodeTrailingSorted (xmlNodeSetPtr nodes, xmlNodePtr node) {
int i, l;
xmlNodePtr cur;
xmlNodeSetPtr ret;
if (node == NULL)
return(nodes);
ret = xmlXPathNodeSetCreate(NULL);
if (xmlXPathNodeSetIsEmpty(nodes) ||
(!xmlXPathNodeSetContains(nodes, node)))
return(ret);
l = xmlXPathNodeSetGetLength(nodes);
for (i = l; i > 0; i--) {
cur = xmlXPathNodeSetItem(nodes, i);
if (cur == node)
break;
xmlXPathNodeSetAddUnique(ret, cur);
}
return(ret);
}
/**
* xmlXPathNodeTrailing:
* @nodes: a node-set
* @node: a node
*
* Implements the EXSLT - Sets trailing() function:
* node-set set:trailing (node-set, node-set)
* @nodes is sorted by document order, then #xmlXPathNodeTrailingSorted
* is called.
*
* Returns the nodes in @nodes that follow @node in document order,
* @nodes if @node is NULL or an empty node-set if @nodes
* doesn't contain @node
*/
xmlNodeSetPtr
xmlXPathNodeTrailing (xmlNodeSetPtr nodes, xmlNodePtr node) {
xmlXPathNodeSetSort(nodes);
return(xmlXPathNodeTrailingSorted(nodes, node));
}
/**
* xmlXPathTrailingSorted:
* @nodes1: a node-set, sorted by document order
* @nodes2: a node-set, sorted by document order
*
* Implements the EXSLT - Sets trailing() function:
* node-set set:trailing (node-set, node-set)
*
* Returns the nodes in @nodes1 that follow the first node in @nodes2
* in document order, @nodes1 if @nodes2 is NULL or empty or
* an empty node-set if @nodes1 doesn't contain @nodes2
*/
xmlNodeSetPtr
xmlXPathTrailingSorted (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
if (xmlXPathNodeSetIsEmpty(nodes2))
return(nodes1);
return(xmlXPathNodeTrailingSorted(nodes1,
xmlXPathNodeSetItem(nodes2, 0)));
}
/**
* xmlXPathTrailing:
* @nodes1: a node-set
* @nodes2: a node-set
*
* Implements the EXSLT - Sets trailing() function:
* node-set set:trailing (node-set, node-set)
* @nodes1 and @nodes2 are sorted by document order, then
* #xmlXPathTrailingSorted is called.
*
* Returns the nodes in @nodes1 that follow the first node in @nodes2
* in document order, @nodes1 if @nodes2 is NULL or empty or
* an empty node-set if @nodes1 doesn't contain @nodes2
*/
xmlNodeSetPtr
xmlXPathTrailing (xmlNodeSetPtr nodes1, xmlNodeSetPtr nodes2) {
if (xmlXPathNodeSetIsEmpty(nodes2))
return(nodes1);
if (xmlXPathNodeSetIsEmpty(nodes1))
return(xmlXPathNodeSetCreate(NULL));
xmlXPathNodeSetSort(nodes1);
xmlXPathNodeSetSort(nodes2);
return(xmlXPathNodeTrailingSorted(nodes1,
xmlXPathNodeSetItem(nodes2, 0)));
}
/************************************************************************
* *
* 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 || name == NULL)
return(-1);
if (ctxt->funcHash == NULL)
ctxt->funcHash = xmlHashCreate(0);
if (ctxt->funcHash == NULL)
return(-1);
if (f == NULL)
return(xmlHashRemoveEntry2(ctxt->funcHash, name, ns_uri, NULL));
if( xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *)f) < 0)
{
xmlHashFree(ctxt->funcHash, NULL);
return(-1);
}
return(0);
}
/**
* xmlXPathRegisterFuncLookup:
* @ctxt: the XPath context
* @f: the lookup function
* @funcCtxt: the lookup data
*
* Registers an external mechanism to do function lookup.
*/
void
xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
xmlXPathFuncLookupFunc f,
void *funcCtxt) {
if (ctxt == NULL)
return;
ctxt->funcLookupFunc = (void *) f;
ctxt->funcLookupData = funcCtxt;
}
/**
* 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) {
if (ctxt == NULL)
return (NULL);
if (ctxt->funcLookupFunc != NULL) {
xmlXPathFunction ret;
xmlXPathFuncLookupFunc f;
f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc;
ret = f(ctxt->funcLookupData, name, NULL);
if (ret != NULL)
return(ret);
}
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 (name == NULL)
return(NULL);
if (ctxt->funcLookupFunc != NULL) {
xmlXPathFunction ret;
xmlXPathFuncLookupFunc f;
f = (xmlXPathFuncLookupFunc) ctxt->funcLookupFunc;
ret = f(ctxt->funcLookupData, name, ns_uri);
if (ret != NULL)
return(ret);
}
if (ctxt->funcHash == 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 Variables *
* *
************************************************************************/
/**
* 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);
if (value == NULL)
return(xmlHashRemoveEntry2(ctxt->varHash, name, ns_uri,
(xmlHashDeallocator)xmlXPathFreeObject));
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 a copy of the value or NULL if not found
*/
xmlXPathObjectPtr
xmlXPathVariableLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
if (ctxt == NULL)
return(NULL);
if (ctxt->varLookupFunc != NULL) {
xmlXPathVariableLookupFunc func = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc;
xmlXPathObjectPtr ret = func(ctxt->varLookupData, name, 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 a copy of 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) {
xmlXPathVariableLookupFunc lookupFunc = (xmlXPathVariableLookupFunc)ctxt->varLookupFunc;
xmlXPathObjectPtr ret = lookupFunc(ctxt->varLookupData, name, ns_uri);
if (ret != NULL)
return(ret);
}
if (ctxt->varHash == NULL)
return(NULL);
if (name == NULL)
return(NULL);
return(xmlXPathObjectCopy((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, (xmlHashDeallocator)xmlXPathFreeObject);
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) {
int res = -1;
xmlChar* uri = NULL;
if (ctxt == NULL)
return(-1);
if (prefix == NULL)
return(-1);
if (ctxt->nsHash == NULL)
ctxt->nsHash = xmlHashCreate(10);
if (ctxt->nsHash == NULL)
return(-1);
if (!ns_uri)
return(xmlHashRemoveEntry(ctxt->nsHash, prefix,(xmlHashDeallocator)xmlFree));
uri = xmlStrdup(ns_uri);
if (uri)
{
res = xmlHashUpdateEntry(ctxt->nsHash, prefix, (void*)uri,
(xmlHashDeallocator)xmlFree);
if ( res == -1)
xmlFree( uri );
}
return( res );
}
/**
* 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->namespaces != NULL) {
int i;
for (i = 0;i < ctxt->nsNr;i++) {
if ((ctxt->namespaces[i] != NULL) &&
(xmlStrEqual(ctxt->namespaces[i]->prefix, prefix)))
return(ctxt->namespaces[i]->href);
}
}
return((const xmlChar *) xmlHashLookup(ctxt->nsHash, prefix));
}
/**
* xmlXPathRegisteredNsCleanup:
* @ctxt: the XPath context
*
* Cleanup the XPath context data associated to registered variables
*/
void
xmlXPathRegisteredNsCleanup(xmlXPathContextPtr ctxt) {
if (ctxt == NULL)
return;
xmlHashFree(ctxt->nsHash, (xmlHashDeallocator)xmlFree);
ctxt->nsHash = NULL;
}
/************************************************************************
* *
* Routines to handle Values *
* *
************************************************************************/
/* Allocations are terrible, one needs 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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating float object\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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating boolean object\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 or NULL if OOM
*
* OOM: possible --> sets OOM flag
*/
xmlXPathObjectPtr
xmlXPathNewString(const xmlChar *val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_STRING;
ret->stringval = xmlStrdup(val ? val : (const xmlChar *)"");
if( OOM_FLAG )
{
xmlXPathFreeObject(ret);
return(NULL);
}
return(ret);
}
/**
* xmlXPathWrapString:
* @val: the xmlChar * value
*
* Wraps the @val string into an XPath object.
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathWrapString (xmlChar *val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_STRING;
ret->stringval = val;
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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating string object\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_STRING;
ret->stringval = xmlStrdup(BAD_CAST val);
if(OOM_FLAG)
{
xmlXPathFreeObject(ret);
return(NULL);
}
return(ret);
}
/**
* xmlXPathWrapCString:
* @val: the char * value
*
* Wraps a string into an XPath object.
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathWrapCString (char * val) {
return(xmlXPathWrapString((xmlChar *)(val)));
}
/**
* xmlXPathWrapExternal:
* @val: the user data
*
* Wraps the @val data into an XPath object.
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathWrapExternal (void *val) {
xmlXPathObjectPtr ret;
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating user object\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_USERS;
ret->user = val;
return(ret);
}
/**
* xmlXPathObjectCopy:
* @val: the original object
*
* allocate a new copy of a given object
*
* Returns the newly created object or NULL if OOM
*/
xmlXPathObjectPtr
xmlXPathObjectCopy(xmlXPathObjectPtr val) {
xmlXPathObjectPtr ret;
if (val == NULL)
return(NULL);
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
if (ret == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("copying object\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);
if( OOM_FLAG )
{
xmlXPathFreeObject(ret);
return(NULL);
}
break;
case XPATH_XSLT_TREE:
if ((val->nodesetval != NULL) &&
(val->nodesetval->nodeTab != NULL)) {
xmlNodePtr cur, tmp;
xmlDocPtr top;
ret->boolval = 1;
top = xmlNewDoc(NULL);
top->name = (char *)
xmlStrdup(val->nodesetval->nodeTab[0]->name);
ret->user = top;
if (top != NULL) {
top->doc = top;
cur = val->nodesetval->nodeTab[0]->children;
while (cur != NULL) {
tmp = xmlDocCopyNode(cur, top, 1);
xmlAddChild((xmlNodePtr) top, tmp);
cur = cur->next;
}
}
ret->nodesetval = xmlXPathNodeSetCreate((xmlNodePtr) top);
} else
ret->nodesetval = xmlXPathNodeSetCreate(NULL);
/* Deallocate the copied tree value */
break;
case XPATH_NODESET:
ret->nodesetval = xmlXPathNodeSetMerge(NULL, val->nodesetval);
/* Do not deallocate the copied tree value */
ret->boolval = 0;
break;
case XPATH_LOCATIONSET:
#ifdef LIBXML_XPTR_ENABLED
{
xmlLocationSetPtr loc = val->user;
ret->user = (void *) xmlXPtrLocationSetMerge(NULL, loc);
break;
}
#endif
case XPATH_USERS:
ret->user = val->user;
break;
case XPATH_UNDEFINED:
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathObjectCopy: unsupported type %d\n"),
val->type);
break;
}
return(ret);
}
/**
* xmlXPathFreeObject:
* @obj: the object to free
*
* Free up an xmlXPathObjectPtr object.
*/
// TODO: OPTIMIZE: Some reduction of function calls is possible...
// Consider using own version of this function
void
xmlXPathFreeObject(xmlXPathObjectPtr obj) {
if (obj == NULL) return;
if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
if (obj->boolval) {
if (obj->user != NULL) {
xmlXPathFreeNodeSet(obj->nodesetval);
xmlFreeNodeList((xmlNodePtr) obj->user);
} else
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);
}
xmlFree(obj);
}
/************************************************************************
* *
* Type Casting Routines *
* *
************************************************************************/
/**
* xmlXPathCastBooleanToString:
* @val: a boolean
*
* Converts a boolean to its string value.
*
* Returns a newly allocated string.
*/
xmlChar *
xmlXPathCastBooleanToString (int val) {
xmlChar *ret;
if (val)
ret = xmlStrdup((const xmlChar*) "true");
else
ret = xmlStrdup((const xmlChar*) "false");
return(ret);
}
/**
* xmlXPathCastNumberToString:
* @val: a number
*
* Converts a number to its string value.
*
* Returns a newly allocated string.
*
* OOM: possible --> returns NULL, OOM flag is set
*/
xmlChar*
xmlXPathCastNumberToString (double val) {
xmlChar *ret;
switch (xmlXPathIsInf(val)) {
case 1:
ret = xmlStrdup((const xmlChar*) "Infinity");
break;
case -1:
ret = xmlStrdup((const xmlChar*) "-Infinity");
break;
default:
if (xmlXPathIsNaN(val)) {
ret = xmlStrdup((const xmlChar*) "NaN");
} else if (val == 0 && xmlXPathGetSign(val) != 0) {
ret = xmlStrdup((const xmlChar*) "0");
} else {
/* TODO: could be improved */
char buf[100];
xmlXPathFormatNumber(val, buf, 100);
ret = xmlStrdup((const xmlChar *) buf);
}
}
return(ret);
}
/**
* xmlXPathCastNodeToString:
* @node: a node
*
* Converts a node to its string value.
*
* Returns a newly allocated string.
*
* OOM: possible --> returns NULL; OOM flag must be checked ALWAYS
*/
xmlChar*
xmlXPathCastNodeToString (xmlNodePtr node) {
return(xmlNodeGetContent(node));
} // TODO: OPTIMIZE: define this as macro
/**
* xmlXPathCastNodeSetToString:
* @ns: a node-set
*
* Converts a node-set to its string value.
*
* Returns a newly allocated string.
*
* OOM: possible --> returns NULL, OOM flag is set
*/
xmlChar *
xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
return(xmlStrdup((const xmlChar *) ""));
xmlXPathNodeSetSort(ns);
return(xmlXPathCastNodeToString(ns->nodeTab[0]));
}
/**
* xmlXPathCastToString:
* @val: an XPath object
*
* Converts an existing object to its string() equivalent
*
* Returns the string value of the object, NULL in case of error.
* A new string is allocated only if needed (@val isn't a
* string object).
*/
xmlChar *
xmlXPathCastToString(xmlXPathObjectPtr val) {
xmlChar *ret = NULL;
if (val == NULL)
return(xmlStrdup((const xmlChar *) ""));
switch (val->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext, "String: undefined\n");
#endif
ret = xmlStrdup((const xmlChar *) "");
break;
case XPATH_NUMBER:
ret = xmlXPathCastNumberToString(val->floatval);
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
ret = xmlXPathCastNodeSetToString(val->nodesetval);
break;
case XPATH_BOOLEAN:
ret = xmlXPathCastBooleanToString(val->boolval);
break;
case XPATH_STRING:
return(xmlStrdup(val->stringval));
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
// TODO
ret = xmlStrdup((const xmlChar *) "");
break;
}
return(ret);
}
/**
* xmlXPathConvertString:
* @val: an XPath object
*
* Converts an existing object to its string() equivalent
*
* Returns the new object, the old one is freed (or the operation
* is done directly on @val) or NULL if OOM
*/
xmlXPathObjectPtr
xmlXPathConvertString(xmlXPathObjectPtr val) {
xmlChar *res = NULL;
xmlXPathObjectPtr ret;
if (val == NULL)
return(xmlXPathNewCString(""));
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;
}
xmlXPathFreeObject(val);
if(OOM_FLAG) return(NULL);
if (res == NULL)
return(xmlXPathNewCString(""));
ret = xmlXPathWrapString(res);
if(OOM_FLAG)
{
xmlFree(res);
return(NULL);
}
return(ret);
}
/**
* xmlXPathCastBooleanToNumber:
* @val: a boolean
*
* Converts a boolean to its number value
*
* Returns the number value
*/
double
xmlXPathCastBooleanToNumber(int val) {
return val ? (1.0) :(0.0);
}
/**
* xmlXPathCastStringToNumber:
* @val: a string
*
* Converts a string to its number value
*
* Returns the number value
*
* OOM: Never
*/
double
xmlXPathCastStringToNumber(const xmlChar * val) {
return(xmlXPathStringEvalNumber(val));
}
/**
* xmlXPathCastNodeToNumber:
* @node: a node
*
* Converts a node to its number value
*
* Returns the number value
*/
double
xmlXPathCastNodeToNumber (xmlNodePtr node) {
xmlChar *strval;
double ret;
if (node == NULL)
return(xmlXPathNAN);
strval = xmlXPathCastNodeToString(node);
if (strval == NULL)
return(xmlXPathNAN);
ret = xmlXPathCastStringToNumber(strval);
xmlFree(strval);
return(ret);
}
/**
* xmlXPathCastNodeSetToNumber:
* @ns: a node-set
*
* Converts a node-set to its number value
*
* Returns the number value
*
* OOM: possible --> OOM flag must be checked
*/
double
xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns) {
xmlChar *str;
double ret;
if (ns == NULL)
return(xmlXPathNAN);
str = xmlXPathCastNodeSetToString(ns); // OOM is possible
if(str){
ret = xmlXPathCastStringToNumber(str);
xmlFree(str);
return(ret);
}
return 0;
}
/**
* xmlXPathCastToNumber:
* @val: an XPath object
*
* Converts an XPath object to its number value
*
* Returns the number value
*/
double
xmlXPathCastToNumber(xmlXPathObjectPtr val) {
double ret = 0.0;
if (val == NULL)
return(xmlXPathNAN);
switch (val->type) {
case XPATH_UNDEFINED:
#ifdef DEGUB_EXPR
xmlGenericError(xmlGenericErrorContext, "NUMBER: undefined\n");
#endif
ret = xmlXPathNAN;
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
ret = xmlXPathCastNodeSetToNumber(val->nodesetval);
break;
case XPATH_STRING:
ret = xmlXPathCastStringToNumber(val->stringval);
break;
case XPATH_NUMBER:
ret = val->floatval;
break;
case XPATH_BOOLEAN:
ret = xmlXPathCastBooleanToNumber(val->boolval);
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO;
ret = xmlXPathNAN;
break;
}
return(ret);
}
/**
* xmlXPathConvertNumber:
* @val: an XPath object
*
* Converts an existing object to its number() equivalent
*
* Returns the new object, the old one is freed (or the operation
* is done directly on @val)
*/
xmlXPathObjectPtr
xmlXPathConvertNumber(xmlXPathObjectPtr val) {
xmlXPathObjectPtr ret;
if (val == NULL)
return(xmlXPathNewFloat(0.0));
if (val->type == XPATH_NUMBER)
return(val);
ret = xmlXPathNewFloat(xmlXPathCastToNumber(val));
xmlXPathFreeObject(val);
return(ret);
}
/**
* xmlXPathCastNumberToBoolean:
* @val: a number
*
* Converts a number to its boolean value
*
* Returns the boolean value
*/
int
xmlXPathCastNumberToBoolean (double val) {
if (xmlXPathIsNaN(val) || (val == 0.0))
return(0);
return(1);
}
/**
* xmlXPathCastStringToBoolean:
* @val: a string
*
* Converts a string to its boolean value
*
* Returns the boolean value
*
* OOM: never
*/
int
xmlXPathCastStringToBoolean (const xmlChar *val) {
if ((val == NULL) || (xmlStrlen(val) == 0))
return(0);
return(1);
}
/**
* xmlXPathCastNodeSetToBoolean:
* @ns: a node-set
*
* Converts a node-set to its boolean value
*
* Returns the boolean value
*
* OOM: never
*/
int
xmlXPathCastNodeSetToBoolean (xmlNodeSetPtr ns) {
if ((ns == NULL) || (ns->nodeNr == 0))
return(0);
return(1);
}
/**
* xmlXPathCastToBoolean:
* @val: an XPath object
*
* Converts an XPath object to its boolean value
*
* Returns the boolean value
*/
int
xmlXPathCastToBoolean (xmlXPathObjectPtr val) {
int ret = 0;
if (val == NULL)
return(0);
switch (val->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext, "BOOLEAN: undefined\n");
#endif
ret = 0;
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
ret = xmlXPathCastNodeSetToBoolean(val->nodesetval);
break;
case XPATH_STRING:
ret = xmlXPathCastStringToBoolean(val->stringval);
break;
case XPATH_NUMBER:
ret = xmlXPathCastNumberToBoolean(val->floatval);
break;
case XPATH_BOOLEAN:
ret = val->boolval;
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO;
ret = 0;
break;
}
return(ret);
}
/**
* xmlXPathConvertBoolean:
* @val: an XPath object
*
* Converts an existing object to its boolean() equivalent
*
* Returns the new object, the old one is freed (or the operation
* is done directly on @val)
*/
xmlXPathObjectPtr
xmlXPathConvertBoolean(xmlXPathObjectPtr val) {
xmlXPathObjectPtr ret;
if (val == NULL)
return(xmlXPathNewBoolean(0));
if (val->type == XPATH_BOOLEAN)
return(val);
ret = xmlXPathNewBoolean(xmlXPathCastToBoolean(val));
xmlXPathFreeObject(val);
return(ret);
}
/************************************************************************
* *
* Routines to handle XPath contexts *
* *
************************************************************************/
#ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED
/**
* xmlHashCopier:
* typedef void *(*xmlHashCopier)(void *payload, xmlChar *name);
*/
void* xeSimpleHashEntryCopier(void *payload, xmlChar* name )
{
return payload; // just return the same value
}
#endif
/**
* xmlXPathNewContext:
* @doc: the XML document
*
* Create a new xmlXPathContext
*
* Returns the xmlXPathContext just allocated. The caller will need to free it.
*/
xmlXPathContextPtr
xmlXPathNewContext(xmlDocPtr doc) {
xmlXPathContextPtr ret;
xmlHashTablePtr gFuncHash;
ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
if (ret == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("creating context\n"));
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
ret->doc = doc; // this is a context node in general, not always a document node
// XMLENGINE: these are nullified already
//ret->node = NULL;
//ret->varHash = NULL;
//ret->nb_types = 0;
//ret->max_types = 0;
//ret->types = NULL;
#ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED
// XMLENGINE: BEGIN NEW CODE
// xmlXPathDefaultFunctionsHash is the new field in xmlGlobalState structure
gFuncHash = xmlXPathDefaultFunctionsHash;
if(!gFuncHash)
{ // global reusable hash table was not initialized yet
if( xmlXPathRegisterAllFunctions(ret) < 0 )
{
xmlFree(ret);
return(NULL);
}
if(xmlXPathDefineExtensionFunctionsGlobally)
{
xmlXPathDefaultFunctionsHash = ret->funcHash;
}
else
{
xmlXPathDefaultFunctionsHash = xmlHashCopy(ret->funcHash, xeSimpleHashEntryCopier);
}
}
else
{ // copy previously initialized hash table OR
// reuse the global hash, then, no cleanup is needed when
// XPath context is freed
if(xmlXPathDefineExtensionFunctionsGlobally)
{
ret->funcHash = gFuncHash;
}
else
{
ret->funcHash = xmlHashCopy( gFuncHash, xeSimpleHashEntryCopier );
}
}
// XMLENGINE: END NEW CODE
#else
/* original code*/
ret->funcHash = xmlHashCreate(0);
xmlXPathRegisterAllFunctions(ret); //TODO: check OOM
#endif /* else !XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED */
// XMLENGINE: these are nullified already
//ret->nb_axis = 0;
//ret->max_axis = 0;
//ret->axis = NULL;
//ret->nsHash = NULL;
//ret->user = NULL;
ret->contextSize = -1;
ret->proximityPosition = -1;
return(ret);
}
/**
* xmlXPathFreeContext:
* @ctxt: the context to free
*
* Free up an xmlXPathContext
*/
void
xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
// TODO: Check will this erase useful error info during ERR cleanup
// merge: i'm not sure if this is useful anymore...
if (&ctxt->lastError)
xmlResetError(&ctxt->lastError);
xmlXPathRegisteredNsCleanup(ctxt);
#ifdef XMLENGINE_XPATH_FUNC_HASH_OPTIMIZED
// DO NOTHING HERE if global hash table is reused.
// The function hash is destroyed only if it is not reused,
// otherwise the reference to it is stored in global:
// xmlXPathDefaultFunctionsHash variable
// and the hash table is freed during XML Engine cleanup with
// xeXPathCleanup()
if(!xmlXPathDefineExtensionFunctionsGlobally)
{
xmlXPathRegisteredFuncsCleanup(ctxt);
}
#else
xmlXPathRegisteredFuncsCleanup(ctxt);
#endif
xmlXPathRegisteredVariablesCleanup(ctxt);
xmlFree(ctxt);
}
/************************************************************************
* *
* Routines to handle XPath parser contexts *
* *
************************************************************************/
#define CHECK_CTXT(ctxt) \
if (ctxt == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
EMBED_ERRTXT("%s:%d Internal error: ctxt == NULL\n"), \
__FILE__, __LINE__); \
} \
// TODO: Quite big macro -- for release build it could be reduced by xmlGenericError calls
#define CHECK_CONTEXT(ctxt) \
if (ctxt == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
EMBED_ERRTXT("%s:%d Internal error: no context\n"), \
__FILE__, __LINE__); \
} \
else if (ctxt->doc == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
EMBED_ERRTXT("%s:%d Internal error: no document\n"), \
__FILE__, __LINE__); \
} \
else if (ctxt->doc->children == NULL) { \
xmlGenericError(xmlGenericErrorContext, \
EMBED_ERRTXT("%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) {
xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating parser context\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);
}
if ((ctxt != NULL) && (ctxt->dict != NULL)) {
ret->comp->dict = ctxt->dict;
xmlDictReference(ret->comp->dict);
}
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) {
xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\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);
xmlXPathErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\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 *
* *
************************************************************************/
/**
* xmlXPathNodeValHash:
* @node: a node pointer
*
* Function computing the beginning of the string value of the node,
* used to speed up comparisons
*
* Returns an int usable as a hash
*/
static unsigned int
xmlXPathNodeValHash(xmlNodePtr node) {
int len = 2;
const xmlChar * string = NULL;
xmlNodePtr tmp = NULL;
unsigned int ret = 0;
if (node == NULL)
return(0);
if (node->type == XML_DOCUMENT_NODE) {
tmp = xmlDocGetRootElement((xmlDocPtr) node);
if (tmp == NULL)
node = node->children;
else
node = tmp;
if (node == NULL)
return(0);
}
switch (node->type) {
case XML_COMMENT_NODE:
case XML_PI_NODE:
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
string = node->content;
if (string == NULL)
return(0);
if (string[0] == 0)
return(0);
return(((unsigned int) string[0]) +
(((unsigned int) string[1]) << 8));
case XML_NAMESPACE_DECL:
string = ((xmlNsPtr)node)->href;
if (string == NULL)
return(0);
if (string[0] == 0)
return(0);
return(((unsigned int) string[0]) +
(((unsigned int) string[1]) << 8));
case XML_ATTRIBUTE_NODE:
tmp = ((xmlAttrPtr) node)->children;
break;
case XML_ELEMENT_NODE:
tmp = node->children;
break;
default:
return(0);
}
while (tmp != NULL) {
switch (tmp->type) {
case XML_COMMENT_NODE:
case XML_PI_NODE:
case XML_CDATA_SECTION_NODE:
case XML_TEXT_NODE:
string = tmp->content;
break;
case XML_NAMESPACE_DECL:
string = ((xmlNsPtr)tmp)->href;
break;
default:
break;
}
if ((string != NULL) && (string[0] != 0)) {
if (len == 1) {
return(ret + (((unsigned int) string[0]) << 8));
}
if (string[1] == 0) {
len = 1;
ret = (unsigned int) string[0];
} else {
return(((unsigned int) string[0]) +
(((unsigned int) string[1]) << 8));
}
}
/*
* Skip to next node
*/
if ((tmp->children != NULL) && (tmp->type != XML_DTD_NODE)) {
if (tmp->children->type != XML_ENTITY_DECL) {
tmp = tmp->children;
continue;
}
}
if (tmp == node)
break;
if (tmp->next != NULL) {
tmp = tmp->next;
continue;
}
do {
tmp = tmp->parent;
if (tmp == NULL)
break;
if (tmp == node) {
tmp = NULL;
break;
}
if (tmp->next != NULL) {
tmp = tmp->next;
break;
}
} while (tmp != NULL);
}
return(ret);
}
/**
* xmlXPathStringHash:
* @string: a string
*
* Function computing the beginning of the string value of the node,
* used to speed up comparisons
*
* Returns an int usable as a hash
*/
static unsigned int
xmlXPathStringHash(const xmlChar * string) {
if (string == NULL)
return((unsigned int) 0);
if (string[0] == 0)
return(0);
return(((unsigned int) string[0]) +
(((unsigned int) string[1]) << 8));
}
/**
* 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 = xmlXPathCastNodeToString(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 = xmlXPathCastNodeToString(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:
* @inf: less than (1) or greater than (0)
* @strict: is the comparison strict
* @arg1: the first 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;
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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(0);
}
for (i = 0;i < ns1->nodeNr;i++) {
val1 = xmlXPathCastNodeToNumber(ns1->nodeTab[i]);
if (xmlXPathIsNaN(val1))
continue;
for (j = 0;j < ns2->nodeNr;j++) {
if (init == 0) {
values2[j] = xmlXPathCastNodeToNumber(ns2->nodeTab[j]);
}
if (xmlXPathIsNaN(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);
}
/**
* 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);
}
/**
* xmlXPathEqualNodeSetString:
* @arg: the nodeset object argument
* @str: the string to compare to.
* @neq: flag to show whether for '=' (0) or '!=' (1)
*
* 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 neq)
{
int i;
xmlNodeSetPtr ns;
xmlChar *str2;
unsigned int hash;
if ((str == NULL) || (arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
return (0);
ns = arg->nodesetval;
/*
* A NULL nodeset compared with a string is always false
* (since there is no node equal, and no node not equal)
*/
if ((ns == NULL) || (ns->nodeNr <= 0) )
return (0);
hash = xmlXPathStringHash(str);
for (i = 0; i < ns->nodeNr; i++) {
if (xmlXPathNodeValHash(ns->nodeTab[i]) == hash) {
str2 = xmlNodeGetContent(ns->nodeTab[i]);
if ((str2 != NULL) && (xmlStrEqual(str, str2))) {
xmlFree(str2);
if (neq)
continue;
return (1);
} else if ((str2 == NULL) && (xmlStrEqual(str, BAD_CAST ""))) {
if (neq)
continue;
return (1);
} else if (neq) {
if (str2 != NULL)
xmlFree(str2);
return (1);
}
if (str2 != NULL)
xmlFree(str2);
} else if (neq)
return (1);
}
return (0);
}
/**
* xmlXPathEqualNodeSetFloat:
* @arg: the nodeset object argument
* @f: the float to compare to
* @neq: flag to show whether to compare '=' (0) or '!=' (1)
*
* 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(xmlXPathParserContextPtr ctxt,
xmlXPathObjectPtr arg, double f, int neq) {
int i, ret=0;
xmlNodeSetPtr ns;
xmlChar *str2;
xmlXPathObjectPtr val;
double v;
if ((arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE)))
return(0);
ns = arg->nodesetval;
if (ns != NULL) {
for (i=0;i<ns->nodeNr;i++) {
str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
if (str2 != NULL) {
valuePush(ctxt, xmlXPathNewString(str2));
xmlFree(str2);
xmlXPathNumberFunction(ctxt, 1);
val = valuePop(ctxt);
v = val->floatval;
xmlXPathFreeObject(val);
if (!xmlXPathIsNaN(v)) {
if ((!neq) && (v==f)) {
ret = 1;
break;
} else if ((neq) && (v!=f)) {
ret = 1;
break;
}
}
}
}
}
return(ret);
}
/**
* xmlXPathEqualNodeSets:
* @arg1: first nodeset object argument
* @arg2: second nodeset object argument
* @neq: flag to show whether to test '=' (0) or '!=' (1)
*
* Implement the equal / not equal operation on XPath nodesets:
* @arg1 == @arg2 or @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 neq) {
int i, j;
unsigned int *hashs1;
unsigned int *hashs2;
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);
/*
* for equal, check if there is a node pertaining to both sets
*/
if (neq == 0)
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) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
return(0);
}
hashs1 = (unsigned int *) xmlMalloc(ns1->nodeNr * sizeof(unsigned int));
if (hashs1 == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
xmlFree(values1);
return(0);
}
memset(values1, 0, ns1->nodeNr * sizeof(xmlChar *));
values2 = (xmlChar **) xmlMalloc(ns2->nodeNr * sizeof(xmlChar *));
if (values2 == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
xmlFree(hashs1);
xmlFree(values1);
return(0);
}
hashs2 = (unsigned int *) xmlMalloc(ns2->nodeNr * sizeof(unsigned int));
if (hashs2 == NULL) {
xmlXPathErrMemory(NULL, EMBED_ERRTXT("comparing nodesets\n"));
xmlFree(hashs1);
xmlFree(values1);
xmlFree(values2);
return(0);
}
memset(values2, 0, ns2->nodeNr * sizeof(xmlChar *));
for (i = 0;i < ns1->nodeNr;i++) {
hashs1[i] = xmlXPathNodeValHash(ns1->nodeTab[i]);
for (j = 0;j < ns2->nodeNr;j++) {
if (i == 0)
hashs2[j] = xmlXPathNodeValHash(ns2->nodeTab[j]);
if (hashs1[i] != hashs2[j]) {
if (neq) {
ret = 1;
break;
}
}
else {
if (values1[i] == NULL)
values1[i] = xmlNodeGetContent(ns1->nodeTab[i]);
if (values2[j] == NULL)
values2[j] = xmlNodeGetContent(ns2->nodeTab[j]);
ret = xmlStrEqual(values1[i], values2[j]) ^ neq;
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);
xmlFree(hashs1);
xmlFree(hashs2);
return(ret);
}
static int
xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
int ret = 0;
/*
*At this point we are assured neither arg1 nor arg2
*is a nodeset, so we can just pick the appropriate routine.
*/
switch (arg1->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_BOOLEAN:
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
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:
ret = (arg1->boolval ==
xmlXPathCastNumberToBoolean(arg2->floatval));
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;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
break;
}
break;
case XPATH_NUMBER:
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_BOOLEAN:
ret = (arg2->boolval==
xmlXPathCastNumberToBoolean(arg1->floatval));
break;
case XPATH_STRING:
valuePush(ctxt, arg2);
xmlXPathNumberFunction(ctxt, 1);
arg2 = valuePop(ctxt);
/* no break on purpose */
case XPATH_NUMBER:
/* Hand check NaN and Infinity equalities */
if (xmlXPathIsNaN(arg1->floatval) ||
xmlXPathIsNaN(arg2->floatval)) {
ret = 0;
} else if (xmlXPathIsInf(arg1->floatval) == 1) {
if (xmlXPathIsInf(arg2->floatval) == 1)
ret = 1;
else
ret = 0;
} else if (xmlXPathIsInf(arg1->floatval) == -1) {
if (xmlXPathIsInf(arg2->floatval) == -1)
ret = 1;
else
ret = 0;
} else if (xmlXPathIsInf(arg2->floatval) == 1) {
if (xmlXPathIsInf(arg1->floatval) == 1)
ret = 1;
else
ret = 0;
} else if (xmlXPathIsInf(arg2->floatval) == -1) {
if (xmlXPathIsInf(arg1->floatval) == -1)
ret = 1;
else
ret = 0;
} else {
ret = (arg1->floatval == arg2->floatval);
}
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
break;
}
break;
case XPATH_STRING:
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
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);
/* Hand check NaN and Infinity equalities */
if (xmlXPathIsNaN(arg1->floatval) ||
xmlXPathIsNaN(arg2->floatval)) {
ret = 0;
} else if (xmlXPathIsInf(arg1->floatval) == 1) {
if (xmlXPathIsInf(arg2->floatval) == 1)
ret = 1;
else
ret = 0;
} else if (xmlXPathIsInf(arg1->floatval) == -1) {
if (xmlXPathIsInf(arg2->floatval) == -1)
ret = 1;
else
ret = 0;
} else if (xmlXPathIsInf(arg2->floatval) == 1) {
if (xmlXPathIsInf(arg1->floatval) == 1)
ret = 1;
else
ret = 0;
} else if (xmlXPathIsInf(arg2->floatval) == -1) {
if (xmlXPathIsInf(arg1->floatval) == -1)
ret = 1;
else
ret = 0;
} else {
ret = (arg1->floatval == arg2->floatval);
}
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
break;
}
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
break;
}
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
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, argtmp;
int ret = 0;
arg2 = valuePop(ctxt);
arg1 = valuePop(ctxt);
if ((arg1 == NULL) || (arg2 == NULL)) {
if (arg1 != NULL)
xmlXPathFreeObject(arg1);
else
xmlXPathFreeObject(arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
if (arg1 == arg2) {
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: by pointer\n");
#endif
return(1);
}
/*
*If either argument is a nodeset, it's a 'special case'
*/
if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
(arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
/*
*Hack it to assure arg1 is the nodeset
*/
if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
argtmp = arg2;
arg2 = arg1;
arg1 = argtmp;
}
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"Equal: undefined\n");
#endif
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
ret = xmlXPathEqualNodeSets(arg1, arg2, 0);
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(ctxt, arg1, arg2->floatval, 0);
break;
case XPATH_STRING:
ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval, 0);
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
}
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(ret);
}
return (xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
}
/**
* xmlXPathNotEqualValues:
* @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
xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
xmlXPathObjectPtr arg1, arg2, argtmp;
int ret = 0;
arg2 = valuePop(ctxt);
arg1 = valuePop(ctxt);
if ((arg1 == NULL) || (arg2 == NULL)) {
if (arg1 != NULL)
xmlXPathFreeObject(arg1);
else
xmlXPathFreeObject(arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
if (arg1 == arg2) {
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"NotEqual: by pointer\n");
#endif
return(0);
}
/*
*If either argument is a nodeset, it's a 'special case'
*/
if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
(arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
/*
*Hack it to assure arg1 is the nodeset
*/
if ((arg1->type != XPATH_NODESET) && (arg1->type != XPATH_XSLT_TREE)) {
argtmp = arg2;
arg2 = arg1;
arg1 = argtmp;
}
switch (arg2->type) {
case XPATH_UNDEFINED:
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"NotEqual: undefined\n");
#endif
break;
case XPATH_NODESET:
case XPATH_XSLT_TREE:
ret = xmlXPathEqualNodeSets(arg1, arg2, 1);
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(ctxt, arg1, arg2->floatval, 1);
break;
case XPATH_STRING:
ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval,1);
break;
case XPATH_USERS:
case XPATH_POINT:
case XPATH_RANGE:
case XPATH_LOCATIONSET:
TODO
break;
}
xmlXPathFreeObject(arg1);
xmlXPathFreeObject(arg2);
return(ret);
}
return (!xmlXPathEqualValuesCommon(ctxt, arg1, arg2));
}
/**
* 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 comparison succeeded, 0 if it failed
*/
int
xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
int ret = 0, arg1i = 0, arg2i = 0;
xmlXPathObjectPtr arg1, arg2;
arg2 = valuePop(ctxt);
arg1 = valuePop(ctxt);
if ((arg1 == NULL) || (arg2 == NULL)) {
if (arg1 != NULL)
xmlXPathFreeObject(arg1);
else
xmlXPathFreeObject(arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
if ((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE) ||
(arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
if (((arg2->type == XPATH_NODESET) || (arg2->type == XPATH_XSLT_TREE)) &&
((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE))){
ret = xmlXPathCompareNodeSets(inf, strict, arg1, arg2);
} else {
if ((arg1->type == XPATH_NODESET) || (arg1->type == XPATH_XSLT_TREE)) {
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
*/
/* Hand check NaN and Infinity comparisons */
if (xmlXPathIsNaN(arg1->floatval) || xmlXPathIsNaN(arg2->floatval)) {
ret=0;
} else {
arg1i=xmlXPathIsInf(arg1->floatval);
arg2i=xmlXPathIsInf(arg2->floatval);
if (inf && strict) {
if ((arg1i == -1 && arg2i != -1) ||
(arg2i == 1 && arg1i != 1)) {
ret = 1;
} else if (arg1i == 0 && arg2i == 0) {
ret = (arg1->floatval < arg2->floatval);
} else {
ret = 0;
}
}
else if (inf && !strict) {
if (arg1i == -1 || arg2i == 1) {
ret = 1;
} else if (arg1i == 0 && arg2i == 0) {
ret = (arg1->floatval <= arg2->floatval);
} else {
ret = 0;
}
}
else if (!inf && strict) {
if ((arg1i == 1 && arg2i != 1) ||
(arg2i == -1 && arg1i != -1)) {
ret = 1;
} else if (arg1i == 0 && arg2i == 0) {
ret = (arg1->floatval > arg2->floatval);
} else {
ret = 0;
}
}
else if (!inf && !strict) {
if (arg1i == 1 || arg2i == -1) {
ret = 1;
} else if (arg1i == 0 && arg2i == 0) {
ret = (arg1->floatval >= arg2->floatval);
} else {
ret = 0;
}
}
}
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) {
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
if (xmlXPathIsNaN(ctxt->value->floatval))
ctxt->value->floatval=xmlXPathNAN;
else if (xmlXPathIsInf(ctxt->value->floatval) == 1)
ctxt->value->floatval=xmlXPathNINF;
else if (xmlXPathIsInf(ctxt->value->floatval) == -1)
ctxt->value->floatval=xmlXPathPINF;
else if (ctxt->value->floatval == 0) {
if (xmlXPathGetSign(ctxt->value->floatval) == 0)
ctxt->value->floatval = xmlXPathNZERO;
else
ctxt->value->floatval = 0;
}
else
ctxt->value->floatval = - ctxt->value->floatval;
}
/**
* 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;
arg = valuePop(ctxt);
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
xmlXPathFreeObject(arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
ctxt->value->floatval += val;
}
/**
* xmlXPathSubValues:
* @ctxt: the XPath Parser context
*
* Implement the subtraction 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;
arg = valuePop(ctxt);
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
xmlXPathFreeObject(arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
ctxt->value->floatval -= val;
}
/**
* 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;
arg = valuePop(ctxt);
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
xmlXPathFreeObject(arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
ctxt->value->floatval *= val;
}
/**
* 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;
arg = valuePop(ctxt);
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
xmlXPathFreeObject(arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval))
ctxt->value->floatval = xmlXPathNAN;
else if (val == 0 && xmlXPathGetSign(val) != 0) {
if (ctxt->value->floatval == 0)
ctxt->value->floatval = xmlXPathNAN;
else if (ctxt->value->floatval > 0)
ctxt->value->floatval = xmlXPathNINF;
else if (ctxt->value->floatval < 0)
ctxt->value->floatval = xmlXPathPINF;
}
else if (val == 0) {
if (ctxt->value->floatval == 0)
ctxt->value->floatval = xmlXPathNAN;
else if (ctxt->value->floatval > 0)
ctxt->value->floatval = xmlXPathPINF;
else if (ctxt->value->floatval < 0)
ctxt->value->floatval = xmlXPathNINF;
} else
ctxt->value->floatval /= val;
}
/**
* 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) {
xmlXPathObjectPtr arg;
double arg1, arg2;
arg = valuePop(ctxt);
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
arg2 = xmlXPathCastToNumber(arg);
xmlXPathFreeObject(arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
arg1 = ctxt->value->floatval;
if (arg2 == 0)
ctxt->value->floatval = xmlXPathNAN;
else {
ctxt->value->floatval = fmod(arg1, arg2);
}
}
/************************************************************************
* *
* The traversal functions *
* *
************************************************************************/
/*
* A traversal function enumerates nodes along an axis.
* Initially it must be called with NULL, and it indicates
* termination on the axis by returning NULL.
*/
typedef xmlNodePtr (*xmlXPathTraversalFunction)
(xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
/**
* xmlXPathNextSelf:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "self" direction
* The self axis contains just the context node itself
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (cur == NULL)
return(ctxt->context->node);
return(NULL);
}
/**
* xmlXPathNextChild:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "child" direction
* The child axis contains the children of the context node in document order.
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (cur == NULL) {
if (ctxt->context->node == NULL) return(NULL);
switch (ctxt->context->node->type) {
case XML_ELEMENT_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
return(ctxt->context->node->children);
case XML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return(((xmlDocPtr) ctxt->context->node)->children);
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_ATTRIBUTE_NODE:
case XML_NAMESPACE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
return(NULL);
}
return(NULL);
}
if ((cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE))
return(NULL);
return(cur->next);
}
/**
* xmlXPathNextDescendant:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "descendant" direction
* the descendant axis contains the descendants of the context node in document
* order; a descendant is a child or a child of a child and so on.
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (cur == NULL) {
if (ctxt->context->node == NULL)
return(NULL);
if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
(ctxt->context->node->type == XML_NAMESPACE_DECL))
return(NULL);
if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
return(ctxt->context->doc->children);
return(ctxt->context->node->children);
}
if (cur->children != NULL) {
/*
* Do not descend on entities declarations
*/
if (cur->children->type != XML_ENTITY_DECL) {
cur = cur->children;
/*
* Skip DTDs
*/
if (cur->type != XML_DTD_NODE)
return(cur);
}
}
if (cur == ctxt->context->node) return(NULL);
while (cur->next != NULL) {
cur = cur->next;
if ((cur->type != XML_ENTITY_DECL) &&
(cur->type != XML_DTD_NODE))
return(cur);
}
do {
cur = cur->parent;
if (cur == NULL) return(NULL);
if (cur == ctxt->context->node) return(NULL);
if (cur->next != NULL) {
cur = cur->next;
return(cur);
}
} while (cur != NULL);
return(cur);
}
/**
* xmlXPathNextDescendantOrSelf:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "descendant-or-self" direction
* the descendant-or-self axis contains the context node and the descendants
* of the context node in document order; thus the context node is the first
* node on the axis, and the first child of the context node is the second node
* on the axis
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (cur == NULL) {
if (ctxt->context->node == NULL)
return(NULL);
if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
(ctxt->context->node->type == XML_NAMESPACE_DECL))
return(NULL);
return(ctxt->context->node);
}
return(xmlXPathNextDescendant(ctxt, cur));
}
/**
* xmlXPathNextParent:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "parent" direction
* The parent axis contains the parent of the context node, if there is one.
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
/*
* the parent of an attribute or namespace node is the element
* to which the attribute or namespace node is attached
* Namespace handling !!!
*/
if (cur == NULL) {
if (ctxt->context->node == NULL) return(NULL);
switch (ctxt->context->node->type) {
case XML_ELEMENT_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
case XML_ENTITY_DECL:
if (ctxt->context->node->parent == NULL)
return((xmlNodePtr) ctxt->context->doc);
if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
((ctxt->context->node->parent->name[0] == ' ') ||
(xmlStrEqual(ctxt->context->node->parent->name,
BAD_CAST "fake node libxslt"))))
return(NULL);
return(ctxt->context->node->parent);
case XML_ATTRIBUTE_NODE: {
xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
return(att->parent);
}
case XML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return(NULL);
case XML_NAMESPACE_DECL: {
xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
if ((ns->next != NULL) &&
(ns->next->type != XML_NAMESPACE_DECL))
return((xmlNodePtr) ns->next);
return(NULL);
}
}
}
return(NULL);
}
/**
* xmlXPathNextAncestor:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "ancestor" direction
* the ancestor axis contains the ancestors of the context node; the ancestors
* of the context node consist of the parent of context node and the parent's
* parent and so on; the nodes are ordered in reverse document order; thus the
* parent is the first node on the axis, and the parent's parent is the second
* node on the axis
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
/*
* the parent of an attribute or namespace node is the element
* to which the attribute or namespace node is attached
* !!!!!!!!!!!!!
*/
if (cur == NULL) {
if (ctxt->context->node == NULL) return(NULL);
switch (ctxt->context->node->type) {
case XML_ELEMENT_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_NOTATION_NODE:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
if (ctxt->context->node->parent == NULL)
return((xmlNodePtr) ctxt->context->doc);
if ((ctxt->context->node->parent->type == XML_ELEMENT_NODE) &&
((ctxt->context->node->parent->name[0] == ' ') ||
(xmlStrEqual(ctxt->context->node->parent->name,
BAD_CAST "fake node libxslt"))))
return(NULL);
return(ctxt->context->node->parent);
case XML_ATTRIBUTE_NODE: {
xmlAttrPtr tmp = (xmlAttrPtr) ctxt->context->node;
return(tmp->parent);
}
case XML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return(NULL);
case XML_NAMESPACE_DECL: {
xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
if ((ns->next != NULL) &&
(ns->next->type != XML_NAMESPACE_DECL))
return((xmlNodePtr) ns->next);
/* Bad, how did that namespace end up here ? */
return(NULL);
}
}
return(NULL);
}
if (cur == ctxt->context->doc->children)
return((xmlNodePtr) ctxt->context->doc);
if (cur == (xmlNodePtr) ctxt->context->doc)
return(NULL);
switch (cur->type) {
case XML_ELEMENT_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_ENTITY_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_NOTATION_NODE:
case XML_DTD_NODE:
case XML_ELEMENT_DECL:
case XML_ATTRIBUTE_DECL:
case XML_ENTITY_DECL:
case XML_XINCLUDE_START:
case XML_XINCLUDE_END:
if (cur->parent == NULL)
return(NULL);
if ((cur->parent->type == XML_ELEMENT_NODE) &&
((cur->parent->name[0] == ' ') ||
(xmlStrEqual(cur->parent->name,
BAD_CAST "fake node libxslt"))))
return(NULL);
return(cur->parent);
case XML_ATTRIBUTE_NODE: {
xmlAttrPtr att = (xmlAttrPtr) ctxt->context->node;
return(att->parent);
}
case XML_NAMESPACE_DECL: {
xmlNsPtr ns = (xmlNsPtr) ctxt->context->node;
if ((ns->next != NULL) &&
(ns->next->type != XML_NAMESPACE_DECL))
return((xmlNodePtr) ns->next);
/* Bad, how did that namespace end up here ? */
return(NULL);
}
case XML_DOCUMENT_NODE:
case XML_DOCUMENT_TYPE_NODE:
case XML_DOCUMENT_FRAG_NODE:
case XML_HTML_DOCUMENT_NODE:
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
#endif
return(NULL);
}
return(NULL);
}
/**
* xmlXPathNextAncestorOrSelf:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "ancestor-or-self" direction
* he ancestor-or-self axis contains the context node and ancestors of
* the context node in reverse document order; thus the context node is
* the first node on the axis, and the context node's parent the second;
* parent here is defined the same as with the parent axis.
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (cur == NULL)
return(ctxt->context->node);
return(xmlXPathNextAncestor(ctxt, cur));
}
/**
* xmlXPathNextFollowingSibling:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "following-sibling" direction
* The following-sibling axis contains the following siblings of the context
* node in document order.
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
(ctxt->context->node->type == XML_NAMESPACE_DECL))
return(NULL);
if (cur == (xmlNodePtr) ctxt->context->doc)
return(NULL);
if (cur == NULL)
return(ctxt->context->node->next);
return(cur->next);
}
/**
* xmlXPathNextPrecedingSibling:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "preceding-sibling" direction
* The preceding-sibling axis contains the preceding siblings of the context
* node in reverse document order; the first preceding sibling is first on the
* axis; the sibling preceding that node is the second on the axis and so on.
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if ((ctxt->context->node->type == XML_ATTRIBUTE_NODE) ||
(ctxt->context->node->type == XML_NAMESPACE_DECL))
return(NULL);
if (cur == (xmlNodePtr) ctxt->context->doc)
return(NULL);
if (cur == NULL)
return(ctxt->context->node->prev);
if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE)) {
cur = cur->prev;
if (cur == NULL)
return(ctxt->context->node->prev);
}
return(cur->prev);
}
/**
* xmlXPathNextFollowing:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "following" direction
* The following axis contains all nodes in the same document as the context
* node that are after the context node in document order, excluding any
* descendants and excluding attribute nodes and namespace nodes; the nodes
* are ordered in document order
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (cur != NULL && cur->children != NULL)
return cur->children ;
if (cur == NULL) cur = ctxt->context->node;
if (cur == NULL) return(NULL) ; /* ERROR */
if (cur->next != NULL) return(cur->next) ;
do {
cur = cur->parent;
if (cur == NULL) return(NULL);
if (cur == (xmlNodePtr) ctxt->context->doc) return(NULL);
if (cur->next != NULL) return(cur->next);
} while (cur != NULL);
return(cur);
}
/*
* xmlXPathIsAncestor:
* @ancestor: the ancestor node
* @node: the current node
*
* Check that @ancestor is a @node's ancestor
*
* returns 1 if @ancestor is a @node's ancestor, 0 otherwise.
*/
static int
xmlXPathIsAncestor(xmlNodePtr ancestor, xmlNodePtr node) {
if ((ancestor == NULL) || (node == NULL)) return(0);
/* nodes need to be in the same document */
if (ancestor->doc != node->doc) return(0);
/* avoid searching if ancestor or node is the root node */
if (ancestor == (xmlNodePtr) node->doc) return(1);
if (node == (xmlNodePtr) ancestor->doc) return(0);
while (node->parent != NULL) {
if (node->parent == ancestor)
return(1);
node = node->parent;
}
return(0);
}
/**
* xmlXPathNextPreceding:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "preceding" direction
* the preceding axis contains all nodes in the same document as the context
* node that are before the context node in document order, excluding any
* ancestors and excluding attribute nodes and namespace nodes; the nodes are
* ordered in reverse document order
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur)
{
if (cur == NULL)
cur = ctxt->context->node;
if (cur == NULL)
return (NULL);
if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
cur = cur->prev;
do {
if (cur->prev != NULL) {
for (cur = cur->prev; cur->last != NULL; cur = cur->last) ;
return (cur);
}
cur = cur->parent;
if (cur == NULL)
return (NULL);
if (cur == ctxt->context->doc->children)
return (NULL);
} while (xmlXPathIsAncestor(cur, ctxt->context->node));
return (cur);
}
/**
* xmlXPathNextPrecedingInternal:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
*
* Traversal function for the "preceding" direction
* the preceding axis contains all nodes in the same document as the context
* node that are before the context node in document order, excluding any
* ancestors and excluding attribute nodes and namespace nodes; the nodes are
* ordered in reverse document order
* This is a faster implementation but internal only since it requires a
* state kept in the parser context: ctxt->ancestor.
*
* Returns the next element following that axis
*/
static xmlNodePtr
xmlXPathNextPrecedingInternal(xmlXPathParserContextPtr ctxt,
xmlNodePtr cur)
{
if (cur == NULL) {
cur = ctxt->context->node;
if (cur == NULL)
return (NULL);
if (cur->type == XML_NAMESPACE_DECL)
cur = (xmlNodePtr)((xmlNsPtr)cur)->next;
ctxt->ancestor = cur->parent;
}
if ((cur->prev != NULL) && (cur->prev->type == XML_DTD_NODE))
cur = cur->prev;
while (cur->prev == NULL) {
cur = cur->parent;
if (cur == NULL)
return (NULL);
if (cur == ctxt->context->doc->children)
return (NULL);
if (cur != ctxt->ancestor)
return (cur);
ctxt->ancestor = cur->parent;
}
cur = cur->prev;
while (cur->last != NULL)
cur = cur->last;
return (cur);
}
/**
* xmlXPathNextNamespace:
* @ctxt: the XPath Parser context
* @cur: the current attribute in the traversal
*
* Traversal function for the "namespace" direction
* the namespace axis contains the namespace nodes of the context node;
* the order of nodes on this axis is implementation-defined; the axis will
* be empty unless the context node is an element
*
* We keep the XML namespace node at the end of the list.
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (ctxt->context->node->type != XML_ELEMENT_NODE)
return(NULL);
if (ctxt->context->tmpNsList == NULL &&
cur != (xmlNodePtr) xmlXPathXMLNamespace)
{
if (ctxt->context->tmpNsList != NULL)
xmlFree(ctxt->context->tmpNsList);
ctxt->context->tmpNsList =
xmlGetNsList(ctxt->context->doc, ctxt->context->node);
ctxt->context->tmpNsNr = 0;
if (ctxt->context->tmpNsList != NULL) {
while (ctxt->context->tmpNsList[ctxt->context->tmpNsNr] != NULL) {
ctxt->context->tmpNsNr++;
}
}
return((xmlNodePtr) xmlXPathXMLNamespace);
}
if (ctxt->context->tmpNsNr > 0) {
return (xmlNodePtr)ctxt->context->tmpNsList[--ctxt->context->tmpNsNr];
} else {
if (ctxt->context->tmpNsList != NULL)
xmlFree(ctxt->context->tmpNsList);
ctxt->context->tmpNsList = NULL;
return(NULL);
}
}
/**
* xmlXPathNextAttribute:
* @ctxt: the XPath Parser context
* @cur: the current attribute in the traversal
*
* Traversal function for the "attribute" direction
* TODO: support DTD inherited default attributes
*
* Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
if (ctxt->context->node == NULL)
return(NULL);
if (ctxt->context->node->type != XML_ELEMENT_NODE)
return(NULL);
if (cur == NULL) {
if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
return(NULL);
return((xmlNodePtr)ctxt->context->node->properties);
}
return((xmlNodePtr)cur->next);
}
/************************************************************************
* *
* NodeTest Functions *
* *
************************************************************************/
#define IS_FUNCTION 200
/************************************************************************
* *
* Implicit tree core function library *
* *
************************************************************************/
/**
* xmlXPathRoot:
* @ctxt: the XPath Parser context
*
* Initialize the context to the root of the document
*/
void
xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
}
/************************************************************************
* *
* The explicit core function library *
*http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html#corelib *
* *
************************************************************************/
/**
* xmlXPathLastFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the last() XPath function
* number last()
* The last function returns the number of nodes in the context node list.
*/
void
xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
if (ctxt->context->contextSize >= 0) {
valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"last() : %d\n", ctxt->context->contextSize);
#endif
} else {
XP_ERROR(XPATH_INVALID_CTXT_SIZE);
}
}
/**
* xmlXPathPositionFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the position() XPath function
* number position()
* The position function returns the position of the context node in the
* context node list. The first position is 1, and so the last position
* will be equal to last().
*/
void
xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
if (ctxt->context->proximityPosition >= 0) {
valuePush(ctxt,
xmlXPathNewFloat((double) ctxt->context->proximityPosition));
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
ctxt->context->proximityPosition);
#endif
} else {
XP_ERROR(XPATH_INVALID_CTXT_POSITION);
}
}
/**
* xmlXPathCountFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the count() XPath function
* number count(node-set)
*/
void
xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
CHECK_ARITY(1);
if ((ctxt->value == NULL) ||
((ctxt->value->type != XPATH_NODESET) &&
(ctxt->value->type != XPATH_XSLT_TREE)))
XP_ERROR(XPATH_INVALID_TYPE);
cur = valuePop(ctxt);
if ((cur == NULL) || (cur->nodesetval == NULL))
valuePush(ctxt, xmlXPathNewFloat((double) 0));
else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) {
valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
} else {
if ((cur->nodesetval->nodeNr != 1) ||
(cur->nodesetval->nodeTab == NULL)) {
valuePush(ctxt, xmlXPathNewFloat((double) 0));
} else {
xmlNodePtr tmp;
int i = 0;
tmp = cur->nodesetval->nodeTab[0];
if (tmp != NULL) {
tmp = tmp->children;
while (tmp != NULL) {
tmp = tmp->next;
i++;
}
}
valuePush(ctxt, xmlXPathNewFloat((double) i));
}
}
xmlXPathFreeObject(cur);
}
/**
* xmlXPathGetElementsByIds:
* @doc: the document
* @ids: a whitespace separated list of IDs
*
* Selects elements by their unique ID.
*
* Returns a node-set of selected elements.
*/
static xmlNodeSetPtr
xmlXPathGetElementsByIds (xmlDocPtr doc, const xmlChar *ids) {
xmlNodeSetPtr ret;
const xmlChar *cur = ids;
xmlChar *ID;
xmlAttrPtr attr;
xmlNodePtr elem = NULL;
if (ids == NULL) return(NULL);
ret = xmlXPathNodeSetCreate(NULL);
if ( !ret )
return NULL;
while (IS_BLANK_CH(*cur)) cur++;
while (*cur != 0) {
while ((!IS_BLANK_CH(*cur)) && (*cur != 0))
cur++;
ID = xmlStrndup(ids, cur - ids);
if (ID != NULL) {
/*
* We used to check the fact that the value passed
* was an NCName, but this generated much troubles for
* me and Aleksey Sanin, people blatantly violated that
* constaint, like Visa3D spec.
* if (xmlValidateNCName(ID, 1) == 0)
*/
attr = xmlGetID(doc, ID);
if (attr != NULL) {
if (attr->type == XML_ATTRIBUTE_NODE)
elem = attr->parent;
else if (attr->type == XML_ELEMENT_NODE)
elem = (xmlNodePtr) attr;
else
elem = NULL;
if (elem != NULL)
xmlXPathNodeSetAdd(ret, elem);
}
xmlFree(ID);
}
while (IS_BLANK_CH(*cur)) cur++;
ids = cur;
}
return(ret);
}
/**
* xmlXPathIdFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the id() XPath function
* node-set id(object)
* The id function selects elements by their unique ID
* (see [5.2.1 Unique IDs]). When the argument to id is of type node-set,
* then the result is the union of the result of applying id to the
* string value of each of the nodes in the argument node-set. When the
* argument to id is of any other type, the argument is converted to a
* string as if by a call to the string function; the string is split
* into a whitespace-separated list of tokens (whitespace is any sequence
* of characters matching the production S); the result is a node-set
* containing the elements in the same document as the context node that
* have a unique ID equal to any of the tokens in the list.
*/
void
xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlChar *tokens;
xmlNodeSetPtr ret;
xmlXPathObjectPtr obj;
xmlXPathObjectPtr wrappedNodeSet;
CHECK_ARITY(1);
obj = valuePop(ctxt);
if (obj == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
if ((obj->type == XPATH_NODESET) || (obj->type == XPATH_XSLT_TREE)) {
xmlNodeSetPtr ns;
int i;
ret = xmlXPathNodeSetCreate(NULL);
if (obj->nodesetval != NULL) {
for (i = 0; i < obj->nodesetval->nodeNr; i++) {
tokens =
xmlXPathCastNodeToString(obj->nodesetval->nodeTab[i]);
ns = xmlXPathGetElementsByIds(ctxt->context->doc, tokens);
ret = xmlXPathNodeSetMerge(ret, ns);
xmlXPathFreeNodeSet(ns);
if (tokens != NULL)
xmlFree(tokens);
}
}
xmlXPathFreeObject(obj);
valuePush(ctxt, xmlXPathWrapNodeSet(ret));
return;
}
obj = xmlXPathConvertString(obj);
ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
wrappedNodeSet = xmlXPathWrapNodeSet(ret);
if ( !wrappedNodeSet )
{
xmlXPathFreeNodeSet( ret );
}
valuePush(ctxt, wrappedNodeSet);
xmlXPathFreeObject(obj);
return;
}
/**
* xmlXPathLocalNameFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the local-name() XPath function
* string local-name(node-set?)
* The local-name function returns a string containing the local part
* of the name of the node in the argument node-set that is first in
* document order. If the node-set is empty or the first node has no
* name, an empty string is returned. If the argument is omitted it
* defaults to the context node.
*/
void
xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
if (nargs == 0) {
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
nargs = 1;
}
CHECK_ARITY(1);
if ((ctxt->value == NULL) ||
((ctxt->value->type != XPATH_NODESET) &&
(ctxt->value->type != XPATH_XSLT_TREE)))
XP_ERROR(XPATH_INVALID_TYPE);
cur = valuePop(ctxt);
if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
valuePush(ctxt, xmlXPathNewCString(""));
} else {
int i = 0; /* Should be first in document order !!!!! */
switch (cur->nodesetval->nodeTab[i]->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
case XML_PI_NODE:
if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
valuePush(ctxt, xmlXPathNewCString(""));
else
valuePush(ctxt,
xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
break;
case XML_NAMESPACE_DECL:
valuePush(ctxt, xmlXPathNewString(
((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
break;
default:
valuePush(ctxt, xmlXPathNewCString(""));
}
}
xmlXPathFreeObject(cur);
}
/**
* xmlXPathNamespaceURIFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the namespace-uri() XPath function
* string namespace-uri(node-set?)
* The namespace-uri function returns a string containing the
* namespace URI of the expanded name of the node in the argument
* node-set that is first in document order. If the node-set is empty,
* the first node has no name, or the expanded name has no namespace
* URI, an empty string is returned. If the argument is omitted it
* defaults to the context node.
*/
void
xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
if (nargs == 0) {
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
nargs = 1;
}
CHECK_ARITY(1);
if ((ctxt->value == NULL) ||
((ctxt->value->type != XPATH_NODESET) &&
(ctxt->value->type != XPATH_XSLT_TREE)))
XP_ERROR(XPATH_INVALID_TYPE);
cur = valuePop(ctxt);
if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
valuePush(ctxt, xmlXPathNewCString(""));
} else {
int i = 0; /* Should be first in document order !!!!! */
switch (cur->nodesetval->nodeTab[i]->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
if (cur->nodesetval->nodeTab[i]->ns == NULL)
valuePush(ctxt, xmlXPathNewCString(""));
else
valuePush(ctxt, xmlXPathNewString(
cur->nodesetval->nodeTab[i]->ns->href));
break;
default:
valuePush(ctxt, xmlXPathNewCString(""));
}
}
xmlXPathFreeObject(cur);
}
/**
* xmlXPathNameFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the name() XPath function
* string name(node-set?)
* The name function returns a string containing a QName representing
* the name of the node in the argument node-set that is first in document
* order. The QName must represent the name with respect to the namespace
* declarations in effect on the node whose name is being represented.
* Typically, this will be the form in which the name occurred in the XML
* source. This need not be the case if there are namespace declarations
* in effect on the node that associate multiple prefixes with the same
* namespace. However, an implementation may include information about
* the original prefix in its representation of nodes; in this case, an
* implementation can ensure that the returned string is always the same
* as the QName used in the XML source. If the argument it omitted it
* defaults to the context node.
* Libxml keep the original prefix so the "real qualified name" used is
* returned.
*/
static void
xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
{
xmlXPathObjectPtr cur;
if (nargs == 0) {
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
nargs = 1;
}
CHECK_ARITY(1);
if ((ctxt->value == NULL) ||
((ctxt->value->type != XPATH_NODESET) &&
(ctxt->value->type != XPATH_XSLT_TREE)))
XP_ERROR(XPATH_INVALID_TYPE);
cur = valuePop(ctxt);
if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
valuePush(ctxt, xmlXPathNewCString(""));
} else {
int i = 0; /* Should be first in document order !!!!! */
switch (cur->nodesetval->nodeTab[i]->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
valuePush(ctxt, xmlXPathNewCString(""));
else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
(cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
valuePush(ctxt,
xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
} else {
xmlChar *fullname;
fullname = xmlBuildQName(cur->nodesetval->nodeTab[i]->name,
cur->nodesetval->nodeTab[i]->ns->prefix,
NULL, 0);
if (fullname == cur->nodesetval->nodeTab[i]->name)
fullname = xmlStrdup(cur->nodesetval->nodeTab[i]->name);
if (fullname == NULL) {
XP_ERROR(XPATH_MEMORY_ERROR);
}
valuePush(ctxt, xmlXPathWrapString(fullname));
}
break;
default:
valuePush(ctxt,
xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
xmlXPathLocalNameFunction(ctxt, 1);
}
}
xmlXPathFreeObject(cur);
}
/**
* xmlXPathStringFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the string() XPath function
* string string(object?)
* The string function converts an object to a string as follows:
* - A node-set is converted to a string by returning the value of
* the node in the node-set that is first in document order.
* If the node-set is empty, an empty string is returned.
* - A number is converted to a string as follows
* + NaN is converted to the string NaN
* + positive zero is converted to the string 0
* + negative zero is converted to the string 0
* + positive infinity is converted to the string Infinity
* + negative infinity is converted to the string -Infinity
* + if the number is an integer, the number is represented in
* decimal form as a Number with no decimal point and no leading
* zeros, preceded by a minus sign (-) if the number is negative
* + otherwise, the number is represented in decimal form as a
* Number including a decimal point with at least one digit
* before the decimal point and at least one digit after the
* decimal point, preceded by a minus sign (-) if the number
* is negative; there must be no leading zeros before the decimal
* point apart possibly from the one required digit immediately
* before the decimal point; beyond the one required digit
* after the decimal point there must be as many, but only as
* many, more digits as are needed to uniquely distinguish the
* number from all other IEEE 754 numeric values.
* - The boolean false value is converted to the string false.
* The boolean true value is converted to the string true.
*
* If the argument is omitted, it defaults to a node-set with the
* context node as its only member.
*/
void
xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
if (nargs == 0) {
valuePush(ctxt,
xmlXPathWrapString(
xmlXPathCastNodeToString(ctxt->context->node)));
return;
}
CHECK_ARITY(1);
cur = valuePop(ctxt);
if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
cur = xmlXPathConvertString(cur);
if(OOM_FLAG) return;
valuePush(ctxt, cur);
}
/**
* xmlXPathStringLengthFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the string-length() XPath function
* number string-length(string?)
* The string-length returns the number of characters in the string
* (see [3.6 Strings]). If the argument is omitted, it defaults to
* the context node converted to a string, in other words the value
* of the context node.
*/
void
xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
if (nargs == 0) {
if (ctxt->context->node == NULL) {
valuePush(ctxt, xmlXPathNewFloat(0));
} else {
xmlChar *content;
content = xmlXPathCastNodeToString(ctxt->context->node);
valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
xmlFree(content);
}
return;
}
CHECK_ARITY(1);
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
cur = valuePop(ctxt);
valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
xmlXPathFreeObject(cur);
}
/**
* xmlXPathConcatFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the concat() XPath function
* string concat(string, string, string*)
* The concat function returns the concatenation of its arguments.
*/
void
xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur, newobj;
xmlChar *tmp;
if (nargs < 2) {
CHECK_ARITY(2);
}
CAST_TO_STRING;
cur = valuePop(ctxt);
if ((cur == NULL) || (cur->type != XPATH_STRING)) {
xmlXPathFreeObject(cur);
return;
}
nargs--;
while (nargs > 0) {
CAST_TO_STRING;
newobj = valuePop(ctxt);
if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
xmlXPathFreeObject(newobj);
xmlXPathFreeObject(cur);
XP_ERROR(XPATH_INVALID_TYPE);
}
tmp = xmlStrcat(newobj->stringval, cur->stringval);
newobj->stringval = cur->stringval;
cur->stringval = tmp;
xmlXPathFreeObject(newobj);
nargs--;
}
valuePush(ctxt, cur);
}
/**
* xmlXPathContainsFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the contains() XPath function
* boolean contains(string, string)
* The contains function returns true if the first argument string
* contains the second argument string, and otherwise returns false.
*/
void
xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr hay, needle;
CHECK_ARITY(2);
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
needle = valuePop(ctxt);
CAST_TO_STRING;
hay = valuePop(ctxt);
if ((hay == NULL) || (hay->type != XPATH_STRING)) {
xmlXPathFreeObject(hay);
xmlXPathFreeObject(needle);
XP_ERROR(XPATH_INVALID_TYPE);
}
if (xmlStrstr(hay->stringval, needle->stringval))
valuePush(ctxt, xmlXPathNewBoolean(1));
else
valuePush(ctxt, xmlXPathNewBoolean(0));
xmlXPathFreeObject(hay);
xmlXPathFreeObject(needle);
}
/**
* xmlXPathStartsWithFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the starts-with() XPath function
* boolean starts-with(string, string)
* The starts-with function returns true if the first argument string
* starts with the second argument string, and otherwise returns false.
*/
void
xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr hay, needle;
int n;
CHECK_ARITY(2);
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
needle = valuePop(ctxt);
CAST_TO_STRING;
hay = valuePop(ctxt);
if ((hay == NULL) || (hay->type != XPATH_STRING)) {
xmlXPathFreeObject(hay);
xmlXPathFreeObject(needle);
XP_ERROR(XPATH_INVALID_TYPE);
}
n = xmlStrlen(needle->stringval);
if (xmlStrncmp(hay->stringval, needle->stringval, n))
valuePush(ctxt, xmlXPathNewBoolean(0));
else
valuePush(ctxt, xmlXPathNewBoolean(1));
xmlXPathFreeObject(hay);
xmlXPathFreeObject(needle);
}
/**
* xmlXPathSubstringFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the substring() XPath function
* string substring(string, number, number?)
* The substring function returns the substring of the first argument
* starting at the position specified in the second argument with
* length specified in the third argument. For example,
* substring("12345",2,3) returns "234". If the third argument is not
* specified, it returns the substring starting at the position specified
* in the second argument and continuing to the end of the string. For
* example, substring("12345",2) returns "2345". More precisely, each
* character in the string (see [3.6 Strings]) is considered to have a
* numeric position: the position of the first character is 1, the position
* of the second character is 2 and so on. The returned substring contains
* those characters for which the position of the character is greater than
* or equal to the second argument and, if the third argument is specified,
* less than the sum of the second and third arguments; the comparisons
* and addition used for the above follow the standard IEEE 754 rules. Thus:
* - substring("12345", 1.5, 2.6) returns "234"
* - substring("12345", 0, 3) returns "12"
* - substring("12345", 0 div 0, 3) returns ""
* - substring("12345", 1, 0 div 0) returns ""
* - substring("12345", -42, 1 div 0) returns "12345"
* - substring("12345", -1 div 0, 1 div 0) returns ""
*/
void
xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr str, start, len;
double le=0, in;
// TODO: Name "l" is bad for a variable -- refactor it
int i, l, m;
xmlChar *ret;
if (nargs < 2) {
// TODO: Use of these CHECK_ARITY macros is bad ...
// -- make check manually OR define normal macro for that
CHECK_ARITY(2);
}
if (nargs > 3) {
CHECK_ARITY(3);
}
/*
* take care of possible last (position) argument
*/
if (nargs == 3) {
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
len = valuePop(ctxt);
le = len->floatval;
xmlXPathFreeObject(len);
}
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
start = valuePop(ctxt);
in = start->floatval;
xmlXPathFreeObject(start);
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
str = valuePop(ctxt);
m = xmlUTF8Strlen((const unsigned char *)str->stringval);
/*
* If last pos not present, calculate last position
*/
if (nargs != 3) {
le = (double)m;
if (in < 1.0)
in = 1.0;
}
/* Need to check for the special cases where either
* the index is NaN, the length is NaN, or both
* arguments are infinity (relying on Inf + -Inf = NaN)
*/
if (!xmlXPathIsNaN(in + le) && !xmlXPathIsInf(in)) {
/*
* To meet the requirements of the spec, the arguments
* must be converted to integer format before
* initial index calculations are done
*
* First we go to integer form, rounding up
* and checking for special cases
*/
i = (int) in;
if (((double)i)+0.5 <= in) i++;
if (xmlXPathIsInf(le) == 1) {
l = m;
if (i < 1)
i = 1;
}
else if (xmlXPathIsInf(le) == -1 || le < 0.0)
l = 0;
else {
l = (int) le;
if (((double)l)+0.5 <= le) l++;
}
/* Now we normalize inidices */
i -= 1;
l += i;
if (i < 0)
i = 0;
if (l > m)
l = m;
/* number of chars to copy */
l -= i;
ret = xmlUTF8Strsub(str->stringval, i, l);
}
else {
ret = NULL;
}
if (ret == NULL)
valuePush(ctxt, xmlXPathNewCString(""));
else {
valuePush(ctxt, xmlXPathNewString(ret));
xmlFree(ret);
}
xmlXPathFreeObject(str);
}
/**
* xmlXPathSubstringBeforeFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the substring-before() XPath function
* string substring-before(string, string)
* The substring-before function returns the substring of the first
* argument string that precedes the first occurrence of the second
* argument string in the first argument string, or the empty string
* if the first argument string does not contain the second argument
* string. For example, substring-before("1999/04/01","/") returns 1999.
*/
void
xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr str;
xmlXPathObjectPtr find;
xmlBufferPtr target;
const xmlChar *point;
int offset;
CHECK_ARITY(2);
CAST_TO_STRING;
find = valuePop(ctxt);
CAST_TO_STRING;
str = valuePop(ctxt);
target = xmlBufferCreate();
if (target) {
point = xmlStrstr(str->stringval, find->stringval);
if (point) {
offset = (int)(point - str->stringval);
xmlBufferAdd(target, str->stringval, offset);
}
valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
xmlBufferFree(target);
}
xmlXPathFreeObject(str);
xmlXPathFreeObject(find);
}
/**
* xmlXPathSubstringAfterFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the substring-after() XPath function
* string substring-after(string, string)
* The substring-after function returns the substring of the first
* argument string that follows the first occurrence of the second
* argument string in the first argument string, or the empty stringi
* if the first argument string does not contain the second argument
* string. For example, substring-after("1999/04/01","/") returns 04/01,
* and substring-after("1999/04/01","19") returns 99/04/01.
*/
void
xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr str;
xmlXPathObjectPtr find;
xmlBufferPtr target;
const xmlChar *point;
int offset;
CHECK_ARITY(2);
CAST_TO_STRING;
find = valuePop(ctxt);
CAST_TO_STRING;
str = valuePop(ctxt);
target = xmlBufferCreate();
if (target) {
point = xmlStrstr(str->stringval, find->stringval);
if (point) {
offset = (int)(point - str->stringval) + xmlStrlen(find->stringval);
xmlBufferAdd(target, &str->stringval[offset],
xmlStrlen(str->stringval) - offset);
}
valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
xmlBufferFree(target);
}
xmlXPathFreeObject(str);
xmlXPathFreeObject(find);
}
/**
* xmlXPathNormalizeFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the normalize-space() XPath function
* string normalize-space(string?)
* The normalize-space function returns the argument string with white
* space normalized by stripping leading and trailing whitespace
* and replacing sequences of whitespace characters by a single
* space. Whitespace characters are the same allowed by the S production
* in XML. If the argument is omitted, it defaults to the context
* node converted to a string, in other words the value of the context node.
*/
void
xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr obj = NULL;
xmlChar *source = NULL;
xmlBufferPtr target;
xmlChar blank;
if (nargs == 0) {
/* Use current context node */
valuePush(ctxt,
xmlXPathWrapString(
xmlXPathCastNodeToString(ctxt->context->node)));
nargs = 1;
}
CHECK_ARITY(1);
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
obj = valuePop(ctxt);
source = obj->stringval;
target = xmlBufferCreate();
if (target && source) {
/* Skip leading whitespaces */
while (IS_BLANK_CH(*source))
source++;
/* Collapse intermediate whitespaces, and skip trailing whitespaces */
blank = 0;
while (*source) {
if (IS_BLANK_CH(*source)) {
blank = 0x20;
} else {
if (blank) {
xmlBufferAdd(target, &blank, 1);
blank = 0;
}
xmlBufferAdd(target, source, 1);
}
source++;
}
valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
xmlBufferFree(target);
}
xmlXPathFreeObject(obj);
}
/**
* xmlXPathTranslateFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the translate() XPath function
* string translate(string, string, string)
* The translate function returns the first argument string with
* occurrences of characters in the second argument string replaced
* by the character at the corresponding position in the third argument
* string. For example, translate("bar","abc","ABC") returns the string
* BAr. If there is a character in the second argument string with no
* character at a corresponding position in the third argument string
* (because the second argument string is longer than the third argument
* string), then occurrences of that character in the first argument
* string are removed. For example, translate("--aaa--","abc-","ABC")
* returns "AAA". If a character occurs more than once in second
* argument string, then the first occurrence determines the replacement
* character. If the third argument string is longer than the second
* argument string, then excess characters are ignored.
*/
void
xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr str;
xmlXPathObjectPtr from;
xmlXPathObjectPtr to;
xmlBufferPtr target;
int offset, max;
xmlChar ch;
xmlChar *point;
xmlChar *cptr;
CHECK_ARITY(3);
CAST_TO_STRING;
to = valuePop(ctxt);
CAST_TO_STRING;
from = valuePop(ctxt);
CAST_TO_STRING;
str = valuePop(ctxt);
target = xmlBufferCreate();
if (target) {
max = xmlUTF8Strlen(to->stringval);
for (cptr = str->stringval; (ch=*cptr); ) {
offset = xmlUTF8Strloc(from->stringval, cptr);
if (offset >= 0) {
if (offset < max) {
point = xmlUTF8Strpos(to->stringval, offset);
if (point)
xmlBufferAdd(target, point, xmlUTF8Strsize(point, 1));
}
} else
xmlBufferAdd(target, cptr, xmlUTF8Strsize(cptr, 1));
/* Step to next character in input */
cptr++;
if ( ch & 0x80 ) {
/* if not simple ascii, verify proper format */
if ( (ch & 0xc0) != 0xc0 ) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n"));
break;
}
/* then skip over remaining bytes for this char */
while ( (ch <<= 1) & 0x80 )
if ( (*cptr++ & 0xc0) != 0x80 ) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathTranslateFunction: Invalid UTF8 string\n"));
break;
}
if (ch & 0x80) /* must have had error encountered */
break;
}
}
}
valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
xmlBufferFree(target);
xmlXPathFreeObject(str);
xmlXPathFreeObject(from);
xmlXPathFreeObject(to);
}
/**
* xmlXPathBooleanFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the boolean() XPath function
* boolean boolean(object)
* The boolean function converts its argument to a boolean as follows:
* - a number is true if and only if it is neither positive or
* negative zero nor NaN
* - a node-set is true if and only if it is non-empty
* - a string is true if and only if its length is non-zero
*/
void
xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
CHECK_ARITY(1);
cur = valuePop(ctxt);
if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
cur = xmlXPathConvertBoolean(cur);
valuePush(ctxt, cur);
}
/**
* xmlXPathNotFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the not() XPath function
* boolean not(boolean)
* The not function returns true if its argument is false,
* and false otherwise.
*/
void
xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(1);
CAST_TO_BOOLEAN;
CHECK_TYPE(XPATH_BOOLEAN);
ctxt->value->boolval = ! ctxt->value->boolval;
}
/**
* xmlXPathTrueFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the true() XPath function
* boolean true()
*/
void
xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
valuePush(ctxt, xmlXPathNewBoolean(1));
}
/**
* xmlXPathFalseFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the false() XPath function
* boolean false()
*/
void
xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
valuePush(ctxt, xmlXPathNewBoolean(0));
}
/**
* xmlXPathLangFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the lang() XPath function
* boolean lang(string)
* The lang function returns true or false depending on whether the
* language of the context node as specified by xml:lang attributes
* is the same as or is a sublanguage of the language specified by
* the argument string. The language of the context node is determined
* by the value of the xml:lang attribute on the context node, or, if
* the context node has no xml:lang attribute, by the value of the
* xml:lang attribute on the nearest ancestor of the context node that
* has an xml:lang attribute. If there is no such attribute, then lang
* returns false. If there is such an attribute, then lang returns
* true if the attribute value is equal to the argument ignoring case,
* or if there is some suffix starting with - such that the attribute
* value is equal to the argument ignoring that suffix of the attribute
* value and ignoring case.
*/
void
xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr val;
const xmlChar *theLang;
const xmlChar *lang;
int ret = 0;
int i;
CHECK_ARITY(1);
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
val = valuePop(ctxt);
lang = val->stringval;
theLang = xmlNodeGetLang(ctxt->context->node);
if ((theLang != NULL) && (lang != NULL)) {
for (i = 0;lang[i] != 0;i++)
if (toupper(lang[i]) != toupper(theLang[i]))
goto not_equal;
ret = 1;
}
not_equal:
if (theLang)
xmlFree((void *)theLang);
xmlXPathFreeObject(val);
valuePush(ctxt, xmlXPathNewBoolean(ret));
}
/**
* xmlXPathNumberFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the number() XPath function
* number number(object?)
*/
void
xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
double res;
if (nargs == 0) {
if (ctxt->context->node == NULL) {
valuePush(ctxt, xmlXPathNewFloat(0.0));
} else {
xmlChar* content = xmlNodeGetContent(ctxt->context->node);
res = xmlXPathStringEvalNumber(content);
valuePush(ctxt, xmlXPathNewFloat(res));
xmlFree(content);
}
return;
}
CHECK_ARITY(1);
cur = valuePop(ctxt);
cur = xmlXPathConvertNumber(cur);
valuePush(ctxt, cur);
}
/**
* xmlXPathSumFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the sum() XPath function
* number sum(node-set)
* The sum function returns the sum of the values of the nodes in
* the argument node-set.
*/
void
xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
int i;
double res = 0.0;
CHECK_ARITY(1);
if ((ctxt->value == NULL) ||
((ctxt->value->type != XPATH_NODESET) &&
(ctxt->value->type != XPATH_XSLT_TREE)))
XP_ERROR(XPATH_INVALID_TYPE);
cur = valuePop(ctxt);
if ((cur->nodesetval != NULL) && (cur->nodesetval->nodeNr != 0)) {
for (i = 0; i < cur->nodesetval->nodeNr; i++) {
res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
}
}
valuePush(ctxt, xmlXPathNewFloat(res));
xmlXPathFreeObject(cur);
}
/**
* xmlXPathFloorFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the floor() XPath function
* number floor(number)
* The floor function returns the largest (closest to positive infinity)
* number that is not greater than the argument and that is an integer.
*/
void
xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
double f;
CHECK_ARITY(1);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
f = (double)((int) ctxt->value->floatval);
if (f != ctxt->value->floatval) {
if (ctxt->value->floatval > 0)
ctxt->value->floatval = f;
else
ctxt->value->floatval = f - 1;
}
}
/**
* xmlXPathCeilingFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the ceiling() XPath function
* number ceiling(number)
* The ceiling function returns the smallest (closest to negative infinity)
* number that is not less than the argument and that is an integer.
*/
void
xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
double f;
CHECK_ARITY(1);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
#if 0
ctxt->value->floatval = ceil(ctxt->value->floatval);
#else
f = (double)((int) ctxt->value->floatval);
if (f != ctxt->value->floatval) {
if (ctxt->value->floatval > 0)
ctxt->value->floatval = f + 1;
else {
if (ctxt->value->floatval < 0 && f == 0)
ctxt->value->floatval = xmlXPathNZERO;
else
ctxt->value->floatval = f;
}
}
#endif
}
/**
* xmlXPathRoundFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the round() XPath function
* number round(number)
* The round function returns the number that is closest to the
* argument and that is an integer. If there are two such numbers,
* then the one that is even is returned.
*/
void
xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
double f;
CHECK_ARITY(1);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
if ((xmlXPathIsNaN(ctxt->value->floatval)) ||
(xmlXPathIsInf(ctxt->value->floatval) == 1) ||
(xmlXPathIsInf(ctxt->value->floatval) == -1) ||
(ctxt->value->floatval == 0.0))
return;
f = (double)((int) ctxt->value->floatval);
if (ctxt->value->floatval < 0) {
if (ctxt->value->floatval < f - 0.5)
ctxt->value->floatval = f - 1;
else
ctxt->value->floatval = f;
if (ctxt->value->floatval == 0)
ctxt->value->floatval = xmlXPathNZERO;
} else {
if (ctxt->value->floatval < f + 0.5)
ctxt->value->floatval = f;
else
ctxt->value->floatval = f + 1;
}
}
/************************************************************************
* *
* The Parser *
* *
************************************************************************/
/*
* a few forward declarations since we use a recursive call based
* implementation.
*/
static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
static xmlChar * xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt,
int qualified);
/**
* xmlXPathCurrentChar:
* @ctxt: the XPath parser context
* @cur: pointer to the beginning of the char
* @len: pointer to the length of the char read
*
* The current char value, if using UTF-8 this may actually span multiple
* bytes in the input buffer.
*
* Returns the current char value and its length
*/
static int
xmlXPathCurrentChar(xmlXPathParserContextPtr ctxt, int *len) {
unsigned char c;
unsigned int val;
const xmlChar *cur;
if (ctxt == NULL)
return(0);
cur = ctxt->cur;
/*
* We are supposed to handle UTF8, check it's valid
* From rfc2044: encoding of the Unicode values on UTF-8:
*
* UCS-4 range (hex.) UTF-8 octet sequence (binary)
* 0000 0000-0000 007F 0xxxxxxx
* 0000 0080-0000 07FF 110xxxxx 10xxxxxx
* 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
*
* Check for the 0x110000 limit too
*/
c = *cur;
if (c & 0x80) {
if ((cur[1] & 0xc0) != 0x80)
goto encoding_error;
if ((c & 0xe0) == 0xe0) {
if ((cur[2] & 0xc0) != 0x80)
goto encoding_error;
if ((c & 0xf0) == 0xf0) {
if (((c & 0xf8) != 0xf0) ||
((cur[3] & 0xc0) != 0x80))
goto encoding_error;
/* 4-byte code */
*len = 4;
val = (cur[0] & 0x7) << 18;
val |= (cur[1] & 0x3f) << 12;
val |= (cur[2] & 0x3f) << 6;
val |= cur[3] & 0x3f;
} else {
/* 3-byte code */
*len = 3;
val = (cur[0] & 0xf) << 12;
val |= (cur[1] & 0x3f) << 6;
val |= cur[2] & 0x3f;
}
} else {
/* 2-byte code */
*len = 2;
val = (cur[0] & 0x1f) << 6;
val |= cur[1] & 0x3f;
}
if (!IS_CHAR(val)) {
XP_ERROR0(XPATH_INVALID_CHAR_ERROR);
}
return(val);
} else {
/* 1-byte code */
*len = 1;
return((int) *cur);
}
encoding_error:
/*
* If we detect an UTF8 error that probably means that the
* input encoding didn't get properly advertised in the
* declaration header. Report the error and switch the encoding
* to ISO-Latin-1 (if you don't like this policy, just declare the
* encoding !)
*/
*len = 0;
XP_ERROR0(XPATH_ENCODING_ERROR);
}
/**
* xmlXPathParseNCName:
* @ctxt: the XPath Parser context
*
* parse an XML namespace non qualified name.
*
* [NS 3] NCName ::= (Letter | '_') (NCNameChar)*
*
* [NS 4] NCNameChar ::= Letter | Digit | '.' | '-' | '_' |
* CombiningChar | Extender
*
* Returns the namespace name or NULL
*/
xmlChar *
xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
const xmlChar *in;
xmlChar *ret;
int count = 0;
/*
* Accelerator for simple ASCII names
*/
in = ctxt->cur;
if (((*in >= 0x61) && (*in <= 0x7A)) ||
((*in >= 0x41) && (*in <= 0x5A)) ||
(*in == '_')) {
in++;
while (((*in >= 0x61) && (*in <= 0x7A)) ||
((*in >= 0x41) && (*in <= 0x5A)) ||
((*in >= 0x30) && (*in <= 0x39)) ||
(*in == '_') || (*in == '.') ||
(*in == '-'))
in++;
if ((*in == ' ') || (*in == '>') || (*in == '/') ||
(*in == '[') || (*in == ']') || (*in == ':') ||
(*in == '@') || (*in == '*')) {
count = in - ctxt->cur;
if (count == 0)
return(NULL);
ret = xmlStrndup(ctxt->cur, count);
if(OOM_FLAG) return(NULL);
ctxt->cur = in;
return(ret);
}
}
return(xmlXPathParseNameComplex(ctxt, 0));
}
/**
* xmlXPathParseQName:
* @ctxt: the XPath Parser context
* @prefix: a xmlChar **
*
* parse an XML qualified name
*
* [NS 5] QName ::= (Prefix ':')? LocalPart
*
* [NS 6] Prefix ::= NCName
*
* [NS 7] LocalPart ::= NCName
*
* Returns the function returns the local part, and prefix is updated
* to get the Prefix if any.
*/
static xmlChar *
xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
xmlChar *ret = NULL;
*prefix = NULL;
ret = xmlXPathParseNCName(ctxt);
if (CUR == ':') {
*prefix = ret;
NEXT;
ret = xmlXPathParseNCName(ctxt);
}
return(ret);
}
/**
* xmlXPathParseName:
* @ctxt: the XPath Parser context
*
* parse an XML name
*
* [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
* CombiningChar | Extender
*
* [5] Name ::= (Letter | '_' | ':') (NameChar)*
*
* Returns the namespace name or NULL
*/
xmlChar *
xmlXPathParseName(xmlXPathParserContextPtr ctxt) {
const xmlChar *in;
xmlChar *ret;
int count = 0;
/*
* Accelerator for simple ASCII names
*/
in = ctxt->cur;
if (((*in >= 0x61) && (*in <= 0x7A)) ||
((*in >= 0x41) && (*in <= 0x5A)) ||
(*in == '_') || (*in == ':')) {
in++;
while (((*in >= 0x61) && (*in <= 0x7A)) ||
((*in >= 0x41) && (*in <= 0x5A)) ||
((*in >= 0x30) && (*in <= 0x39)) ||
(*in == '_') || (*in == '-') ||
(*in == ':') || (*in == '.'))
in++;
if ((*in > 0) && (*in < 0x80)) {
count = in - ctxt->cur;
ret = xmlStrndup(ctxt->cur, count);
ctxt->cur = in;
return(ret);
}
}
return(xmlXPathParseNameComplex(ctxt, 1));
}
/*
* OOM: possible --> check OOM flag
*/
static xmlChar*
xmlXPathParseNameComplex(xmlXPathParserContextPtr ctxt, int qualified)
{
xmlChar buf[XML_MAX_NAMELEN + 5];
int len = 0, l; // TODO: rename 'l' variable
int c;
/*
* Handler for more complex cases
*/
c = CUR_CHAR(l);
if ((c == ' ') || (c == '>') || (c == '/') || /* accelerators */
(c == '[') || (c == ']') || (c == '@') || /* accelerators */
(c == '*') || /* accelerators */
(!IS_LETTER(c) &&
(c != '_') &&
qualified &&
(c != ':')
)
)
{
return(NULL);
}
while((c != ' ') && (c != '>') && (c != '/')
&& /* test bigname.xml */
(
IS_LETTER(c) ||
IS_DIGIT(c) ||
(c == '.') ||
(c == '-') ||
(c == '_') ||
(qualified && (c == ':')) ||
IS_COMBINING(c) ||
IS_EXTENDER(c)
)
)
{
COPY_BUF(l,buf,len,c);
NEXTL(l);
c = CUR_CHAR(l);
if (len >= XML_MAX_NAMELEN)
{
/*
* Okay someone managed to make a huge name, so he's ready to pay
* for the processing speed.
*/
xmlChar* buffer;
int max = len * 2;
buffer = (xmlChar*) xmlMallocAtomic(max * sizeof(xmlChar));
if (!buffer) {
XP_ERROR0(XPATH_MEMORY_ERROR);
}
memcpy(buffer, buf, len);
while ((IS_LETTER(c)) || (IS_DIGIT(c)) || /* test bigname.xml */
(c == '.') || (c == '-') ||
(c == '_') || ((qualified) && (c == ':')) ||
(IS_COMBINING(c)) ||
(IS_EXTENDER(c)))
{
if (len + 10 > max)
{
xmlChar* tmp;
max *= 2;
// DONE: Fix xmlRealloc
tmp = (xmlChar*) xmlRealloc(buffer, max * sizeof(xmlChar));
if (!tmp) {
xmlFree(buffer);
XP_ERROR0(XPATH_MEMORY_ERROR);
}
buffer = tmp;
}
COPY_BUF(l,buffer,len,c);
NEXTL(l);
c = CUR_CHAR(l);
}
buffer[len] = 0;
return(buffer);
}
}
if (len == 0)
return(NULL);
return(xmlStrndup(buf, len));
}
#define MAX_FRAC 20
/*
* These are used as divisors for the fractional part of a number.
* Since the table includes 1.0 (representing '0' fractional digits),
* it must be dimensioned at MAX_FRAC+1 (bug 133921)
*/
static const double my_pow10[MAX_FRAC+1] = {
1.0, 10.0, 100.0, 1000.0, 10000.0,
100000.0, 1000000.0, 10000000.0, 100000000.0, 1000000000.0,
10000000000.0, 100000000000.0, 1000000000000.0, 10000000000000.0,
100000000000000.0,
1000000000000000.0, 10000000000000000.0, 100000000000000000.0,
1000000000000000000.0, 10000000000000000000.0, 100000000000000000000.0
};
/**
* xmlXPathStringEvalNumber:
* @str: A string to scan
*
* [30a] Float ::= Number ('e' Digits?)?
*
* [30] Number ::= Digits ('.' Digits?)?
* | '.' Digits
* [31] Digits ::= [0-9]+
*
* Compile a Number in the string
* In complement of the Number expression, this function also handles
* negative values : '-' Number.
*
* Returns the double value.
*
* OOM: never
*/
double
xmlXPathStringEvalNumber(const xmlChar *str) {
const xmlChar *cur = str;
double ret;
int ok = 0;
int isneg = 0;
int exponent = 0;
int is_exponent_negative = 0;
#ifdef __GNUC__
unsigned long tmp = 0;
double temp;
#endif
if (cur == NULL)
return(0);
//
while (IS_BLANK_CH(*cur))
{
cur++;
}
if ((*cur != '.') &&
((*cur < '0') || (*cur > '9')) &&
(*cur != '-'))
{
return(xmlXPathNAN);
}
if (*cur == '-') {
isneg = 1;
cur++;
}
#ifdef __GNUC__
/*
* tmp/temp is a workaround against a gcc compiler bug
* http://veillard.com/gcc.bug
*/
ret = 0;
while ((*cur >= '0') && (*cur <= '9'))
{
ret = ret * 10;
tmp = (*cur - '0');
ok = 1;
cur++;
temp = (double) tmp;
ret = ret + temp;
}
#else
ret = 0;
while ((*cur >= '0') && (*cur <= '9'))
{
ret = ret * 10 + (*cur - '0');
ok = 1;
cur++;
}
#endif
if (*cur == '.')
{
int v, frac = 0;
double fraction = 0;
cur++;
if (((*cur < '0') || (*cur > '9')) && (!ok))
{
return(xmlXPathNAN);
}
while (((*cur >= '0') && (*cur <= '9')) && (frac < MAX_FRAC))
{
v = (*cur - '0');
fraction = fraction * 10 + v;
frac = frac + 1;
cur++;
}
fraction /= my_pow10[frac];
ret = ret + fraction;
while ((*cur >= '0') && (*cur <= '9'))
{
cur++;
}
}
if ((*cur == 'e') || (*cur == 'E'))
{
cur++;
if (*cur == '-')
{
is_exponent_negative = 1;
cur++;
}
while ((*cur >= '0') && (*cur <= '9'))
{
exponent = exponent * 10 + (*cur - '0');
cur++;
}
}
while (IS_BLANK_CH(*cur))
{
cur++;
}
if (*cur != 0)
return(xmlXPathNAN);
if (isneg)
ret = -ret;
if (is_exponent_negative)
exponent = -exponent;
//
ret *= pow(10.0, (double)exponent);
return(ret);
}
/**
* xmlXPathCompNumber:
* @ctxt: the XPath Parser context
*
* [30] Number ::= Digits ('.' Digits?)?
* | '.' Digits
* [31] Digits ::= [0-9]+
*
* Compile a Number, then push it on the stack
*
*/
static void
xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
{
double ret = 0.0;
double mult = 1;
int ok = 0;
int exponent = 0;
int is_exponent_negative = 0;
#ifdef __GNUC__
unsigned long tmp = 0;
double temp;
#endif
CHECK_ERROR;
if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
XP_ERROR(XPATH_NUMBER_ERROR);
}
#ifdef __GNUC__
/*
* tmp/temp is a workaround against a gcc compiler bug
* http://veillard.com/gcc.bug
*/
ret = 0;
while ((CUR >= '0') && (CUR <= '9')) {
ret = ret * 10;
tmp = (CUR - '0');
ok = 1;
NEXT;
temp = (double) tmp;
ret = ret + temp;
}
#else
ret = 0;
while ((CUR >= '0') && (CUR <= '9')) {
ret = ret * 10 + (CUR - '0');
ok = 1;
NEXT;
}
#endif
if (CUR == '.') {
NEXT;
if (((CUR < '0') || (CUR > '9')) && (!ok)) {
XP_ERROR(XPATH_NUMBER_ERROR);
}
while ((CUR >= '0') && (CUR <= '9')) {
mult /= 10;
ret = ret + (CUR - '0') * mult;
NEXT;
}
}
if ((CUR == 'e') || (CUR == 'E')) {
NEXT;
if (CUR == '-') {
is_exponent_negative = 1;
NEXT;
}
while ((CUR >= '0') && (CUR <= '9')) {
exponent = exponent * 10 + (CUR - '0');
NEXT;
}
if (is_exponent_negative)
exponent = -exponent;
ret *= pow(10.0, (double) exponent);
}
PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
xmlXPathNewFloat(ret), NULL);
}
/**
* xmlXPathParseLiteral:
* @ctxt: the XPath Parser context
*
* Parse a Literal
*
* [29] Literal ::= '"' [^"]* '"'
* | "'" [^']* "'"
*
* Returns the value found or NULL in case of error
*/
static xmlChar *
xmlXPathParseLiteral(xmlXPathParserContextPtr ctxt) {
const xmlChar *q;
xmlChar *ret = NULL;
if (CUR == '"') {
NEXT;
q = CUR_PTR;
while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
NEXT;
if (!IS_CHAR_CH(CUR)) {
XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
} else {
ret = xmlStrndup(q, CUR_PTR - q);
NEXT;
}
} else if (CUR == '\'') {
NEXT;
q = CUR_PTR;
while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
NEXT;
if (!IS_CHAR_CH(CUR)) {
XP_ERROR0(XPATH_UNFINISHED_LITERAL_ERROR);
} else {
ret = xmlStrndup(q, CUR_PTR - q);
NEXT;
}
} else {
XP_ERROR0(XPATH_START_LITERAL_ERROR);
}
return(ret);
}
/**
* xmlXPathCompLiteral:
* @ctxt: the XPath Parser context
*
* Parse a Literal and push it on the stack.
*
* [29] Literal ::= '"' [^"]* '"'
* | "'" [^']* "'"
*
* TODO: xmlXPathCompLiteral memory allocation could be improved.
*/
static void
xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
const xmlChar *q;
xmlChar *ret = NULL;
if (CUR == '"') {
NEXT;
q = CUR_PTR;
while ((IS_CHAR_CH(CUR)) && (CUR != '"'))
NEXT;
if (!IS_CHAR_CH(CUR)) {
XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
} else {
ret = xmlStrndup(q, CUR_PTR - q);
NEXT;
}
} else if (CUR == '\'') {
NEXT;
q = CUR_PTR;
while ((IS_CHAR_CH(CUR)) && (CUR != '\''))
NEXT;
if (!IS_CHAR_CH(CUR)) {
XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
} else {
ret = xmlStrndup(q, CUR_PTR - q);
NEXT;
}
} else {
XP_ERROR(XPATH_START_LITERAL_ERROR);
}
if (ret == NULL) return;
PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
xmlXPathNewString(ret), NULL);
xmlFree(ret);
}
/**
* xmlXPathCompVariableReference:
* @ctxt: the XPath Parser context
*
* Parse a VariableReference, evaluate it and push it on the stack.
*
* The variable bindings consist of a mapping from variable names
* to variable values. The value of a variable is an object, which can be
* of any of the types that are possible for the value of an expression,
* and may also be of additional types not specified here.
*
* Early evaluation is possible since:
* The variable bindings [...] used to evaluate a subexpression are
* always the same as those used to evaluate the containing expression.
*
* [36] VariableReference ::= '$' QName
*/
static void
xmlXPathCompVariableReference(xmlXPathParserContextPtr ctxt) {
xmlChar *name;
xmlChar *prefix;
SKIP_BLANKS;
if (CUR != '$') {
XP_ERROR(XPATH_VARIABLE_REF_ERROR);
}
NEXT;
name = xmlXPathParseQName(ctxt, &prefix);
if (name == NULL) {
XP_ERROR(XPATH_VARIABLE_REF_ERROR);
}
ctxt->comp->last = -1;
PUSH_LONG_EXPR(XPATH_OP_VARIABLE, 0, 0, 0,
name, prefix);
SKIP_BLANKS;
}
/**
* xmlXPathIsNodeType:
* @name: a name string
*
* Is the name given a NodeType one.
*
* [38] NodeType ::= 'comment'
* | 'text'
* | 'processing-instruction'
* | 'node'
*
* Returns 1 if true 0 otherwise
*/
int
xmlXPathIsNodeType(const xmlChar *name) {
if (name == NULL)
return(0);
if (xmlStrEqual(name, BAD_CAST "node"))
return(1);
if (xmlStrEqual(name, BAD_CAST "text"))
return(1);
if (xmlStrEqual(name, BAD_CAST "comment"))
return(1);
if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
return(1);
return(0);
}
/**
* xmlXPathCompFunctionCall:
* @ctxt: the XPath Parser context
*
* [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
* [17] Argument ::= Expr
*
* Compile a function call, the evaluation of all arguments are
* pushed on the stack
*/
static void
xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
xmlChar *name;
xmlChar *prefix;
int nbargs = 0;
name = xmlXPathParseQName(ctxt, &prefix);
if (name == NULL) {
XP_ERROR(XPATH_EXPR_ERROR);
}
SKIP_BLANKS;
#ifdef DEBUG_EXPR
if (prefix == NULL)
xmlGenericError(xmlGenericErrorContext, "Calling function %s\n",
name);
else
xmlGenericError(xmlGenericErrorContext, "Calling function %s:%s\n",
prefix, name);
#endif
if (CUR != '(') {
XP_ERROR(XPATH_EXPR_ERROR);
}
NEXT;
SKIP_BLANKS;
ctxt->comp->last = -1;
if (CUR != ')') {
while (CUR != 0) {
int op1 = ctxt->comp->last;
ctxt->comp->last = -1;
xmlXPathCompileExpr(ctxt);
if(OOM_FLAG)
{
xmlFree(name);
return;
}
CHECK_ERROR;
PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
nbargs++;
if (CUR == ')') break;
if (CUR != ',') {
XP_ERROR(XPATH_EXPR_ERROR);
}
NEXT;
SKIP_BLANKS;
}
}
if ( PUSH_LONG_EXPR(XPATH_OP_FUNCTION, nbargs, 0, 0, name, prefix) == -1)
goto Err;
NEXT;
SKIP_BLANKS;
return;
Err:
if(name)
xmlFree(name);
if(prefix)
xmlFree(prefix);
}
/**
* xmlXPathCompPrimaryExpr:
* @ctxt: the XPath Parser context
*
* [15] PrimaryExpr ::= VariableReference
* | '(' Expr ')'
* | Literal
* | Number
* | FunctionCall
*
* Compile a primary expression.
*/
static void
xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
SKIP_BLANKS;
if (CUR == '$') xmlXPathCompVariableReference(ctxt);
else if (CUR == '(') {
NEXT;
SKIP_BLANKS;
xmlXPathCompileExpr(ctxt);
CHECK_ERROR;
if (CUR != ')') {
XP_ERROR(XPATH_EXPR_ERROR);
}
NEXT;
SKIP_BLANKS;
} else if (IS_DIGIT_CH(CUR) || (CUR == '.' && IS_DIGIT_CH(NXT(1)))) {
xmlXPathCompNumber(ctxt);
} else if ((CUR == '\'') || (CUR == '"')) {
xmlXPathCompLiteral(ctxt);
} else {
xmlXPathCompFunctionCall(ctxt);
}
SKIP_BLANKS;
}
/**
* xmlXPathCompFilterExpr:
* @ctxt: the XPath Parser context
*
* [20] FilterExpr ::= PrimaryExpr
* | FilterExpr Predicate
*
* Compile a filter expression.
* Square brackets are used to filter expressions in the same way that
* they are used in location paths. It is an error if the expression to
* be filtered does not evaluate to a node-set. The context node list
* used for evaluating the expression in square brackets is the node-set
* to be filtered listed in document order.
*/
static void
xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
xmlXPathCompPrimaryExpr(ctxt);
CHECK_ERROR;
SKIP_BLANKS;
while (CUR == '[') {
xmlXPathCompPredicate(ctxt, 1);
SKIP_BLANKS;
}
}
/**
* xmlXPathScanName:
* @ctxt: the XPath Parser context
*
* Trickery: parse an XML name but without consuming the input flow
* Needed to avoid insanity in the parser state.
*
* [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
* CombiningChar | Extender
*
* [5] Name ::= (Letter | '_' | ':') (NameChar)*
*
* [6] Names ::= Name (S Name)*
*
* Returns the Name parsed or NULL
*
* OOM: possible --> sets OOM when NULL is return
*/
static xmlChar*
xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
xmlChar buf[XML_MAX_NAMELEN];
int len = 0;
SKIP_BLANKS;
if (!IS_LETTER_CH(CUR) &&
(CUR != '_') &&
(CUR != ':'))
{
return(NULL);
}
// TODO: OPTIMIZE: Repeated while conditions: NXT(len)
while ((IS_LETTER_CH(NXT(len))) ||
(IS_DIGIT_CH(NXT(len))) ||
(NXT(len) == '.') ||
(NXT(len) == '-') ||
(NXT(len) == '_') ||
(NXT(len) == ':') ||
(IS_COMBINING_CH(NXT(len))) ||
(IS_EXTENDER_CH(NXT(len))))
{
buf[len] = NXT(len);
len++;
if (len >= XML_MAX_NAMELEN) // TODO: Make a configurable parameter
{
xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlScanName: reached XML_MAX_NAMELEN limit\n"));
while (
(IS_LETTER_CH(NXT(len))) ||
(IS_DIGIT_CH(NXT(len))) ||
(NXT(len) == '.') ||
(NXT(len) == '-') ||
(NXT(len) == '_') ||
(NXT(len) == ':') ||
(IS_COMBINING_CH(NXT(len))) ||
(IS_EXTENDER_CH(NXT(len))))
{
len++;
}
break; // while
}
}
return(xmlStrndup(buf, len)); // may cause OOM
}
/**
* xmlXPathCompPathExpr:
* @ctxt: the XPath Parser context
*
* [19] PathExpr ::= LocationPath
* | FilterExpr
* | FilterExpr '/' RelativeLocationPath
* | FilterExpr '//' RelativeLocationPath
*
* Compile a path expression.
* The / operator and // operators combine an arbitrary expression
* and a relative location path. It is an error if the expression
* does not evaluate to a node-set.
* The / operator does composition in the same way as when / is
* used in a location path. As in location paths, // is short for
* /descendant-or-self::node()/.
*
* OOM:
*/
static void
xmlXPathCompPathExpr(xmlXPathParserContextPtr ctxt) {
int lc = 1; /* Should we branch to LocationPath ? */
xmlChar *name = NULL; /* we may have to preparse a name to find out */
SKIP_BLANKS;
if ((CUR == '$') || (CUR == '(') || (IS_DIGIT_CH(CUR)) ||
(CUR == '\'') || (CUR == '"') || (CUR == '.' && IS_DIGIT_CH(NXT(1))))
{
lc = 0;
} else if (CUR == '*') {
/* relative or absolute location path */
lc = 1;
} else if (CUR == '/') {
/* relative or absolute location path */
lc = 1;
} else if (CUR == '@') {
/* relative abbreviated attribute location path */
lc = 1;
} else if (CUR == '.') {
/* relative abbreviated attribute location path */
lc = 1;
} else {
/*
* Problem is finding if we have a name here whether it's:
* - a nodetype
* - a function call in which case it's followed by '('
* - an axis in which case it's followed by ':'
* - a element name
* We do an a priori analysis here rather than having to
* maintain parsed token content through the recursive function
* calls. This looks uglier but makes the code easier to
* read/write/debug.
*/
SKIP_BLANKS;
name = xmlXPathScanName(ctxt);
if(OOM_FLAG) return;
if (name && (xmlStrstr(name, (xmlChar *) "::") != NULL)) {
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "PathExpr: Axis\n");
#endif
lc = 1;
xmlFree(name);
} else if (name != NULL) {
int len =xmlStrlen(name);
while (NXT(len) != 0) {
if (NXT(len) == '/') {
/* element name */
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
#endif
lc = 1;
break;
} else if (IS_BLANK_CH(NXT(len))) {
/* ignore blanks */
;
} else if (NXT(len) == ':') {
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
#endif
lc = 1;
break;
} else if ((NXT(len) == '(')) {
/* Note Type or Function */
if (xmlXPathIsNodeType(name)) {
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "PathExpr: Type search\n");
#endif
lc = 1;
} else {
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "PathExpr: function call\n");
#endif
lc = 0;
}
break;
} else if ((NXT(len) == '[')) {
/* element name */
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
#endif
lc = 1;
break;
} else if ((NXT(len) == '<') ||
(NXT(len) == '>') ||
(NXT(len) == '='))
{
lc = 1;
break;
} else {
lc = 1;
break;
}
len++;
}
if (NXT(len) == 0) {
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "PathExpr: AbbrRelLocation\n");
#endif
/* element name */
lc = 1;
}
xmlFree(name);
} else {
/* make sure all cases are covered explicitly */
XP_ERROR(XPATH_EXPR_ERROR);
}
}
if (lc) {
if (CUR == '/') {
PUSH_LEAVE_EXPR(XPATH_OP_ROOT, 0, 0);
} else {
PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
}
xmlXPathCompLocationPath(ctxt);
if(OOM_FLAG) return;
} else {
xmlXPathCompFilterExpr(ctxt);
CHECK_ERROR;
if ((CUR == '/') && (NXT(1) == '/'))
{
SKIP(2);
SKIP_BLANKS;
PUSH_LONG_EXPR(
XPATH_OP_COLLECT,
AXIS_DESCENDANT_OR_SELF,
NODE_TEST_TYPE,
NODE_TYPE_NODE,
NULL,
NULL);
PUSH_UNARY_EXPR(XPATH_OP_RESET, ctxt->comp->last, 1, 0);
xmlXPathCompRelativeLocationPath(ctxt);
} else if (CUR == '/') {
xmlXPathCompRelativeLocationPath(ctxt);
}
}
SKIP_BLANKS;
}
/**
* xmlXPathCompUnionExpr:
* @ctxt: the XPath Parser context
*
* [18] UnionExpr ::= PathExpr
* | UnionExpr '|' PathExpr
*
* Compile an union expression.
*
* OOM:
*/
static void
xmlXPathCompUnionExpr(xmlXPathParserContextPtr ctxt) {
xmlXPathCompPathExpr(ctxt);
if(OOM_FLAG) return;
CHECK_ERROR;
SKIP_BLANKS;
while (CUR == '|') {
int op1 = ctxt->comp->last;
PUSH_LEAVE_EXPR(XPATH_OP_NODE, 0, 0);
NEXT;
SKIP_BLANKS;
xmlXPathCompPathExpr(ctxt);
PUSH_BINARY_EXPR(XPATH_OP_UNION, op1, ctxt->comp->last, 0, 0);
SKIP_BLANKS;
}
}
/**
* xmlXPathCompUnaryExpr:
* @ctxt: the XPath Parser context
*
* [27] UnaryExpr ::= UnionExpr
* | '-' UnaryExpr
*
* Compile an unary expression.
*
* OOM:
*/
static void
xmlXPathCompUnaryExpr(xmlXPathParserContextPtr ctxt) {
int minus = 0;
int found = 0;
SKIP_BLANKS;
while (CUR == '-') {
minus = 1 - minus;
found = 1;
NEXT;
SKIP_BLANKS;
}
xmlXPathCompUnionExpr(ctxt);
if(OOM_FLAG) return;
CHECK_ERROR;
if (found) {
if (minus)
PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 2, 0);
else
PUSH_UNARY_EXPR(XPATH_OP_PLUS, ctxt->comp->last, 3, 0);
}
}
/**
* xmlXPathCompMultiplicativeExpr:
* @ctxt: the XPath Parser context
*
* [26] MultiplicativeExpr ::= UnaryExpr
* | MultiplicativeExpr MultiplyOperator UnaryExpr
* | MultiplicativeExpr 'div' UnaryExpr
* | MultiplicativeExpr 'mod' UnaryExpr
* [34] MultiplyOperator ::= '*'
*
* Compile an Additive expression.
*
* OOM:
*/
static void
xmlXPathCompMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
xmlXPathCompUnaryExpr(ctxt);
if(OOM_FLAG) return;
CHECK_ERROR;
SKIP_BLANKS;
while ((CUR == '*') ||
((CUR == 'd') && (NXT(1) == 'i') && (NXT(2) == 'v')) ||
((CUR == 'm') && (NXT(1) == 'o') && (NXT(2) == 'd'))) {
int op = -1;
int op1 = ctxt->comp->last;
if (CUR == '*') {
op = 0;
NEXT;
} else if (CUR == 'd') {
op = 1;
SKIP(3);
} else if (CUR == 'm') {
op = 2;
SKIP(3);
}
SKIP_BLANKS;
xmlXPathCompUnaryExpr(ctxt);
CHECK_ERROR;
PUSH_BINARY_EXPR(XPATH_OP_MULT, op1, ctxt->comp->last, op, 0);
SKIP_BLANKS;
}
}
/**
* xmlXPathCompAdditiveExpr:
* @ctxt: the XPath Parser context
*
* [25] AdditiveExpr ::= MultiplicativeExpr
* | AdditiveExpr '+' MultiplicativeExpr
* | AdditiveExpr '-' MultiplicativeExpr
*
* Compile an Additive expression.
*
* OOM:
*/
static void
xmlXPathCompAdditiveExpr(xmlXPathParserContextPtr ctxt) {
xmlXPathCompMultiplicativeExpr(ctxt);
if(OOM_FLAG) return;
CHECK_ERROR;
SKIP_BLANKS;
while ((CUR == '+') || (CUR == '-')) {
int plus;
int op1 = ctxt->comp->last;
if (CUR == '+') plus = 1;
else plus = 0;
NEXT;
SKIP_BLANKS;
xmlXPathCompMultiplicativeExpr(ctxt);
CHECK_ERROR;
PUSH_BINARY_EXPR(XPATH_OP_PLUS, op1, ctxt->comp->last, plus, 0);
SKIP_BLANKS;
}
}
/**
* xmlXPathCompRelationalExpr:
* @ctxt: the XPath Parser context
*
* [24] RelationalExpr ::= AdditiveExpr
* | RelationalExpr '<' AdditiveExpr
* | RelationalExpr '>' AdditiveExpr
* | RelationalExpr '<=' AdditiveExpr
* | RelationalExpr '>=' AdditiveExpr
*
* A <= B > C is allowed ? Answer from James, yes with
* (AdditiveExpr <= AdditiveExpr) > AdditiveExpr
* which is basically what got implemented.
*
* Compile a Relational expression, then push the result
* on the stack
*
* OOM:
*/
static void
xmlXPathCompRelationalExpr(xmlXPathParserContextPtr ctxt) {
xmlChar ch;
int* last;
// XML ENGINE: Changes/optimization were applied here
xmlXPathCompAdditiveExpr(ctxt);
if(OOM_FLAG) return;
CHECK_ERROR;
SKIP_BLANKS;
last = &(ctxt->comp->last);
// NOTE: this loop allows expressions like A > B <= C < D .. etc.
// which will be evaluated in a way that may be unexpected:
// e.g. the example above is evaluated as
// ((A > B) <= C) < D with boolean results within parentheses
// converted to 1 or 0 (from 'true' and 'false')
// TODO: Check how well this confoms to the spec
for(;;)
{
ch = *ctxt->cur;
if (ch == '<' || ch == '>')
{
int op1 = *last;
int strict = (*(++(ctxt->cur)) == '=') ? 0 : 1; // evaluate 'strict' and NEXT
if (!strict)
ctxt->cur++; // NEXT again if '='
SKIP_BLANKS;
xmlXPathCompAdditiveExpr(ctxt);
CHECK_ERROR;
PUSH_BINARY_EXPR(
XPATH_OP_CMP,
op1,
*last,
(ch == '<') & 1,
strict);
SKIP_BLANKS;
}
else
return;
} // for(;;)
}
/**
* xmlXPathCompEqualityExpr:
* @ctxt: the XPath Parser context
*
* [23] EqualityExpr ::= RelationalExpr
* | EqualityExpr '=' RelationalExpr
* | EqualityExpr '!=' RelationalExpr
*
* A != B != C is allowed ? Answer from James, yes with
* (RelationalExpr = RelationalExpr) = RelationalExpr
* (RelationalExpr != RelationalExpr) != RelationalExpr
* which is basically what got implemented.
*
* Compile an Equality expression.
*
* OOM:
*/
static void
xmlXPathCompEqualityExpr(xmlXPathParserContextPtr ctxt) {
xmlXPathCompRelationalExpr(ctxt);
if(OOM_FLAG) return;
CHECK_ERROR;
SKIP_BLANKS;
while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
int eq;
int op1 = ctxt->comp->last;
if (CUR == '=') eq = 1;
else eq = 0;
NEXT;
if (!eq) NEXT;
SKIP_BLANKS;
xmlXPathCompRelationalExpr(ctxt);
CHECK_ERROR;
PUSH_BINARY_EXPR(XPATH_OP_EQUAL, op1, ctxt->comp->last, eq, 0);
SKIP_BLANKS;
}
}
/**
* xmlXPathCompAndExpr:
* @ctxt: the XPath Parser context
*
* [22] AndExpr ::= EqualityExpr
* | AndExpr 'and' EqualityExpr
*
* Compile an AND expression.
*
* OOM:
*/
static void
xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
xmlXPathCompEqualityExpr(ctxt);
if(OOM_FLAG) return;
CHECK_ERROR;
SKIP_BLANKS;
while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'd')) {
int op1 = ctxt->comp->last;
SKIP(3);
SKIP_BLANKS;
xmlXPathCompEqualityExpr(ctxt);
CHECK_ERROR;
PUSH_BINARY_EXPR(XPATH_OP_AND, op1, ctxt->comp->last, 0, 0);
SKIP_BLANKS;
}
}
#define XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS
/**
* xmlXPathCompileExpr:
* @ctxt: the XPath Parser context
*
* [14] Expr ::= OrExpr
* [21] OrExpr ::= AndExpr
* | OrExpr 'or' AndExpr
*
* Parse and compile an expression
*
* OOM: possible --> check OOM flag ////
*/
static void
xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt)
{
xmlXPathCompAndExpr(ctxt);
CHECK_ERROR;
if(OOM_FLAG){
XP_ERROR(XPATH_MEMORY_ERROR);
}
SKIP_BLANKS;
while ((CUR == 'o') && (NXT(1) == 'r')) {
int op1 = ctxt->comp->last;
SKIP(2);
SKIP_BLANKS;
xmlXPathCompAndExpr(ctxt);
CHECK_ERROR;
PUSH_BINARY_EXPR(XPATH_OP_OR, op1, ctxt->comp->last, 0, 0);
op1 = ctxt->comp->nbStep;
SKIP_BLANKS;
}
if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
/* more ops could be optimized too */
PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
}
#ifdef XMLENGINE_OPTIMIZE_COMP_EXPR_STEPS
// Free unused memory in the tail of ctxt->comp->steps array
{
xmlXPathStepOp* tmp;
// DONE: OPTIMIZE: free the rest of preallocated step table
// This is a exeprimental code in XML Engine
// DONE: Fix xmlRealloc (Note: OOM should not happen since the size is smaller, but...)
tmp = (xmlXPathStepOp*)xmlRealloc(ctxt->comp->steps, sizeof(xmlXPathStepOp) * ctxt->comp->nbStep);
if(tmp)
{
ctxt->comp->steps = tmp;
ctxt->comp->maxStep = ctxt->comp->nbStep;
}
}
#endif
//
}
/**
* xmlXPathCompPredicate:
* @ctxt: the XPath Parser context
* @filter: act as a filter
*
* [8] Predicate ::= '[' PredicateExpr ']'
* [9] PredicateExpr ::= Expr
*
* Compile a predicate expression
*/
static void
xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
int op1 = ctxt->comp->last;
SKIP_BLANKS;
if (CUR != '[') {
XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
}
NEXT;
SKIP_BLANKS;
ctxt->comp->last = -1;
xmlXPathCompileExpr(ctxt);
CHECK_ERROR;
if (CUR != ']') {
XP_ERROR(XPATH_INVALID_PREDICATE_ERROR);
}
if (filter)
PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0);
else
PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0);
NEXT;
SKIP_BLANKS;
}
/**
* xmlXPathCompNodeTest:
* @ctxt: the XPath Parser context
* @test: pointer to a xmlXPathTestVal
* @type: pointer to a xmlXPathTypeVal
* @prefix: placeholder for a possible name prefix
*
* [7] NodeTest ::= NameTest
* | NodeType '(' ')'
* | 'processing-instruction' '(' Literal ')'
*
* [37] NameTest ::= '*'
* | NCName ':' '*'
* | QName
* [38] NodeType ::= 'comment'
* | 'text'
* | 'processing-instruction'
* | 'node'
*
* Returns the name found and updates @test, @type and @prefix appropriately
*/
static xmlChar *
xmlXPathCompNodeTest(xmlXPathParserContextPtr ctxt, xmlXPathTestVal *test,
xmlXPathTypeVal *type, const xmlChar **prefix,
xmlChar *name) {
int blanks;
if ((test == NULL) || (type == NULL) || (prefix == NULL)) {
STRANGE;
return(NULL);
}
*type = (xmlXPathTypeVal) 0;
*test = (xmlXPathTestVal) 0;
*prefix = NULL;
SKIP_BLANKS;
if ((name == NULL) && (CUR == '*')) {
/*
* All elements
*/
NEXT;
*test = NODE_TEST_ALL;
return(NULL);
}
if (name == NULL)
name = xmlXPathParseNCName(ctxt);
if (name == NULL) {
XP_ERROR0(XPATH_EXPR_ERROR);
}
blanks = IS_BLANK_CH(CUR);
SKIP_BLANKS;
if (CUR == '(') {
NEXT;
/*
* NodeType or PI search
*/
if (xmlStrEqual(name, BAD_CAST "comment"))
*type = NODE_TYPE_COMMENT;
else if (xmlStrEqual(name, BAD_CAST "node"))
*type = NODE_TYPE_NODE;
else if (xmlStrEqual(name, BAD_CAST "processing-instruction"))
*type = NODE_TYPE_PI;
else if (xmlStrEqual(name, BAD_CAST "text"))
*type = NODE_TYPE_TEXT;
else {
if (name != NULL)
xmlFree(name);
XP_ERROR0(XPATH_EXPR_ERROR);
}
*test = NODE_TEST_TYPE;
SKIP_BLANKS;
if (*type == NODE_TYPE_PI) {
/*
* Specific case: search a PI by name.
*/
if (name != NULL)
xmlFree(name);
name = NULL;
if (CUR != ')') {
name = xmlXPathParseLiteral(ctxt);
CHECK_ERROR 0;
*test = NODE_TEST_PI;
SKIP_BLANKS;
}
}
if (CUR != ')') {
if (name != NULL)
xmlFree(name);
XP_ERROR0(XPATH_UNCLOSED_ERROR);
}
NEXT;
return(name);
}
*test = NODE_TEST_NAME;
if ((!blanks) && (CUR == ':')) {
NEXT;
/*
* Since currently the parser context don't have a
* namespace list associated:
* The namespace name for this prefix can be computed
* only at evaluation time. The compilation is done
* outside of any context.
*/
#if 0
*prefix = xmlXPathNsLookup(ctxt->context, name);
if (name != NULL)
xmlFree(name);
if (*prefix == NULL) {
XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
}
#else
*prefix = name;
#endif
if (CUR == '*') {
/*
* All elements
*/
NEXT;
*test = NODE_TEST_ALL;
return(NULL);
}
name = xmlXPathParseNCName(ctxt);
if (name == NULL) {
XP_ERROR0(XPATH_EXPR_ERROR);
}
}
return(name);
}
/**
* xmlXPathIsAxisName:
* @name: a preparsed name token
*
* [6] AxisName ::= 'ancestor'
* | 'ancestor-or-self'
* | 'attribute'
* | 'child'
* | 'descendant'
* | 'descendant-or-self'
* | 'following'
* | 'following-sibling'
* | 'namespace'
* | 'parent'
* | 'preceding'
* | 'preceding-sibling'
* | 'self'
*
* Returns the axis or 0
*/
static xmlXPathAxisVal
xmlXPathIsAxisName(const xmlChar *name) {
xmlXPathAxisVal ret = (xmlXPathAxisVal) 0;
switch (name[0]) {
// TODO: OPTIMIZE: Reorder case's optimally
case 'a':
if (xmlStrEqual(name, BAD_CAST "ancestor"))
ret = AXIS_ANCESTOR;
if (xmlStrEqual(name, BAD_CAST "ancestor-or-self"))
ret = AXIS_ANCESTOR_OR_SELF;
if (xmlStrEqual(name, BAD_CAST "attribute"))
ret = AXIS_ATTRIBUTE;
break;
case 'c':
if (xmlStrEqual(name, BAD_CAST "child"))
ret = AXIS_CHILD;
break;
case 'd':
if (xmlStrEqual(name, BAD_CAST "descendant"))
ret = AXIS_DESCENDANT;
if (xmlStrEqual(name, BAD_CAST "descendant-or-self"))
ret = AXIS_DESCENDANT_OR_SELF;
break;
case 'f':
if (xmlStrEqual(name, BAD_CAST "following"))
ret = AXIS_FOLLOWING;
if (xmlStrEqual(name, BAD_CAST "following-sibling"))
ret = AXIS_FOLLOWING_SIBLING;
break;
case 'n':
if (xmlStrEqual(name, BAD_CAST "namespace"))
ret = AXIS_NAMESPACE;
break;
case 'p':
if (xmlStrEqual(name, BAD_CAST "parent"))
ret = AXIS_PARENT;
if (xmlStrEqual(name, BAD_CAST "preceding"))
ret = AXIS_PRECEDING;
if (xmlStrEqual(name, BAD_CAST "preceding-sibling"))
ret = AXIS_PRECEDING_SIBLING;
break;
case 's':
if (xmlStrEqual(name, BAD_CAST "self"))
ret = AXIS_SELF;
break;
}
return(ret);
}
/**
* xmlXPathCompStep:
* @ctxt: the XPath Parser context
*
* [4] Step ::= AxisSpecifier NodeTest Predicate*
* | AbbreviatedStep
*
* [12] AbbreviatedStep ::= '.' | '..'
*
* [5] AxisSpecifier ::= AxisName '::'
* | AbbreviatedAxisSpecifier
*
* [13] AbbreviatedAxisSpecifier ::= '@'?
*
* Modified for XPtr range support as:
*
* [4xptr] Step ::= AxisSpecifier NodeTest Predicate*
* | AbbreviatedStep
* | 'range-to' '(' Expr ')' Predicate*
*
* Compile one step in a Location Path
* A location step of . is short for self::node(). This is
* particularly useful in conjunction with //. For example, the
* location path .//para is short for
* self::node()/descendant-or-self::node()/child::para
* and so will select all para descendant elements of the context
* node.
* Similarly, a location step of .. is short for parent::node().
* For example, ../title is short for parent::node()/child::title
* and so will select the title children of the parent of the context
* node.
*/
static void
xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
#ifdef LIBXML_XPTR_ENABLED
int rangeto = 0;
int op2 = -1;
#endif
SKIP_BLANKS;
if ((CUR == '.') && (NXT(1) == '.')) {
SKIP(2);
SKIP_BLANKS;
PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_PARENT,
NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
} else if (CUR == '.') {
NEXT;
SKIP_BLANKS;
} else {
xmlChar *name = NULL;
const xmlChar *prefix = NULL;
xmlXPathTestVal test;
xmlXPathAxisVal axis = (xmlXPathAxisVal) 0;
xmlXPathTypeVal type;
int op1;
/*
* The modification needed for XPointer change to the production
*/
#ifdef LIBXML_XPTR_ENABLED
if (ctxt->xptr) {
name = xmlXPathParseNCName(ctxt);
if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) {
op2 = ctxt->comp->last;
xmlFree(name);
SKIP_BLANKS;
if (CUR != '(') {
XP_ERROR(XPATH_EXPR_ERROR);
}
NEXT;
SKIP_BLANKS;
xmlXPathCompileExpr(ctxt);
/* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
CHECK_ERROR;
SKIP_BLANKS;
if (CUR != ')') {
XP_ERROR(XPATH_EXPR_ERROR);
}
NEXT;
rangeto = 1;
goto eval_predicates;
}
}
#endif
if (CUR == '*') {
axis = AXIS_CHILD;
} else {
if (name == NULL)
name = xmlXPathParseNCName(ctxt);
if(OOM_FLAG) return;
if (name != NULL) {
axis = xmlXPathIsAxisName(name);
if (axis != 0) {
SKIP_BLANKS;
if ((CUR == ':') && (NXT(1) == ':')) {
SKIP(2);
xmlFree(name);
name = NULL;
} else {
/* an element name can conflict with an axis one :-\ */
axis = AXIS_CHILD;
}
} else {
axis = AXIS_CHILD;
}
} else if (CUR == '@') {
NEXT;
axis = AXIS_ATTRIBUTE;
} else {
axis = AXIS_CHILD;
}
}
CHECK_ERROR;
name = xmlXPathCompNodeTest(ctxt, &test, &type, &prefix, name);
if (test == 0)
return;
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext,
"Basis : computing new set\n");
#endif
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "Basis : ");
if (ctxt->value == NULL)
xmlGenericError(xmlGenericErrorContext, "no value\n");
else if (ctxt->value->nodesetval == NULL)
xmlGenericError(xmlGenericErrorContext, "Empty\n");
else
xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval);
#endif
#ifdef LIBXML_XPTR_ENABLED
eval_predicates:
#endif
op1 = ctxt->comp->last;
ctxt->comp->last = -1;
SKIP_BLANKS;
while (CUR == '[') {
xmlXPathCompPredicate(ctxt, 0);
}
#ifdef LIBXML_XPTR_ENABLED
if (rangeto) {
PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, op1, 0, 0);
} else
#endif
PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis,
test, type, (void *)prefix, (void *)name);
}
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "Step : ");
if (ctxt->value == NULL)
xmlGenericError(xmlGenericErrorContext, "no value\n");
else if (ctxt->value->nodesetval == NULL)
xmlGenericError(xmlGenericErrorContext, "Empty\n");
else
xmlGenericErrorContextNodeSet(xmlGenericErrorContext,
ctxt->value->nodesetval);
#endif
}
/**
* xmlXPathCompRelativeLocationPath:
* @ctxt: the XPath Parser context
*
* [3] RelativeLocationPath ::= Step
* | RelativeLocationPath '/' Step
* | AbbreviatedRelativeLocationPath
* [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
*
* Compile a relative location path.
*/
static void
xmlXPathCompRelativeLocationPath
(xmlXPathParserContextPtr ctxt) {
SKIP_BLANKS;
if ((CUR == '/') && (NXT(1) == '/')) {
SKIP(2);
SKIP_BLANKS;
PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
} else if (CUR == '/') {
NEXT;
SKIP_BLANKS;
}
xmlXPathCompStep(ctxt);
if(OOM_FLAG) return;
SKIP_BLANKS;
while (CUR == '/') {
if ((CUR == '/') && (NXT(1) == '/')) {
SKIP(2);
SKIP_BLANKS;
PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
xmlXPathCompStep(ctxt);
if(OOM_FLAG) return;
} else if (CUR == '/') {
NEXT;
SKIP_BLANKS;
xmlXPathCompStep(ctxt);
}
SKIP_BLANKS;
}
}
/**
* xmlXPathCompLocationPath:
* @ctxt: the XPath Parser context
*
* [1] LocationPath ::= RelativeLocationPath
* | AbsoluteLocationPath
* [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
* | AbbreviatedAbsoluteLocationPath
* [10] AbbreviatedAbsoluteLocationPath ::=
* '//' RelativeLocationPath
*
* Compile a location path
*
* // is short for /descendant-or-self::node()/. For example,
* //para is short for /descendant-or-self::node()/child::para and
* so will select any para element in the document (even a para element
* that is a document element will be selected by //para since the
* document element node is a child of the root node); div//para is
* short for div/descendant-or-self::node()/child::para and so will
* select all para descendants of div children.
*/
static void
xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) {
SKIP_BLANKS;
if (CUR != '/') {
xmlXPathCompRelativeLocationPath(ctxt);
} else {
while (CUR == '/') {
if ((CUR == '/') && (NXT(1) == '/')) {
SKIP(2);
SKIP_BLANKS;
PUSH_LONG_EXPR(XPATH_OP_COLLECT, AXIS_DESCENDANT_OR_SELF,
NODE_TEST_TYPE, NODE_TYPE_NODE, NULL, NULL);
xmlXPathCompRelativeLocationPath(ctxt);
if(OOM_FLAG) return;
} else if (CUR == '/') {
NEXT;
SKIP_BLANKS;
if ((CUR != 0 ) &&
((IS_LETTER_CH(CUR)) || (CUR == '_') || (CUR == '.') ||
(CUR == '@') || (CUR == '*')))
{
xmlXPathCompRelativeLocationPath(ctxt);
if(OOM_FLAG) return;
}
}
}
}
}
/************************************************************************
* *
* XPath precompiled expression evaluation *
* *
************************************************************************/
static int
xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op);
/**
* xmlXPathNodeCollectAndTest:
* @ctxt: the XPath Parser context
* @op: the XPath precompiled step operation
* @first: pointer to the first element in document order
* @last: pointer to the last element in document order
*
* This is the function implementing a step: based on the current list
* of nodes, it builds up a new list, looking at all nodes under that
* axis and selecting them. It also does the predicate filtering
*
* Pushes the new NodeSet resulting from the search.
*
* Returns the number of nodes traversed or -1 when OOM
*/
static int
xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
xmlXPathStepOpPtr op,
xmlNodePtr * first, xmlNodePtr * last)
{
xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
const xmlChar* prefix = (xmlChar*) op->value4;
const xmlChar* name = (xmlChar*) op->value5;
const xmlChar* URI = NULL;
#ifdef DEBUG_STEP
int n = 0;
#endif
int i, t = 0;
xmlNodeSetPtr ret = NULL;
xmlNodeSetPtr list = NULL;
xmlXPathTraversalFunction next = NULL;
void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
xmlNodeSetPtr (*mergeNodeSet) (xmlNodeSetPtr, xmlNodeSetPtr);
xmlNodePtr cur = NULL;
xmlXPathObjectPtr obj;
xmlNodeSetPtr nodelist;
xmlNodePtr tmp;
CHECK_TYPE0(XPATH_NODESET);
obj = valuePop(ctxt);
addNode = xmlXPathNodeSetAdd;
mergeNodeSet = xmlXPathNodeSetMerge;
if (prefix != NULL) {
URI = xmlXPathNsLookup(ctxt->context, prefix);
if (URI == NULL)
XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
}
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "new step : ");
#endif
switch (axis)
{
// TODO: OPTIMIZATION: Reorder options according to expectation of invocation
case AXIS_ANCESTOR: {
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
#endif
first = NULL;
next = xmlXPathNextAncestor;
break;
}
case AXIS_ANCESTOR_OR_SELF: {
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'ancestors-or-self' ");
#endif
first = NULL;
next = xmlXPathNextAncestorOrSelf;
break;
}
case AXIS_ATTRIBUTE:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
#endif
first = NULL;
last = NULL;
next = xmlXPathNextAttribute;
mergeNodeSet = xmlXPathNodeSetMergeUnique;
break;
}
case AXIS_CHILD:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
#endif
last = NULL;
next = xmlXPathNextChild;
mergeNodeSet = xmlXPathNodeSetMergeUnique;
break;
}
case AXIS_DESCENDANT:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
#endif
last = NULL;
next = xmlXPathNextDescendant;
break;
}
case AXIS_DESCENDANT_OR_SELF:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'descendant-or-self' ");
#endif
last = NULL;
next = xmlXPathNextDescendantOrSelf;
break;
}
case AXIS_FOLLOWING:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
#endif
last = NULL;
next = xmlXPathNextFollowing;
break;
}
case AXIS_FOLLOWING_SIBLING:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'following-siblings' ");
#endif
last = NULL;
next = xmlXPathNextFollowingSibling;
break;
}
case AXIS_NAMESPACE:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
#endif
first = NULL;
last = NULL;
next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
mergeNodeSet = xmlXPathNodeSetMergeUnique;
break;
}
case AXIS_PARENT:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
#endif
first = NULL;
next = xmlXPathNextParent;
break;
}
case AXIS_PRECEDING:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
#endif
first = NULL;
next = xmlXPathNextPrecedingInternal;
break;
}
case AXIS_PRECEDING_SIBLING:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'preceding-sibling' ");
#endif
first = NULL;
next = xmlXPathNextPrecedingSibling;
break;
}
case AXIS_SELF:{
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
#endif
first = NULL;
last = NULL;
next = xmlXPathNextSelf;
mergeNodeSet = xmlXPathNodeSetMergeUnique;
break;
}
} // switch(axis)
if (next == NULL)
return(0);
nodelist = obj->nodesetval;
if (nodelist == NULL) {
xmlXPathFreeObject(obj);
valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
return(0);
}
addNode = xmlXPathNodeSetAddUnique;
ret = NULL;
#ifdef DEBUG_STEP
{
xmlGenericError(xmlGenericErrorContext, " context contains %d nodes\n", nodelist->nodeNr);
switch (test) {
case NODE_TEST_NONE:
xmlGenericError(xmlGenericErrorContext, " searching for none !!!\n");
break;
case NODE_TEST_TYPE:
xmlGenericError(xmlGenericErrorContext, " searching for type %d\n", type);
break;
case NODE_TEST_PI:
xmlGenericError(xmlGenericErrorContext, " searching for PI !!!\n");
break;
case NODE_TEST_ALL:
xmlGenericError(xmlGenericErrorContext, " searching for *\n");
break;
case NODE_TEST_NS:
xmlGenericError(xmlGenericErrorContext, " searching for namespace %s\n", prefix);
break;
case NODE_TEST_NAME:
xmlGenericError(xmlGenericErrorContext, " searching for name %s\n", name);
if (prefix != NULL)
xmlGenericError(xmlGenericErrorContext, " with namespace %s\n", prefix);
break;
}
xmlGenericError(xmlGenericErrorContext, "Testing : ");
}
#endif
/*
* 2.3 Node Tests
* - For the attribute axis, the principal node type is attribute.
* - For the namespace axis, the principal node type is namespace.
* - For other axes, the principal node type is element.
*
* A node test * is true for any node of the
* principal node type. For example, child::* will
* select all element children of the context node
*/
tmp = ctxt->context->node;
for (i = 0; i < nodelist->nodeNr; i++)
{
ctxt->context->node = nodelist->nodeTab[i];
cur = NULL;
list = xmlXPathNodeSetCreate(NULL);
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(ret);
return(-1);
}
//
do {
cur = next(ctxt, cur);
if (cur == NULL)
break;
if ((first != NULL) && (*first == cur))
break;
if (((t % 256) == 0) &&
(first != NULL) && (*first != NULL) &&
(xmlXPathCmpNodes(*first, cur) >= 0))
break;
if ((last != NULL) && (*last == cur))
break;
if (((t % 256) == 0) &&
(last != NULL) && (*last != NULL) &&
(xmlXPathCmpNodes(cur, *last) >= 0))
break;
t++;
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
#endif
switch (test)
{
case NODE_TEST_NONE:{
ctxt->context->node = tmp;
STRANGE return(t);
}
case NODE_TEST_TYPE:{
// TODO: OPTIMIZATION: Reorder to maximize performance of partial evaluation
if ((cur->type == type) ||
((type == NODE_TYPE_NODE) &&
((cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE) ||
(cur->type == XML_ELEMENT_NODE) ||
(cur->type == XML_NAMESPACE_DECL) ||
(cur->type == XML_ATTRIBUTE_NODE) ||
(cur->type == XML_PI_NODE) ||
(cur->type == XML_COMMENT_NODE) ||
(cur->type == XML_CDATA_SECTION_NODE) ||
(cur->type == XML_TEXT_NODE))) ||
((type == NODE_TYPE_TEXT) &&
(cur->type == XML_CDATA_SECTION_NODE)))
{
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, cur);
}
break;
}
case NODE_TEST_PI: {
if (cur->type == XML_PI_NODE)
{
if ((name != NULL) && (!xmlStrEqual(name, cur->name)))
break;
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, cur);
}
break;
}
case NODE_TEST_ALL: {
if (axis == AXIS_ATTRIBUTE) {
if (cur->type == XML_ATTRIBUTE_NODE) {
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, cur);
}
} else
if (axis == AXIS_NAMESPACE) {
if (cur->type == XML_NAMESPACE_DECL) {
#ifdef DEBUG_STEP
n++;
#endif
xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur);
}
} else {
if (cur->type == XML_ELEMENT_NODE) {
if (prefix == NULL) {
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, cur);
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(list);
xmlXPathFreeNodeSet(ret);
return(-1);
}
} else
if ((cur->ns != NULL) && (xmlStrEqual(URI, cur->ns->href))) {
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, cur);
}
}
}
break;
}
case NODE_TEST_NS: {
TODO;
break;
}
case NODE_TEST_NAME:{
switch (cur->type) {
case XML_ELEMENT_NODE: {
if (xmlStrEqual(name, cur->name)) {
if (prefix == NULL) {
if (cur->ns == NULL) { // should we add here || cur->ns->pref == NULL ?
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, cur);
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(list);
return(-1);
}
}
} else {
if ((cur->ns != NULL) && (xmlStrEqual(URI, cur->ns->href))) {
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, cur);
}
}
}
break;
}
case XML_ATTRIBUTE_NODE:{
xmlAttrPtr attr = (xmlAttrPtr) cur;
if (xmlStrEqual(name, attr->name)) {
if (prefix == NULL) {
if ((attr->ns == NULL) || (attr->ns->prefix == NULL)) {
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, (xmlNodePtr) attr);
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(list);
return(-1);
}
}
} else {
if ((attr->ns != NULL) && (xmlStrEqual(URI, attr->ns->href))) {
#ifdef DEBUG_STEP
n++;
#endif
addNode(list, (xmlNodePtr) attr);
}
}
}
break;
}
case XML_NAMESPACE_DECL:{
if (cur->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns = (xmlNsPtr) cur;
if ((ns->prefix != NULL) && (name != NULL)
&& (xmlStrEqual(ns->prefix, name)))
{
#ifdef DEBUG_STEP
n++;
#endif
xmlXPathNodeSetAddNs(list, ctxt->context->node, (xmlNsPtr) cur);
}
}
break;
}
default:
break;
}
break;
break;
}
} // switch(test)
} while (cur != NULL);
/*
* If there is some predicate filtering do it now
*/
if ((op->ch2 != -1) && (list != NULL) && (list->nodeNr > 0))
{
xmlXPathObjectPtr obj2;
valuePush(ctxt, xmlXPathWrapNodeSet(list));
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(list);
xmlXPathFreeNodeSet(ret);
return(-1);
}
xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
CHECK_TYPE0(XPATH_NODESET);
obj2 = valuePop(ctxt);
list = obj2->nodesetval;
obj2->nodesetval = NULL;
xmlXPathFreeObject(obj2);
}
if (ret == NULL) {
ret = list;
} else {
ret = mergeNodeSet(ret, list);
xmlXPathFreeNodeSet(list);
}
} // for (i = 0; i < nodelist->nodeNr; i++)
ctxt->context->node = tmp;
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "\nExamined %d nodes, found %d nodes at that step\n", t, n);
#endif
valuePush(ctxt, xmlXPathWrapNodeSet(ret));
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(list);
return(-1);
}
if ((obj->boolval) && (obj->user != NULL)) {
ctxt->value->boolval = 1;
ctxt->value->user = obj->user;
obj->user = NULL;
obj->boolval = 0;
}
xmlXPathFreeObject(obj);
return(t);
}
/**
* xmlXPathNodeCollectAndTestNth:
* @ctxt: the XPath Parser context
* @op: the XPath precompiled step operation
* @indx: the index to collect
* @first: pointer to the first element in document order
* @last: pointer to the last element in document order
*
* This is the function implementing a step: based on the current list
* of nodes, it builds up a new list, looking at all nodes under that
* axis and selecting them. It also does the predicate filtering
*
* Pushes the new NodeSet resulting from the search.
* Returns the number of node traversed or -1 when OOM
*/
static int
xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
xmlXPathStepOpPtr op, int indx,
xmlNodePtr * first, xmlNodePtr * last)
{
xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
xmlXPathTestVal test = (xmlXPathTestVal) op->value2;
xmlXPathTypeVal type = (xmlXPathTypeVal) op->value3;
const xmlChar *prefix = (xmlChar*)op->value4;
const xmlChar *name = (xmlChar*)op->value5;
const xmlChar *URI = NULL;
int n = 0, t = 0;
int i;
xmlNodeSetPtr list;
xmlXPathTraversalFunction next = NULL;
void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
xmlNodePtr cur = NULL;
xmlXPathObjectPtr obj;
xmlNodeSetPtr nodelist;
xmlNodePtr tmp;
CHECK_TYPE0(XPATH_NODESET);
obj = valuePop(ctxt);
addNode = xmlXPathNodeSetAdd;
if (prefix != NULL) {
URI = xmlXPathNsLookup(ctxt->context, prefix);
if (URI == NULL)
XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
}
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "new step : ");
if (first != NULL) {
if (*first != NULL)
xmlGenericError(xmlGenericErrorContext, "first = %s ",
(*first)->name);
else
xmlGenericError(xmlGenericErrorContext, "first = NULL ");
}
if (last != NULL) {
if (*last != NULL)
xmlGenericError(xmlGenericErrorContext, "last = %s ",
(*last)->name);
else
xmlGenericError(xmlGenericErrorContext, "last = NULL ");
}
#endif
switch (axis) {
case AXIS_ANCESTOR:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'ancestors' ");
#endif
first = NULL;
next = xmlXPathNextAncestor;
break;
case AXIS_ANCESTOR_OR_SELF:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
"axis 'ancestors-or-self' ");
#endif
first = NULL;
next = xmlXPathNextAncestorOrSelf;
break;
case AXIS_ATTRIBUTE:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'attributes' ");
#endif
first = NULL;
last = NULL;
next = xmlXPathNextAttribute;
break;
case AXIS_CHILD:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
#endif
last = NULL;
next = xmlXPathNextChild;
break;
case AXIS_DESCENDANT:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'descendant' ");
#endif
last = NULL;
next = xmlXPathNextDescendant;
break;
case AXIS_DESCENDANT_OR_SELF:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
"axis 'descendant-or-self' ");
#endif
last = NULL;
next = xmlXPathNextDescendantOrSelf;
break;
case AXIS_FOLLOWING:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'following' ");
#endif
last = NULL;
next = xmlXPathNextFollowing;
break;
case AXIS_FOLLOWING_SIBLING:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
"axis 'following-siblings' ");
#endif
last = NULL;
next = xmlXPathNextFollowingSibling;
break;
case AXIS_NAMESPACE:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'namespace' ");
#endif
last = NULL;
first = NULL;
next = (xmlXPathTraversalFunction) xmlXPathNextNamespace;
break;
case AXIS_PARENT:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'parent' ");
#endif
first = NULL;
next = xmlXPathNextParent;
break;
case AXIS_PRECEDING:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'preceding' ");
#endif
first = NULL;
next = xmlXPathNextPrecedingInternal;
break;
case AXIS_PRECEDING_SIBLING:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
"axis 'preceding-sibling' ");
#endif
first = NULL;
next = xmlXPathNextPrecedingSibling;
break;
case AXIS_SELF:
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "axis 'self' ");
#endif
first = NULL;
last = NULL;
next = xmlXPathNextSelf;
break;
}
if (next == NULL)
return(0);
nodelist = obj->nodesetval;
if (nodelist == NULL) {
xmlXPathFreeObject(obj);
valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
return(0);
}
addNode = xmlXPathNodeSetAddUnique;
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
" context contains %d nodes\n", nodelist->nodeNr);
switch (test) {
case NODE_TEST_NONE:
xmlGenericError(xmlGenericErrorContext,
" searching for none !!!\n");
break;
case NODE_TEST_TYPE:
xmlGenericError(xmlGenericErrorContext,
" searching for type %d\n", type);
break;
case NODE_TEST_PI:
xmlGenericError(xmlGenericErrorContext,
" searching for PI !!!\n");
break;
case NODE_TEST_ALL:
xmlGenericError(xmlGenericErrorContext,
" searching for *\n");
break;
case NODE_TEST_NS:
xmlGenericError(xmlGenericErrorContext,
" searching for namespace %s\n",
prefix);
break;
case NODE_TEST_NAME:
xmlGenericError(xmlGenericErrorContext,
" searching for name %s\n", name);
if (prefix != NULL)
xmlGenericError(xmlGenericErrorContext,
" with namespace %s\n", prefix);
break;
}
xmlGenericError(xmlGenericErrorContext, "Testing : ");
#endif
/*
* 2.3 Node Tests
* - For the attribute axis, the principal node type is attribute.
* - For the namespace axis, the principal node type is namespace.
* - For other axes, the principal node type is element.
*
* A node test * is true for any node of the
* principal node type. For example, child::* will
* select all element children of the context node
*/
tmp = ctxt->context->node;
list = xmlXPathNodeSetCreate(NULL);
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
return(-1);
}
for (i = 0; i < nodelist->nodeNr; i++) {
ctxt->context->node = nodelist->nodeTab[i];
cur = NULL;
n = 0;
do {
cur = next(ctxt, cur);
if (cur == NULL)
break;
if ((first != NULL) && (*first == cur))
break;
if (((t % 256) == 0) &&
(first != NULL) && (*first != NULL) &&
(xmlXPathCmpNodes(*first, cur) >= 0))
break;
if ((last != NULL) && (*last == cur))
break;
if (((t % 256) == 0) &&
(last != NULL) && (*last != NULL) &&
(xmlXPathCmpNodes(cur, *last) >= 0))
break;
t++;
switch (test) {
case NODE_TEST_NONE:
ctxt->context->node = tmp;
STRANGE return(0);
case NODE_TEST_TYPE:
if ((cur->type == type) ||
((type == NODE_TYPE_NODE) &&
((cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE) ||
(cur->type == XML_ELEMENT_NODE) ||
(cur->type == XML_PI_NODE) ||
(cur->type == XML_COMMENT_NODE) ||
(cur->type == XML_CDATA_SECTION_NODE) ||
(cur->type == XML_TEXT_NODE))) ||
((type == NODE_TYPE_TEXT) &&
(cur->type == XML_CDATA_SECTION_NODE))) {
n++;
if (n == indx)
addNode(list, cur);
}
break;
case NODE_TEST_PI:
if (cur->type == XML_PI_NODE) {
if ((name != NULL) &&
(!xmlStrEqual(name, cur->name)))
break;
n++;
if (n == indx)
addNode(list, cur);
}
break;
case NODE_TEST_ALL:
if (axis == AXIS_ATTRIBUTE) {
if (cur->type == XML_ATTRIBUTE_NODE) {
n++;
if (n == indx)
addNode(list, cur);
}
} else if (axis == AXIS_NAMESPACE) {
if (cur->type == XML_NAMESPACE_DECL) {
n++;
if (n == indx)
xmlXPathNodeSetAddNs(list, ctxt->context->node,
(xmlNsPtr) cur);
}
} else {
if (cur->type == XML_ELEMENT_NODE) {
if (prefix == NULL) {
n++;
if (n == indx)
addNode(list, cur);
} else if ((cur->ns != NULL) &&
(xmlStrEqual(URI, cur->ns->href))) {
n++;
if (n == indx)
addNode(list, cur);
}
}
}
break;
case NODE_TEST_NS:{
TODO;
break;
}
case NODE_TEST_NAME:
switch (cur->type) {
case XML_ELEMENT_NODE:
if (xmlStrEqual(name, cur->name)) {
if (prefix == NULL) {
if (cur->ns == NULL) {
n++;
if (n == indx)
{
addNode(list, cur);
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(list);
return(-1);
}
}
}
} else {
if ((cur->ns != NULL) &&
(xmlStrEqual(URI,
cur->ns->href))) {
n++;
if (n == indx)
addNode(list, cur);
}
}
}
break;
case XML_ATTRIBUTE_NODE:{
xmlAttrPtr attr = (xmlAttrPtr) cur;
if (xmlStrEqual(name, attr->name)) {
if (prefix == NULL) {
if ((attr->ns == NULL) ||
(attr->ns->prefix == NULL)) {
n++;
if (n == indx)
addNode(list, cur);
}
} else {
if ((attr->ns != NULL) &&
(xmlStrEqual(URI,
attr->ns->
href))) {
n++;
if (n == indx)
addNode(list, cur);
}
}
}
break;
}
case XML_NAMESPACE_DECL:
if (cur->type == XML_NAMESPACE_DECL) {
xmlNsPtr ns = (xmlNsPtr) cur;
if ((ns->prefix != NULL) && (name != NULL)
&& (xmlStrEqual(ns->prefix, name))) {
n++;
if (n == indx)
xmlXPathNodeSetAddNs(list,
ctxt->context->node, (xmlNsPtr) cur);
}
}
break;
default:
break;
}
break;
break;
}
} while (n < indx);
}
ctxt->context->node = tmp;
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
"\nExamined %d nodes, found %d nodes at that step\n",
t, list->nodeNr);
#endif
valuePush(ctxt, xmlXPathWrapNodeSet(list));
if(OOM_FLAG)
{
xmlXPathFreeObject(obj);
xmlXPathFreeNodeSet(list);
return(-1);
}
if ((obj->boolval) && (obj->user != NULL)) {
ctxt->value->boolval = 1;
ctxt->value->user = obj->user;
obj->user = NULL;
obj->boolval = 0;
}
xmlXPathFreeObject(obj);
return(t);
}
/**
* xmlXPathCompOpEvalFirst:
* @ctxt: the XPath parser context with the compiled expression
* @op: an XPath compiled operation
* @first: the first elem found so far
*
* Evaluate the Precompiled XPath operation searching only the first
* element in document order
*
* Returns the number of examined objects.
*/
static int
xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
xmlXPathStepOpPtr op, xmlNodePtr * first)
{
int total = 0, cur;
xmlXPathCompExprPtr comp;
xmlXPathObjectPtr arg1, arg2;
CHECK_ERROR0;
comp = ctxt->comp;
switch (op->op) {
case XPATH_OP_END:
return (0);
case XPATH_OP_UNION:
total =
xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1],
first);
CHECK_ERROR0;
if ((ctxt->value != NULL)
&& (ctxt->value->type == XPATH_NODESET)
&& (ctxt->value->nodesetval != NULL)
&& (ctxt->value->nodesetval->nodeNr >= 1)) {
/*
* limit tree traversing to first node in the result
*/
xmlXPathNodeSetSort(ctxt->value->nodesetval);
*first = ctxt->value->nodesetval->nodeTab[0];
}
cur =
xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch2],
first);
CHECK_ERROR0;
CHECK_TYPE0(XPATH_NODESET);
arg2 = valuePop(ctxt);
CHECK_TYPE0(XPATH_NODESET);
arg1 = valuePop(ctxt);
arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
arg2->nodesetval);
valuePush(ctxt, arg1);
xmlXPathFreeObject(arg2);
/* optimizer */
if (total > cur)
xmlXPathCompSwap(op);
return (total + cur);
case XPATH_OP_ROOT:
xmlXPathRoot(ctxt);
return (0);
case XPATH_OP_NODE:
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
return (total);
case XPATH_OP_RESET:
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
ctxt->context->node = NULL;
return (total);
case XPATH_OP_COLLECT:{
if (op->ch1 == -1)
return (total);
total = xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
/*
* Optimization for [n] selection where n is a number
*/
if ((op->ch2 != -1) &&
(comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
(comp->steps[op->ch2].ch1 == -1) &&
(comp->steps[op->ch2].ch2 != -1) &&
(comp->steps[comp->steps[op->ch2].ch2].op ==
XPATH_OP_VALUE)) {
xmlXPathObjectPtr val;
val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4;
if ((val != NULL) && (val->type == XPATH_NUMBER)) {
int indx = (int) val->floatval;
if (val->floatval == (float) indx) {
xmlXPathNodeCollectAndTestNth(ctxt, op, indx,
first, NULL);
return (total);
}
}
}
total += xmlXPathNodeCollectAndTest(ctxt, op, first, NULL);
return (total);
}
case XPATH_OP_VALUE:
valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
return (0);
case XPATH_OP_SORT:
if (op->ch1 != -1)
total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], first);
CHECK_ERROR0;
if ((ctxt->value != NULL)
&& (ctxt->value->type == XPATH_NODESET)
&& (ctxt->value->nodesetval != NULL))
{
xmlXPathNodeSetSort(ctxt->value->nodesetval);
}
return (total);
default:
return (xmlXPathCompOpEval(ctxt, op));
}
}
/**
* xmlXPathCompOpEvalLast:
* @ctxt: the XPath parser context with the compiled expression
* @op: an XPath compiled operation
* @last: the last elem found so far
*
* Evaluate the Precompiled XPath operation searching only the last
* element in document order
*
* Returns the number of nodes traversed
*/
static int
xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
xmlNodePtr * last)
{
int total = 0, cur;
xmlXPathCompExprPtr comp;
xmlXPathObjectPtr arg1, arg2;
xmlNodePtr bak;
xmlDocPtr bakd;
int pp;
int cs;
CHECK_ERROR0;
comp = ctxt->comp;
switch (op->op) {
case XPATH_OP_END:
return (0);
case XPATH_OP_UNION:
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total =
xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
CHECK_ERROR0;
if ((ctxt->value != NULL)
&& (ctxt->value->type == XPATH_NODESET)
&& (ctxt->value->nodesetval != NULL)
&& (ctxt->value->nodesetval->nodeNr >= 1)) {
/*
* limit tree traversing to first node in the result
*/
xmlXPathNodeSetSort(ctxt->value->nodesetval);
*last =
ctxt->value->nodesetval->nodeTab[ctxt->value->
nodesetval->nodeNr -
1];
}
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
cur =
xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch2], last);
CHECK_ERROR0;
if ((ctxt->value != NULL)
&& (ctxt->value->type == XPATH_NODESET)
&& (ctxt->value->nodesetval != NULL)
&& (ctxt->value->nodesetval->nodeNr >= 1)) {
}
CHECK_TYPE0(XPATH_NODESET);
arg2 = valuePop(ctxt);
CHECK_TYPE0(XPATH_NODESET);
arg1 = valuePop(ctxt);
arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
arg2->nodesetval);
valuePush(ctxt, arg1);
xmlXPathFreeObject(arg2);
/* optimizer */
if (total > cur)
xmlXPathCompSwap(op);
return (total + cur);
case XPATH_OP_ROOT:
xmlXPathRoot(ctxt);
return (0);
case XPATH_OP_NODE:
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
return (total);
case XPATH_OP_RESET:
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
ctxt->context->node = NULL;
return (total);
case XPATH_OP_COLLECT:{
if (op->ch1 == -1)
return (0);
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
/*
* Optimization for [n] selection where n is a number
*/
if ((op->ch2 != -1) &&
(comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
(comp->steps[op->ch2].ch1 == -1) &&
(comp->steps[op->ch2].ch2 != -1) &&
(comp->steps[comp->steps[op->ch2].ch2].op ==
XPATH_OP_VALUE)) {
xmlXPathObjectPtr val;
val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4;
if ((val != NULL) && (val->type == XPATH_NUMBER)) {
int indx = (int) val->floatval;
if (val->floatval == (float) indx) {
total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, last);
return (total);
}
}
}
total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, last);
return (total);
}
case XPATH_OP_VALUE:
valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
return (0);
case XPATH_OP_SORT:
if (op->ch1 != -1)
total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], last);
CHECK_ERROR0;
if ((ctxt->value != NULL)
&& (ctxt->value->type == XPATH_NODESET)
&& (ctxt->value->nodesetval != NULL))
xmlXPathNodeSetSort(ctxt->value->nodesetval);
{
return (total);
}
default:
return (xmlXPathCompOpEval(ctxt, op));
}
}
/**
* xmlXPathCompOpEval:
* @ctxt: the XPath parser context with the compiled expression
* @op: an XPath compiled operation
*
* Evaluate the Precompiled XPath operation
* Returns the number of nodes traversed or -1 when OOM
*/
static int
xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
{
int total = 0;
int equal, ret;
xmlXPathCompExprPtr comp;
xmlXPathObjectPtr arg1, arg2;
xmlNodePtr bak;
xmlDocPtr bakd;
int pp;
int cs;
CHECK_ERROR0;
comp = ctxt->comp;
//TODO: OPTIMIZE: xmlXPathContextPtr xpctxt = ctxt->context; // and reuse it below
switch (op->op)
{
case XPATH_OP_END: return (0);
case XPATH_OP_AND: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, 1);
#endif
xmlXPathBooleanFunction(ctxt, 1);
if ((ctxt->value == NULL) || (ctxt->value->boolval == 0))
return (total);
arg2 = valuePop(ctxt);
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
if (ctxt->error) {
xmlXPathFreeObject(arg2);
return(0);
}
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, 1);
#endif
xmlXPathBooleanFunction(ctxt, 1);
arg1 = valuePop(ctxt);
arg1->boolval &= arg2->boolval;
valuePush(ctxt, arg1);
xmlXPathFreeObject(arg2);
return (total);
}
case XPATH_OP_OR: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, 1);
#endif
xmlXPathBooleanFunction(ctxt, 1);
/*
#ifdef XMLENGINE_XFORMS_EXTENSIONS
// in dependency evaluation mode we must always process both parts
// of OR operation in the XPath expression
if (!ctxt->context->dependencyList && (ctxt->value || (ctxt->value->boolval == 1)))
return (total);
#else
*/
if ((ctxt->value == NULL) || (ctxt->value->boolval == 1))
return (total);
/*
#endif
*/
arg2 = valuePop(ctxt);
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
if (ctxt->error) {
xmlXPathFreeObject(arg2);
return(0);
}
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, 1);
#endif
xmlXPathBooleanFunction(ctxt, 1);
arg1 = valuePop(ctxt);
arg1->boolval |= arg2->boolval;
valuePush(ctxt, arg1);
xmlXPathFreeObject(arg2);
return (total);
}
case XPATH_OP_EQUAL: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, 2);
#endif
if (op->value)
equal = xmlXPathEqualValues(ctxt);
else
equal = xmlXPathNotEqualValues(ctxt);
valuePush(ctxt, xmlXPathNewBoolean(equal));
return (total);
}
case XPATH_OP_CMP: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, 2);
#endif
ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
valuePush(ctxt, xmlXPathNewBoolean(ret));
return (total);
}
case XPATH_OP_PLUS: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 != -1) {
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
}
CHECK_ERROR0;
#ifdef XMLENGINE_XFORMS_EXTENSIONS
// TODO: OPTIMIZATION: Can we use (op->value!=2) here? / Is reordering required?
if (op->value == 0 || op->value == 1)
addNodeSetsFromStackToDependencyList(ctxt, 2);
else if (op->value == 2)
addNodeSetsFromStackToDependencyList(ctxt, 1);
#endif
// TODO: OPTIMIZATION: Reordering accordig to value type frequency
if (op->value == 0)
xmlXPathSubValues(ctxt);
else if (op->value == 1)
xmlXPathAddValues(ctxt);
else if (op->value == 2)
xmlXPathValueFlipSign(ctxt);
else if (op->value == 3) {
CAST_TO_NUMBER;
CHECK_TYPE0(XPATH_NUMBER);
}
return (total);
}
case XPATH_OP_MULT: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, 2);
#endif
if (op->value == 0)
xmlXPathMultValues(ctxt);
else if (op->value == 1)
xmlXPathDivValues(ctxt);
else if (op->value == 2)
xmlXPathModValues(ctxt);
return (total);
}
case XPATH_OP_UNION: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
pp = ctxt->context->proximityPosition;
cs = ctxt->context->contextSize;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
ctxt->context->doc = bakd;
ctxt->context->node = bak;
ctxt->context->proximityPosition = pp;
ctxt->context->contextSize = cs;
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
CHECK_TYPE0(XPATH_NODESET);
arg2 = valuePop(ctxt);
CHECK_TYPE0(XPATH_NODESET);
arg1 = valuePop(ctxt);
arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval, arg2->nodesetval);
valuePush(ctxt, arg1);
xmlXPathFreeObject(arg2);
return (total);
}
case XPATH_OP_ROOT: {
xmlXPathRoot(ctxt);
return (total);
}
case XPATH_OP_NODE: {
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
if(OOM_FLAG) return(-1);
return (total);
}
case XPATH_OP_RESET: {
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
ctxt->context->node = NULL;
return (total);
}
case XPATH_OP_COLLECT: {
if (op->ch1 == -1)
return (total);
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
if(OOM_FLAG) return(-1);
CHECK_ERROR0;
/*
* Optimization for [n] selection where n is a number
*/
if ((op->ch2 != -1) &&
(comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
(comp->steps[op->ch2].ch1 == -1) &&
(comp->steps[op->ch2].ch2 != -1) &&
(comp->steps[comp->steps[op->ch2].ch2].op == XPATH_OP_VALUE))
{
xmlXPathObjectPtr val;
val = (xmlXPathObjectPtr)comp->steps[comp->steps[op->ch2].ch2].value4;
if ((val != NULL) && (val->type == XPATH_NUMBER)) {
int indx = (int) val->floatval;
if (val->floatval == (float) indx) {
total += xmlXPathNodeCollectAndTestNth(ctxt, op, indx, NULL, NULL);
if(OOM_FLAG) return(-1);
return (total);
}
}
}
total += xmlXPathNodeCollectAndTest(ctxt, op, NULL, NULL);
if(OOM_FLAG) return(-1);
return (total);
}
case XPATH_OP_VALUE: {
valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
return (total);
}
case XPATH_OP_VARIABLE: {
xmlXPathObjectPtr val;
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
if (op->value5 == NULL) {
val = xmlXPathVariableLookup(ctxt->context, (xmlChar*) op->value4);
if (val == NULL) {
ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
return(0);
}
valuePush(ctxt, val);
} else {
const xmlChar *URI;
URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5);
if (URI == NULL) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathCompOpEval: variable %s bound to undefined prefix %s\n"),
op->value4, op->value5);
return (total);
}
val = xmlXPathVariableLookupNS(ctxt->context, (xmlChar*) op->value4, URI);
if (val == NULL) {
ctxt->error = XPATH_UNDEF_VARIABLE_ERROR;
return(0);
}
valuePush(ctxt, val);
}
return (total);
}
case XPATH_OP_FUNCTION: {
xmlXPathFunction func;
const xmlChar *oldFunc, *oldFuncURI;
int i;
if (op->ch1 != -1)
{
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
if(OOM_FLAG) return(-1);
}
if (ctxt->valueNr < op->value) {
xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n"));
ctxt->error = XPATH_INVALID_OPERAND;
return (total);
}
for (i = 0; i < op->value; i++)
if (ctxt->valueTab[(ctxt->valueNr - 1) - i] == NULL) {
xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathCompOpEval: parameter error\n"));
ctxt->error = XPATH_INVALID_OPERAND;
return (total);
}
if (op->cache != NULL){
func = (xmlXPathFunction)op->cache;//(REINTERPRET_CAST(xmlXPathFunction,(op->cache)));
}else {
const xmlChar *URI = NULL;
if (op->value5 == NULL)
func = xmlXPathFunctionLookup(ctxt->context, (xmlChar*)op->value4);
else {
URI = xmlXPathNsLookup(ctxt->context, (xmlChar*) op->value5);
if (URI == NULL) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathCompOpEval: function %s bound to undefined prefix %s\n"),
op->value4, op->value5);
return (total);
}
func = xmlXPathFunctionLookupNS(ctxt->context, (xmlChar*)op->value4, URI);
}
if (func == NULL) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathCompOpEval: function %s not found\n"), op->value4);
XP_ERROR0(XPATH_UNKNOWN_FUNC_ERROR);
}
op->cache = (void *) func;
op->cacheURI = (void *) URI;
} // if (op->cache != NULL)
oldFunc = ctxt->context->function;
oldFuncURI = ctxt->context->functionURI;
ctxt->context->function = (xmlChar*) op->value4;
ctxt->context->functionURI = (xmlChar*) op->cacheURI;
#ifdef XMLENGINE_XFORMS_EXTENSIONS
addNodeSetsFromStackToDependencyList(ctxt, op->value);
#endif
func(ctxt, op->value);
if(OOM_FLAG) return(-1);
ctxt->context->function = oldFunc;
ctxt->context->functionURI = oldFuncURI;
return (total);
}
case XPATH_OP_ARG: {
bakd = ctxt->context->doc;
bak = ctxt->context->node;
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
ctxt->context->doc = bakd;
ctxt->context->node = bak;
CHECK_ERROR0;
if (op->ch2 != -1) {
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
if(OOM_FLAG) return(-1);
ctxt->context->doc = bakd;
ctxt->context->node = bak;
CHECK_ERROR0;
}
return (total);
}
case XPATH_OP_PREDICATE:
case XPATH_OP_FILTER: {
xmlXPathObjectPtr res;
xmlXPathObjectPtr obj, tmp;
xmlNodeSetPtr newset = NULL;
xmlNodeSetPtr oldset;
xmlNodePtr oldnode;
int i;
/*
* Optimization for ()[1] selection i.e. the first elem
*/
if ((op->ch1 != -1) && (op->ch2 != -1) &&
(comp->steps[op->ch1].op == XPATH_OP_SORT) &&
(comp->steps[op->ch2].op == XPATH_OP_VALUE))
{
xmlXPathObjectPtr val;
val = (xmlXPathObjectPtr)comp->steps[op->ch2].value4;
if ((val != NULL) && (val->type == XPATH_NUMBER) && (val->floatval == 1.0))
{
xmlNodePtr first = NULL;
total += xmlXPathCompOpEvalFirst(ctxt, &comp->steps[op->ch1], &first);
CHECK_ERROR0;
/*
* The nodeset should be in document order,
* Keep only the first value
*/
if ((ctxt->value != NULL) &&
(ctxt->value->type == XPATH_NODESET) &&
(ctxt->value->nodesetval != NULL) &&
(ctxt->value->nodesetval->nodeNr > 1))
{
ctxt->value->nodesetval->nodeNr = 1;
}
return (total);
}
}
/*
* Optimization for ()[last()] selection i.e. the last elem
*/
if ((op->ch1 != -1) && (op->ch2 != -1) &&
(comp->steps[op->ch1].op == XPATH_OP_SORT) &&
(comp->steps[op->ch2].op == XPATH_OP_SORT))
{
int f = comp->steps[op->ch2].ch1;
if ((f != -1) &&
(comp->steps[f].op == XPATH_OP_FUNCTION) &&
(comp->steps[f].value5 == NULL) &&
(comp->steps[f].value == 0) &&
(comp->steps[f].value4 != NULL) &&
(xmlStrEqual((xmlChar*)comp->steps[f].value4, BAD_CAST "last")))
{
xmlNodePtr last = NULL;
total += xmlXPathCompOpEvalLast(ctxt, &comp->steps[op->ch1], &last);
CHECK_ERROR0;
/*
* The nodeset should be in document order,
* Keep only the last value
*/
if ((ctxt->value != NULL) &&
(ctxt->value->type == XPATH_NODESET) &&
(ctxt->value->nodesetval != NULL) &&
(ctxt->value->nodesetval->nodeTab != NULL) &&
(ctxt->value->nodesetval->nodeNr > 1))
{
ctxt->value->nodesetval->nodeTab[0] =
ctxt->value->nodesetval->nodeTab[ctxt->value->nodesetval->nodeNr - 1];
ctxt->value->nodesetval->nodeNr = 1;
}
return (total);
}
}
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
if (op->ch2 == -1)
return (total);
if (ctxt->value == NULL)
return (total);
oldnode = ctxt->context->node;
#ifdef LIBXML_XPTR_ENABLED
/*
* Hum are we filtering the result of an XPointer expression
*/
if (ctxt->value->type == XPATH_LOCATIONSET)
{
xmlLocationSetPtr newlocset = NULL;
xmlLocationSetPtr oldlocset;
/*
* Extract the old locset, and then evaluate the result of the
* expression for all the element in the locset. use it to grow
* up a new locset.
*/
CHECK_TYPE0(XPATH_LOCATIONSET);
obj = valuePop(ctxt);
oldlocset = obj->user;
ctxt->context->node = NULL;
if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
ctxt->context->contextSize = 0;
ctxt->context->proximityPosition = 0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
res = valuePop(ctxt);
if (res != NULL)
xmlXPathFreeObject(res);
valuePush(ctxt, obj);
CHECK_ERROR0;
return (total);
}
newlocset = xmlXPtrLocationSetCreate(NULL);
for (i = 0; i < oldlocset->locNr; i++)
{
/*
* Run the evaluation with a node list made of a
* single item in the nodelocset.
*/
ctxt->context->node = oldlocset->locTab[i]->user;
ctxt->context->contextSize = oldlocset->locNr;
ctxt->context->proximityPosition = i + 1;
tmp = xmlXPathNewNodeSet(ctxt->context->node);
valuePush(ctxt, tmp);
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
/*
* The result of the evaluation need to be tested to
* decided whether the filter succeeded or not
*/
res = valuePop(ctxt);
if (xmlXPathEvaluatePredicateResult(ctxt, res))
{
xmlXPtrLocationSetAdd(newlocset, xmlXPathObjectCopy(oldlocset->locTab[i]));
}
/*
* Cleanup
*/
if (res != NULL)
xmlXPathFreeObject(res);
if (ctxt->value == tmp) {
res = valuePop(ctxt);
xmlXPathFreeObject(res);
}
ctxt->context->node = NULL;
} // for (i = 0; i < oldlocset->locNr; i++)
/*
* The result is used as the new evaluation locset.
*/
xmlXPathFreeObject(obj);
ctxt->context->node = NULL;
ctxt->context->contextSize = -1;
ctxt->context->proximityPosition = -1;
valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
ctxt->context->node = oldnode;
return (total);
}
#endif /* LIBXML_XPTR_ENABLED */
/*
* Extract the old set, and then evaluate the result of the
* expression for all the element in the set. use it to grow
* up a new set.
*/
CHECK_TYPE0(XPATH_NODESET);
obj = valuePop(ctxt);
oldset = obj->nodesetval;
oldnode = ctxt->context->node;
ctxt->context->node = NULL;
if ((oldset == NULL) || (oldset->nodeNr == 0))
{
ctxt->context->contextSize = 0;
ctxt->context->proximityPosition = 0;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
res = valuePop(ctxt);
if (res != NULL)
xmlXPathFreeObject(res);
valuePush(ctxt, obj);
ctxt->context->node = oldnode;
CHECK_ERROR0;
} else {
/*
* Initialize the new set.
*/
newset = xmlXPathNodeSetCreate(NULL);
if(OOM_FLAG) return(-1);
for (i = 0; i < oldset->nodeNr; i++) {
/*
* Run the evaluation with a node list made of
* a single item in the nodeset.
*/
ctxt->context->node = oldset->nodeTab[i];
tmp = xmlXPathNewNodeSet(ctxt->context->node);
valuePush(ctxt, tmp);
ctxt->context->contextSize = oldset->nodeNr;
ctxt->context->proximityPosition = i + 1;
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
/*
* The result of the evaluation needs to be tested to
* decide whether the filter succeeded or not
*/
res = valuePop(ctxt);
if (xmlXPathEvaluatePredicateResult(ctxt, res))
{
xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
}
/*
* Cleanup
*/
if (res != NULL)
xmlXPathFreeObject(res);
if (ctxt->value == tmp) {
res = valuePop(ctxt);
xmlXPathFreeObject(res);
}
ctxt->context->node = NULL;
} // (i = 0; i < oldset->nodeNr; i++)
/*
* The result is used as the new evaluation set.
*/
xmlXPathFreeObject(obj);
ctxt->context->node = NULL;
ctxt->context->contextSize = -1;
ctxt->context->proximityPosition = -1;
valuePush(ctxt, xmlXPathWrapNodeSet(newset));
}
ctxt->context->node = oldnode;
return (total);
}
case XPATH_OP_SORT: {
if (op->ch1 != -1)
{
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
if(OOM_FLAG) return(-1);
}
CHECK_ERROR0;
if ((ctxt->value != NULL) &&
(ctxt->value->type == XPATH_NODESET) &&
(ctxt->value->nodesetval != NULL))
{
xmlXPathNodeSetSort(ctxt->value->nodesetval);
}
return (total);
}
#ifdef LIBXML_XPTR_ENABLED
case XPATH_OP_RANGETO: {
xmlXPathObjectPtr range;
xmlXPathObjectPtr res, obj;
xmlXPathObjectPtr tmp;
xmlLocationSetPtr newlocset = NULL;
xmlLocationSetPtr oldlocset;
xmlNodeSetPtr oldset;
int i, j;
if (op->ch1 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
if (op->ch2 == -1)
return (total);
if (ctxt->value->type == XPATH_LOCATIONSET)
{
/*
* Extract the old locset, and then evaluate the result of the
* expression for all the element in the locset. use it to grow
* up a new locset.
*/
CHECK_TYPE0(XPATH_LOCATIONSET);
obj = valuePop(ctxt);
oldlocset = obj->user;
if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
ctxt->context->node = NULL;
ctxt->context->contextSize = 0;
ctxt->context->proximityPosition = 0;
total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]);
res = valuePop(ctxt);
if (res != NULL)
xmlXPathFreeObject(res);
valuePush(ctxt, obj);
CHECK_ERROR0;
return (total);
}
newlocset = xmlXPtrLocationSetCreate(NULL);
for (i = 0; i < oldlocset->locNr; i++) {
/*
* Run the evaluation with a node list made of a
* single item in the nodelocset.
*/
ctxt->context->node = oldlocset->locTab[i]->user;
ctxt->context->contextSize = oldlocset->locNr;
ctxt->context->proximityPosition = i + 1;
tmp = xmlXPathNewNodeSet(ctxt->context->node);
valuePush(ctxt, tmp);
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
res = valuePop(ctxt);
if (res->type == XPATH_LOCATIONSET)
{
xmlLocationSetPtr rloc = (xmlLocationSetPtr)res->user;
for (j=0; j<rloc->locNr; j++) {
range = xmlXPtrNewRange(
oldlocset->locTab[i]->user,
oldlocset->locTab[i]->index,
rloc->locTab[j]->user2,
rloc->locTab[j]->index2);
if (range != NULL) {
xmlXPtrLocationSetAdd(newlocset, range);
}
}
} else {
range = xmlXPtrNewRangeNodeObject((xmlNodePtr)oldlocset->locTab[i]->user, res);
if (range != NULL) {
xmlXPtrLocationSetAdd(newlocset,range);
}
}
/*
* Cleanup
*/
if (res != NULL)
xmlXPathFreeObject(res);
if (ctxt->value == tmp) {
res = valuePop(ctxt);
xmlXPathFreeObject(res);
}
ctxt->context->node = NULL;
}
}
else
{ /* Not a location set */
CHECK_TYPE0(XPATH_NODESET);
obj = valuePop(ctxt);
oldset = obj->nodesetval;
ctxt->context->node = NULL;
newlocset = xmlXPtrLocationSetCreate(NULL);
if (oldset != NULL) {
for (i = 0; i < oldset->nodeNr; i++) {
/*
* Run the evaluation with a node list made of a single item
* in the nodeset.
*/
ctxt->context->node = oldset->nodeTab[i];
tmp = xmlXPathNewNodeSet(ctxt->context->node);
valuePush(ctxt, tmp);
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
res = valuePop(ctxt);
range = xmlXPtrNewRangeNodeObject(oldset->nodeTab[i], res);
if (range != NULL) {
xmlXPtrLocationSetAdd(newlocset, range);
}
/*
* Cleanup
*/
if (res != NULL)
xmlXPathFreeObject(res);
if (ctxt->value == tmp) {
res = valuePop(ctxt);
xmlXPathFreeObject(res);
}
ctxt->context->node = NULL;
} // for
}
}
/*
* The result is used as the new evaluation set.
*/
xmlXPathFreeObject(obj);
ctxt->context->node = NULL;
ctxt->context->contextSize = -1;
ctxt->context->proximityPosition = -1;
valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
return (total);
}
#endif /* LIBXML_XPTR_ENABLED */
} // switch(op->op)
// None of options matched
xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("XPath: unknown precompiled operation %d\n"), op->op);
return (total);
}
/**
* xmlXPathRunEval:
* @ctxt: the XPath parser context with the compiled expression
*
* Evaluate the Precompiled XPath expression in the given context.
*/
static void
xmlXPathRunEval(xmlXPathParserContextPtr ctxt) {
xmlXPathCompExprPtr comp;
if ((ctxt == NULL) || (ctxt->comp == NULL))
return;
if (ctxt->valueTab == NULL) {
/* Allocate the value stack */
// TODO: Check what's the "magic" number here / Make a setup value
ctxt->valueTab = (xmlXPathObjectPtr *)
xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
if (ctxt->valueTab == NULL) {
xmlXPathPErrMemory(ctxt, EMBED_ERRTXT("creating evaluation context\n"));
xmlFree(ctxt);
}
ctxt->valueNr = 0;
ctxt->valueMax = 10;
ctxt->value = NULL;
}
comp = ctxt->comp;
if(comp->last < 0) {
xmlGenericError(xmlGenericErrorContext, EMBED_ERRTXT("xmlXPathRunEval: last is less than zero\n"));
return;
}
xmlXPathCompOpEval(ctxt, &comp->steps[comp->last]);
}
/************************************************************************
* *
* Public interfaces *
* *
************************************************************************/
/**
* xmlXPathEvalPredicate:
* @ctxt: the XPath context
* @res: the Predicate Expression evaluation result
*
* Evaluate a predicate result for the current node.
* A PredicateExpr is evaluated by evaluating the Expr and converting
* the result to a boolean. If the result is a number, the result will
* be converted to true if the number is equal to the position of the
* context node in the context node list (as returned by the position
* function) and will be converted to false otherwise; if the result
* is not a number, then the result will be converted as if by a call
* to the boolean function.
*
* Returns 1 if predicate is true, 0 otherwise
*/
int
xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) {
if (res == NULL) return(0);
switch (res->type) {
case XPATH_BOOLEAN:
return(res->boolval);
case XPATH_NUMBER:
return(res->floatval == ctxt->proximityPosition);
case XPATH_NODESET:
case XPATH_XSLT_TREE:
return res->nodesetval
?
res->nodesetval->nodeNr != 0
: 0;
case XPATH_STRING:
return
res->stringval &&
xmlStrlen(res->stringval) != 0;
default:
STRANGE
}
return(0);
}
/**
* xmlXPathEvaluatePredicateResult:
* @ctxt: the XPath Parser context
* @res: the Predicate Expression evaluation result
*
* Evaluate a predicate result for the current node.
* A PredicateExpr is evaluated by evaluating the Expr and converting
* the result to a boolean. If the result is a number, the result will
* be converted to true if the number is equal to the position of the
* context node in the context node list (as returned by the position
* function) and will be converted to false otherwise; if the result
* is not a number, then the result will be converted as if by a call
* to the boolean function.
*
* Returns 1 if predicate is true, 0 otherwise
*/
int
xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
xmlXPathObjectPtr res) {
if (res == NULL) return(0);
switch (res->type) {
case XPATH_BOOLEAN:
return(res->boolval);
case XPATH_NUMBER:
#if defined(__BORLANDC__) || (defined(_MSC_VER) && (_MSC_VER == 1200))
return((res->floatval == ctxt->context->proximityPosition) &&
(!xmlXPathIsNaN(res->floatval))); /* MSC pbm Mark Vakoc !*/
#else
return(res->floatval == ctxt->context->proximityPosition);
#endif
case XPATH_NODESET:
case XPATH_XSLT_TREE:
if (res->nodesetval == NULL)
return(0);
return(res->nodesetval->nodeNr != 0);
case XPATH_STRING:
return((res->stringval != NULL) &&
(xmlStrlen(res->stringval) != 0));
#ifdef LIBXML_XPTR_ENABLED
case XPATH_LOCATIONSET:{
xmlLocationSetPtr ptr = res->user;
if (ptr == NULL)
return(0);
return (ptr->locNr != 0);
}
#endif
default:
STRANGE
}
return(0);
}
/**
* xmlXPathCtxtCompile:
* @ctxt: an XPath context
* @str: the XPath expression
*
* Compile an XPath expression
*
* Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
* the caller has to free the object.
*/
xmlXPathCompExprPtr
xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
xmlXPathParserContextPtr pctxt;
xmlXPathCompExprPtr comp;
xmlXPathInit();
pctxt = xmlXPathNewParserContext(str, ctxt);
if(OOM_FLAG) return(NULL);
xmlXPathCompileExpr(pctxt);
if(OOM_FLAG)
{
xmlXPathFreeParserContext(pctxt);
return(NULL);
}
if( pctxt->error != XPATH_EXPRESSION_OK )
{
xmlXPathFreeParserContext(pctxt);
return (0);
}
if (*pctxt->cur != 0) {
/*
* aleksey: in some cases this line prints *second* error message
* (see bug #78858) and probably this should be fixed.
* However, we are not sure that all error messages are printed
* out in other places. It's not critical so we leave it as-is for now
*/
xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
comp = NULL;
} else {
comp = pctxt->comp;
pctxt->comp = NULL;
}
xmlXPathFreeParserContext(pctxt);
if (comp != NULL) {
comp->expr = xmlStrdup(str);
#ifdef DEBUG_EVAL_COUNTS
comp->string = xmlStrdup(str);
comp->nb = 0;
#endif
}
return(comp);
}
/**
* xmlXPathCompile:
* @str: the XPath expression
*
* Compile an XPath expression
*
* Returns the xmlXPathCompExprPtr resulting from the compilation or NULL.
* the caller has to free the object.
*/
xmlXPathCompExprPtr
xmlXPathCompile(const xmlChar *str) {
return(xmlXPathCtxtCompile(NULL, str));
}
/**
* xmlXPathCompiledEval:
* @comp: the compiled XPath expression
* @ctx: the XPath context
*
* Evaluate the Precompiled XPath expression in the given context.
*
* Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
* the caller has to free the object.
*/
xmlXPathObjectPtr
xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
xmlXPathParserContextPtr ctxt;
xmlXPathObjectPtr res, tmp, init = NULL;
int stack = 0;
#ifndef LIBXML_THREAD_ENABLED
//FIXIT
//static
int reentance = 0;
#endif
if ((comp == NULL) || (ctx == NULL))
return(NULL);
xmlXPathInit();
// TODO: CHECK_CONTEXT -- Replace macro with a function (too big for reuse more then once)
CHECK_CONTEXT(ctx)
#ifndef LIBXML_THREAD_ENABLED
reentance++;
if (reentance > 1)
xmlXPathDisableOptimizer = 1;
#endif
#ifdef DEBUG_EVAL_COUNTS
comp->nb++;
if ((comp->string != NULL) && (comp->nb > 100)) {
fprintf(stderr, "100 x %s\n", comp->string);
comp->nb = 0;
}
#endif
ctxt = xmlXPathCompParserContext(comp, ctx);
xmlXPathRunEval(ctxt); //might set OOM flag
if(OOM_FLAG)
{
if(ctxt)
{
ctxt->comp = NULL;
xmlXPathFreeParserContext(ctxt);
}
return(NULL);
}
if (ctxt->value == NULL) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathCompiledEval: evaluation failed\n"));
res = NULL;
} else {
res = valuePop(ctxt);
}
do {
tmp = valuePop(ctxt);
if (tmp != NULL) {
if (tmp != init)
stack++;
xmlXPathFreeObject(tmp);
}
} while (tmp != NULL);
if ((stack != 0) && (res != NULL)) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathCompiledEval: %d object left on the stack\n"),
stack);
}
if (ctxt->error != XPATH_EXPRESSION_OK) {
xmlXPathFreeObject(res);
res = NULL;
}
ctxt->comp = NULL;
xmlXPathFreeParserContext(ctxt);
#ifndef LIBXML_THREAD_ENABLED
reentance--;
#endif
return(res);
}
/**
* xmlXPathEvalExpr:
* @ctxt: the XPath Parser context
*
* Parse and evaluate an XPath expression in the given context,
* then push the result on the context stack
*
* OOM: possible --> check OOM flag
*/
void
xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
xmlXPathCompileExpr(ctxt);
CHECK_ERROR;
xmlXPathRunEval(ctxt);
}
/**
* xmlXPathEval:
* @str: the XPath expression
* @ctx: the XPath context
*
* Evaluate the XPath Location Path in the given context.
*
* Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
* the caller has to free the object.
*/
xmlXPathObjectPtr
xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
xmlXPathParserContextPtr ctxt;
xmlXPathObjectPtr res, tmp, init = NULL;
int stack = 0;
xmlXPathInit();
CHECK_CONTEXT(ctx)
ctxt = xmlXPathNewParserContext(str, ctx);
xmlXPathEvalExpr(ctxt);
if (ctxt->value == NULL) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathEval: evaluation failed\n"));
res = NULL;
} else if (*ctxt->cur != 0) {
xmlXPatherror(ctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
res = NULL;
} else {
res = valuePop(ctxt);
}
do {
tmp = valuePop(ctxt);
if (tmp != NULL) {
if (tmp != init)
stack++;
xmlXPathFreeObject(tmp);
}
} while (tmp != NULL);
if ((stack != 0) && (res != NULL)) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathEval: %d object left on the stack\n"),
stack);
}
if (ctxt->error != XPATH_EXPRESSION_OK) {
xmlXPathFreeObject(res);
res = NULL;
}
xmlXPathFreeParserContext(ctxt);
return(res);
}
/**
* xmlXPathEvalExpression:
* @str: the XPath expression
* @ctxt: the XPath context
*
* Evaluate the XPath expression in the given context.
*
* Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
* the caller has to free the object.
*/
xmlXPathObjectPtr
xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
xmlXPathParserContextPtr pctxt;
xmlXPathObjectPtr res, tmp;
int stack = 0;
xmlXPathInit();
CHECK_CONTEXT(ctxt)
pctxt = xmlXPathNewParserContext(str, ctxt);
xmlXPathEvalExpr(pctxt);
if (*pctxt->cur != 0) {
xmlXPatherror(pctxt, __FILE__, __LINE__, XPATH_EXPR_ERROR);
res = NULL;
} else {
res = valuePop(pctxt);
}
do {
tmp = valuePop(pctxt);
if (tmp != NULL) {
xmlXPathFreeObject(tmp);
stack++;
}
} while (tmp != NULL);
if ((stack != 0) && (res != NULL)) {
xmlGenericError(xmlGenericErrorContext,
EMBED_ERRTXT("xmlXPathEvalExpression: %d object left on the stack\n"),
stack);
}
xmlXPathFreeParserContext(pctxt);
return(res);
}
#ifdef XMLENGINE_XFORMS_EXTENSIONS
/* First initialises static nodeset variable from deplist (argument).
* Calls xmlXPathCompiledEval. If the result is a nodeset it is copied
* to the deplist. Otherwise modifications in xmlXPathCompOpEval have
* already filled deplist.
*/
xmlXPathObjectPtr
xmlXPathCompiledEvalWithDependencies(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx, xmlNodeSetPtr deplist)
{
xmlXPathObjectPtr res = NULL;
ctx->dependencyList = deplist;
res = xmlXPathCompiledEval(comp, ctx);
/* if the result is nodeset there are no other dependencies */
if (res != NULL && res->type == XPATH_NODESET) {
if (!xmlXPathNodeSetIsEmpty(deplist)) {
;/* this should not happen */
}
xmlXPathNodeSetMerge(deplist, res->nodesetval);
// KO: moved out of IF block // xmlXPathNodeSetSort(deplist);
}
// KO: moved out of IF block //else xmlXPathNodeSetSort(deplist);
xmlXPathNodeSetSort(deplist); /* dep has been filled with nodes */
ctx->dependencyList = NULL;
return res;
}
/* First pops of nargs values from stack (ctxt) and stores them in temporary
* array. Adds all values of type nodeset to deplist and pushes everything back
* on the stack.
*/
void addNodeSetsFromStackToDependencyList(xmlXPathParserContextPtr ctxt, int nargs)
{
xmlNodeSetPtr deplist = ctxt->context->dependencyList;
#define MAXTMPSTACK 10
xmlXPathObjectPtr tmpStack[MAXTMPSTACK] = {0,0,0,0,0,0,0,0,0,0};
int i=0;
if(!deplist) return;
// TODO: improve code
// KO: If to decide number of popped elements first (min(nargs,MAXTMPSTACK))
// then no need to prefill tmpStack with zeros and make checks in the second loop
/* copy all arguments of type nodeset to deplist */
// KO: it is better not to pop and push arguments back, but rather PEEK the values from
// the internal stack: PEEK(i) = ctxt->valueTab[ctxt->valueNr - i - 1];
// where 'i=1' is the index of the top-most element on the stack
for (i=0; (i < nargs) && (i < MAXTMPSTACK); i++) {
tmpStack[i] = valuePop(ctxt);
}
for (i= MAXTMPSTACK-1; i>=0 ; i--) {
if (tmpStack[i] != NULL) {
if (tmpStack[i]->type == XPATH_NODESET)
xmlXPathNodeSetMerge(deplist, tmpStack[i]->nodesetval);
valuePush(ctxt, tmpStack[i]);
}
}
#define p__enable_this_test
#ifdef __enable_this_test
// TODO: OPTIMIZE: try how this new version of addNodeSetsFromStackToDependencyList works
xmlXPathObjectPtr* stack = ctxt->valueTab;
int index = ctxt->valueNr - 1;
for (i= 0; i < nargs ; i++) {
xmlXPathObjectPtr obj = stack[index--];
if (obj->type == XPATH_NODESET)
xmlXPathNodeSetMerge(deplist, obj->nodesetval);
}
#endif
#undef __enable_this_test
}
/*
* xmlXFormsInstanceFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the instance() XForms function
* nodeset instance(string)
*/
void
xmlXFormsInstanceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur = NULL;
xmlXPathObjectPtr ret = NULL;
xmlDocPtr doc = NULL;
CHECK_ARITY(1);
cur = valuePop(ctxt);
if (cur == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
/* convert argument to string */
cur = xmlXPathConvertString(cur);
/* TODO: get document from store */
if (ctxt->context->instanceDocs)
{
doc = (xmlDocPtr)xmlHashLookup(ctxt->context->instanceDocs, cur->stringval);
}
/* create a node set and change context, which will be reset by
the operations: AND, OR, EQUAL, CMP, MULT, UNION, ARG */
if (doc)
{
ctxt->context->doc = doc;
ctxt->context->node = xmlDocGetRootElement(doc);
ret = xmlXPathNewNodeSet(ctxt->context->node);
}
else
{
ret = xmlXPathNewNodeSet(NULL);
}
/* push the instance on the stack */
valuePush(ctxt, ret);
/* free cur*/
xmlXPathFreeObject(cur);
}
#endif // XMLENGINE_XFROMS_EXTENSIONS
/************************************************************************
* *
* Extra functions not pertaining to the XPath spec *
* *
************************************************************************/
/**
* xmlXPathEscapeUriFunction:
* @ctxt: the XPath Parser context
* @nargs: the number of arguments
*
* Implement the escape-uri() XPath function
* string escape-uri(string $str, bool $escape-reserved)
*
* This function applies the URI escaping rules defined in section 2 of [RFC
* 2396] to the string supplied as $uri-part, which typically represents all
* or part of a URI. The effect of the function is to replace any special
* character in the string by an escape sequence of the form %xx%yy...,
* where xxyy... is the hexadecimal representation of the octets used to
* represent the character in UTF-8.
*
* The set of characters that are escaped depends on the setting of the
* boolean argument $escape-reserved.
*
* If $escape-reserved is true, all characters are escaped other than lower
* case letters a-z, upper case letters A-Z, digits 0-9, and the characters
* referred to in [RFC 2396] as "marks": specifically, "-" | "_" | "." | "!"
* | "~" | "*" | "'" | "(" | ")". The "%" character itself is escaped only
* if it is not followed by two hexadecimal digits (that is, 0-9, a-f, and
* A-F).
*
* If $escape-reserved is false, the behavior differs in that characters
* referred to in [RFC 2396] as reserved characters are not escaped. These
* characters are ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ",".
*
* [RFC 2396] does not define whether escaped URIs should use lower case or
* upper case for hexadecimal digits. To ensure that escaped URIs can be
* compared using string comparison functions, this function must always use
* the upper-case letters A-F.
*
* Generally, $escape-reserved should be set to true when escaping a string
* that is to form a single part of a URI, and to false when escaping an
* entire URI or URI reference.
*
* In the case of non-ascii characters, the string is encoded according to
* utf-8 and then converted according to RFC 2396.
*
* Examples
* xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), true())
* returns "gopher%3A%2F%2Fspinaltap.micro.umn.edu%2F00%2FWeather%2FCalifornia%2FLos%20Angeles%23ocean"
* xf:escape-uri ("gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles#ocean"), false())
* returns "gopher://spinaltap.micro.umn.edu/00/Weather/California/Los%20Angeles%23ocean"
*
*/
static void
xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr str;
int escape_reserved;
xmlBufferPtr target;
xmlChar *cptr;
xmlChar escape[4];
CHECK_ARITY(2);
escape_reserved = xmlXPathPopBoolean(ctxt);
CAST_TO_STRING;
str = valuePop(ctxt);
target = xmlBufferCreate();
escape[0] = '%';
escape[3] = 0;
if (target) {
for (cptr = str->stringval; *cptr; cptr++) {
if ((*cptr >= 'A' && *cptr <= 'Z') ||
(*cptr >= 'a' && *cptr <= 'z') ||
(*cptr >= '0' && *cptr <= '9') ||
*cptr == '-' || *cptr == '_' || *cptr == '.' ||
*cptr == '!' || *cptr == '~' || *cptr == '*' ||
*cptr == '\''|| *cptr == '(' || *cptr == ')' ||
(*cptr == '%' &&
((cptr[1] >= 'A' && cptr[1] <= 'F') ||
(cptr[1] >= 'a' && cptr[1] <= 'f') ||
(cptr[1] >= '0' && cptr[1] <= '9')) &&
((cptr[2] >= 'A' && cptr[2] <= 'F') ||
(cptr[2] >= 'a' && cptr[2] <= 'f') ||
(cptr[2] >= '0' && cptr[2] <= '9'))) ||
(!escape_reserved &&
(*cptr == ';' || *cptr == '/' || *cptr == '?' ||
*cptr == ':' || *cptr == '@' || *cptr == '&' ||
*cptr == '=' || *cptr == '+' || *cptr == '$' ||
*cptr == ',')))
{
xmlBufferAdd(target, cptr, 1);
} else {
if ((*cptr >> 4) < 10)
escape[1] = '0' + (*cptr >> 4);
else
escape[1] = 'A' - 10 + (*cptr >> 4);
if ((*cptr & 0xF) < 10)
escape[2] = '0' + (*cptr & 0xF);
else
escape[2] = 'A' - 10 + (*cptr & 0xF);
xmlBufferAdd(target, &escape[0], 3);
}
}
}
valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
xmlBufferFree(target);
xmlXPathFreeObject(str);
}
/**
* xmlXPathRegisterAllFunctions:
* @ctxt: the XPath context
*
* Registers all default XPath functions in this context
*
* Returns 0 in case of success, -1 in case of error
*/
int
xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
{ // TODO: CHECK OOM after all these and cleanup funcHash if needed
// TODO: return error code OR setup global OOM flag
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"boolean", xmlXPathBooleanFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"ceiling", xmlXPathCeilingFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"count", xmlXPathCountFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"concat", xmlXPathConcatFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"contains",xmlXPathContainsFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"id", xmlXPathIdFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"false", xmlXPathFalseFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"floor", xmlXPathFloorFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"last", xmlXPathLastFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"lang", xmlXPathLangFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"local-name", xmlXPathLocalNameFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"not", xmlXPathNotFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"name", xmlXPathNameFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"namespace-uri", xmlXPathNamespaceURIFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"normalize-space", xmlXPathNormalizeFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"number", xmlXPathNumberFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"position", xmlXPathPositionFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"round", xmlXPathRoundFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string", xmlXPathStringFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"string-length", xmlXPathStringLengthFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"starts-with", xmlXPathStartsWithFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring", xmlXPathSubstringFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-before", xmlXPathSubstringBeforeFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"substring-after", xmlXPathSubstringAfterFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"sum", xmlXPathSumFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"true", xmlXPathTrueFunction) < 0) return(-1);
if( xmlXPathRegisterFunc(ctxt, (const xmlChar*)"translate", xmlXPathTranslateFunction) < 0) return(-1);
if( xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
(const xmlChar *)"http://www.w3.org/2002/08/xquery-functions", xmlXPathEscapeUriFunction) < 0) return(-1);
// TODO: define special function that will register instance() function for XForms support
#ifdef XMLENGINE_XFORMS_EXTENSIONS
if( xmlXPathRegisterFunc(ctxt, (const xmlChar *)"instance", xmlXFormsInstanceFunction) < 0) return(-1);
#endif
return(0);
}
#endif /* LIBXML_XPATH_ENABLED */