blob: 09869863297101c8988ebf76bd5f9b9fecd9673a [file] [log] [blame]
/*
* xpath.c: XML Path Language implementation
* XPath is a language for addressing parts of an XML document,
* designed to be used by both XSLT and XPointer
*
* Reference: W3C Recommendation 16 November 1999
* http://www.w3.org/TR/1999/REC-xpath-19991116
* Public reference:
* http://www.w3.org/TR/xpath
*
* See Copyright for the status of this software
*
* Author: daniel@veillard.com
*
*/
#define IN_LIBXML
#include "libxml.h"
#include <string.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_MATH_H
#include <math.h>
#endif
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#include <libxml/xmlmemory.h>
#include <libxml/tree.h>
#include <libxml/valid.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/parserInternals.h>
#include <libxml/hash.h>
#ifdef LIBXML_XPTR_ENABLED
#include <libxml/xpointer.h>
#endif
#ifdef LIBXML_DEBUG_ENABLED
#include <libxml/debugXML.h>
#endif
#include <libxml/xmlerror.h>
#include <libxml/threads.h>
#include <libxml/globals.h>
#define TODO \
xmlGenericError(xmlGenericErrorContext, \
"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 "trionan.c"
/*
* The lack of portability of this section of the libc is annoying !
*/
double xmlXPathNAN = 0;
double xmlXPathPINF = 1;
double xmlXPathNINF = -1;
double xmlXPathNZERO = 0;
static int xmlXPathInitialized = 0;
/**
* xmlXPathInit:
*
* Initialize the XPath environment
*/
void
xmlXPathInit(void) {
if (xmlXPathInitialized) return;
xmlXPathPINF = trio_pinf();
xmlXPathNINF = trio_ninf();
xmlXPathNAN = trio_nan();
xmlXPathNZERO = trio_nzero();
xmlXPathInitialized = 1;
}
/**
* xmlXPathIsNaN:
* @val: a double value
*
* Provides a portable isnan() function to detect whether a double
* is a NotaNumber. Based on trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 if the value is a NaN, 0 otherwise
*/
int
xmlXPathIsNaN(double val) {
return(trio_isnan(val));
}
/**
* xmlXPathIsInf:
* @val: a double value
*
* Provides a portable isinf() function to detect whether a double
* is a +Infinite or -Infinite. Based on trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 vi the value is +Infinite, -1 if -Infinite, 0 otherwise
*/
int
xmlXPathIsInf(double val) {
return(trio_isinf(val));
}
#endif /* SCHEMAS or XPATH */
#ifdef LIBXML_XPATH_ENABLED
/**
* xmlXPathGetSign:
* @val: a double value
*
* Provides a portable function to detect the sign of a double
* Modified from trio code
* http://sourceforge.net/projects/ctrio/
*
* Returns 1 if the value is Negative, 0 if positive
*/
static int
xmlXPathGetSign(double val) {
return(trio_signbit(val));
}
/*
* TODO: when compatibility allows remove all "fake node libxslt" strings
* the test should just be name[0] = ' '
*/
/* #define DEBUG */
/* #define DEBUG_STEP */
/* #define DEBUG_STEP_NTH */
/* #define DEBUG_EXPR */
/* #define DEBUG_EVAL_COUNTS */
static xmlNs xmlXPathXMLNamespaceStruct = {
NULL,
XML_NAMESPACE_DECL,
XML_XML_NAMESPACE,
BAD_CAST "xml",
NULL
};
static xmlNsPtr xmlXPathXMLNamespace = &xmlXPathXMLNamespaceStruct;
#ifndef LIBXML_THREAD_ENABLED
/*
* Optimizer is disabled only when threaded apps are detected while
* the library ain't compiled for thread safety.
*/
static int xmlXPathDisableOptimizer = 0;
#endif
/************************************************************************
* *
* Error handling routines *
* *
************************************************************************/
/*
* The array xmlXPathErrorMessages corresponds to the enum xmlXPathError
*/
static const char *xmlXPathErrorMessages[] = {
"Ok\n",
"Number encoding\n",
"Unfinished literal\n",
"Start of literal\n",
"Expected $ for variable reference\n",
"Undefined variable\n",
"Invalid predicate\n",
"Invalid expression\n",
"Missing closing curly brace\n",
"Unregistered function\n",
"Invalid operand\n",
"Invalid type\n",
"Invalid number of arguments\n",
"Invalid context size\n",
"Invalid context position\n",
"Memory allocation error\n",
"Syntax error\n",
"Resource error\n",
"Sub resource error\n",
"Undefined namespace prefix\n",
"Encoding error\n",
"Char out of XML range\n"
};
/**
* xmlXPathErrMemory:
* @ctxt: an XPath context
* @extra: extra informations
*
* Handle a redefinition of attribute error
*/
static void
xmlXPathErrMemory(xmlXPathContextPtr ctxt, const char *extra)
{
if (ctxt != NULL) {
if (extra) {
xmlChar buf[200];
xmlStrPrintf(buf, 200,
BAD_CAST "Memory allocation failed : %s\n",
extra);
ctxt->lastError.message = (char *) xmlStrdup(buf);
} else {
ctxt->lastError.message = (char *)
xmlStrdup(BAD_CAST "Memory allocation failed\n");
}
ctxt->lastError.domain = XML_FROM_XPATH;
ctxt->lastError.code = XML_ERR_NO_MEMORY;
if (ctxt->error != NULL)
ctxt->error(ctxt->userData, &ctxt->lastError);
} else {
if (extra)
__xmlRaiseError(NULL, NULL, NULL,
NULL, NULL, XML_FROM_XPATH,
XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
extra, NULL, NULL, 0, 0,
"Memory allocation failed : %s\n", extra);
else
__xmlRaiseError(NULL, NULL, NULL,
NULL, NULL, XML_FROM_XPATH,
XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
NULL, NULL, NULL, 0, 0,
"Memory allocation failed\n");
}
}
/**
* xmlXPathErrMemory:
* @ctxt: an XPath parser context
* @extra: extra informations
*
* Handle a redefinition of attribute error
*/
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)
{
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;
}
ctxt->context->lastError.domain = XML_FROM_XPATH;
ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
XPATH_EXPRESSION_OK;
ctxt->context->lastError.level = XML_ERR_ERROR;
ctxt->context->lastError.str1 = (char *) xmlStrdup(ctxt->base);
ctxt->context->lastError.int1 = ctxt->cur - ctxt->base;
ctxt->context->lastError.node = ctxt->context->debugNode;
if (ctxt->context->error != NULL) {
ctxt->context->error(ctxt->context->userData,
&ctxt->context->lastError);
} else {
__xmlRaiseError(NULL, NULL, NULL,
NULL, ctxt->context->debugNode, XML_FROM_XPATH,
error + XML_XPATH_EXPRESSION_OK - XPATH_EXPRESSION_OK,
XML_ERR_ERROR, NULL, 0,
(const char *) ctxt->base, NULL, NULL,
ctxt->cur - ctxt->base, 0,
xmlXPathErrorMessages[error]);
}
}
/**
* xmlXPatherror:
* @ctxt: the XPath Parser context
* @file: the file name
* @line: the line number
* @no: the error number
*
* Formats an error message.
*/
void
xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
int line ATTRIBUTE_UNUSED, int no) {
xmlXPathErr(ctxt, no);
}
/************************************************************************
* *
* Parser Types *
* *
************************************************************************/
/*
* Types are private:
*/
typedef enum {
XPATH_OP_END=0,
XPATH_OP_AND,
XPATH_OP_OR,
XPATH_OP_EQUAL,
XPATH_OP_CMP,
XPATH_OP_PLUS,
XPATH_OP_MULT,
XPATH_OP_UNION,
XPATH_OP_ROOT,
XPATH_OP_NODE,
XPATH_OP_RESET,
XPATH_OP_COLLECT,
XPATH_OP_VALUE,
XPATH_OP_VARIABLE,
XPATH_OP_FUNCTION,
XPATH_OP_ARG,
XPATH_OP_PREDICATE,
XPATH_OP_FILTER,
XPATH_OP_SORT
#ifdef LIBXML_XPTR_ENABLED
,XPATH_OP_RANGETO
#endif
} xmlXPathOp;
typedef enum {
AXIS_ANCESTOR = 1,
AXIS_ANCESTOR_OR_SELF,
AXIS_ATTRIBUTE,
AXIS_CHILD,
AXIS_DESCENDANT,
AXIS_DESCENDANT_OR_SELF,
AXIS_FOLLOWING,
AXIS_FOLLOWING_SIBLING,
AXIS_NAMESPACE,
AXIS_PARENT,
AXIS_PRECEDING,
AXIS_PRECEDING_SIBLING,
AXIS_SELF
} xmlXPathAxisVal;
typedef enum {
NODE_TEST_NONE = 0,
NODE_TEST_TYPE = 1,
NODE_TEST_PI = 2,
NODE_TEST_ALL = 3,
NODE_TEST_NS = 4,
NODE_TEST_NAME = 5
} xmlXPathTestVal;
typedef enum {
NODE_TYPE_NODE = 0,
NODE_TYPE_COMMENT = XML_COMMENT_NODE,
NODE_TYPE_TEXT = XML_TEXT_NODE,
NODE_TYPE_PI = XML_PI_NODE
} xmlXPathTypeVal;
typedef struct _xmlXPathStepOp xmlXPathStepOp;
typedef xmlXPathStepOp *xmlXPathStepOpPtr;
struct _xmlXPathStepOp {
xmlXPathOp op; /* The identifier of the operation */
int ch1; /* First child */
int ch2; /* Second child */
int value;
int value2;
int value3;
void *value4;
void *value5;
void *cache;
void *cacheURI;
};
struct _xmlXPathCompExpr {
int nbStep; /* Number of steps in this expression */
int maxStep; /* Maximum number of steps allocated */
xmlXPathStepOp *steps; /* ops for computation of this expression */
int last; /* index of last step in expression */
xmlChar *expr; /* the expression being computed */
xmlDictPtr dict; /* the dictionnary to use if any */
#ifdef DEBUG_EVAL_COUNTS
int nb;
xmlChar *string;
#endif
};
/************************************************************************
* *
* Parser Type functions *
* *
************************************************************************/
/**
* xmlXPathNewCompExpr:
*
* Create a new Xpath component
*
* Returns the newly allocated xmlXPathCompExprPtr or NULL in case of error
*/
static xmlXPathCompExprPtr
xmlXPathNewCompExpr(void) {
xmlXPathCompExprPtr cur;
cur = (xmlXPathCompExprPtr) xmlMalloc(sizeof(xmlXPathCompExpr));
if (cur == NULL) {
xmlXPathErrMemory(NULL, "allocating component\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlXPathCompExpr));
cur->maxStep = 10;
cur->nbStep = 0;
cur->steps = (xmlXPathStepOp *) xmlMalloc(cur->maxStep *
sizeof(xmlXPathStepOp));
if (cur->steps == NULL) {
xmlXPathErrMemory(NULL, "allocating steps\n");
xmlFree(cur);
return(NULL);
}
memset(cur->steps, 0, cur->maxStep * sizeof(xmlXPathStepOp));
cur->last = -1;
#ifdef DEBUG_EVAL_COUNTS
cur->nb = 0;
#endif
return(cur);
}
/**
* xmlXPathFreeCompExpr:
* @comp: an XPATH comp
*
* Free up the memory allocated by @comp
*/
void
xmlXPathFreeCompExpr(xmlXPathCompExprPtr comp)
{
xmlXPathStepOpPtr op;
int i;
if (comp == NULL)
return;
if (comp->dict == NULL) {
for (i = 0; i < comp->nbStep; i++) {
op = &comp->steps[i];
if (op->value4 != NULL) {
if (op->op == XPATH_OP_VALUE)
xmlXPathFreeObject(op->value4);
else
xmlFree(op->value4);
}
if (op->value5 != NULL)
xmlFree(op->value5);
}
} else {
for (i = 0; i < comp->nbStep; i++) {
op = &comp->steps[i];
if (op->value4 != NULL) {
if (op->op == XPATH_OP_VALUE)
xmlXPathFreeObject(op->value4);
}
}
xmlDictFree(comp->dict);
}
if (comp->steps != NULL) {
xmlFree(comp->steps);
}
#ifdef DEBUG_EVAL_COUNTS
if (comp->string != NULL) {
xmlFree(comp->string);
}
#endif
if (comp->expr != NULL) {
xmlFree(comp->expr);
}
xmlFree(comp);
}
/**
* xmlXPathCompExprAdd:
* @comp: the compiled expression
* @ch1: first child index
* @ch2: second child index
* @op: an op
* @value: the first int value
* @value2: the second int value
* @value3: the third int value
* @value4: the first string value
* @value5: the second string value
*
* Add a step to an XPath Compiled Expression
*
* Returns -1 in case of failure, the index otherwise
*/
static int
xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2,
xmlXPathOp op, int value,
int value2, int value3, void *value4, void *value5) {
if (comp->nbStep >= comp->maxStep) {
xmlXPathStepOp *real;
comp->maxStep *= 2;
real = (xmlXPathStepOp *) xmlRealloc(comp->steps,
comp->maxStep * sizeof(xmlXPathStepOp));
if (real == NULL) {
comp->maxStep /= 2;
xmlXPathErrMemory(NULL, "adding step\n");
return(-1);
}
comp->steps = real;
}
comp->last = comp->nbStep;
comp->steps[comp->nbStep].ch1 = ch1;
comp->steps[comp->nbStep].ch2 = ch2;
comp->steps[comp->nbStep].op = op;
comp->steps[comp->nbStep].value = value;
comp->steps[comp->nbStep].value2 = value2;
comp->steps[comp->nbStep].value3 = value3;
if ((comp->dict != NULL) &&
((op == XPATH_OP_FUNCTION) || (op == XPATH_OP_VARIABLE) ||
(op == XPATH_OP_COLLECT))) {
if (value4 != NULL) {
comp->steps[comp->nbStep].value4 = (xmlChar *)
(void *)xmlDictLookup(comp->dict, value4, -1);
xmlFree(value4);
} else
comp->steps[comp->nbStep].value4 = NULL;
if (value5 != NULL) {
comp->steps[comp->nbStep].value5 = (xmlChar *)
(void *)xmlDictLookup(comp->dict, value5, -1);
xmlFree(value5);
} else
comp->steps[comp->nbStep].value5 = NULL;
} else {
comp->steps[comp->nbStep].value4 = value4;
comp->steps[comp->nbStep].value5 = value5;
}
comp->steps[comp->nbStep].cache = NULL;
return(comp->nbStep++);
}
/**
* xmlXPathCompSwap:
* @comp: the compiled expression
* @op: operation index
*
* Swaps 2 operations in the compiled expression
*/
static void
xmlXPathCompSwap(xmlXPathStepOpPtr op) {
int tmp;
#ifndef LIBXML_THREAD_ENABLED
/*
* Since this manipulates possibly shared variables, this is
* disabled if one detects that the library is used in a multithreaded
* application
*/
if (xmlXPathDisableOptimizer)
return;
#endif
tmp = op->ch1;
op->ch1 = op->ch2;
op->ch2 = tmp;
}
#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \
xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \
(op), (val), (val2), (val3), (val4), (val5))
#define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \
xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \
(op), (val), (val2), (val3), (val4), (val5))
#define PUSH_LEAVE_EXPR(op, val, val2) \
xmlXPathCompExprAdd(ctxt->comp, -1, -1, (op), (val), (val2), 0 ,NULL ,NULL)
#define PUSH_UNARY_EXPR(op, ch, val, val2) \
xmlXPathCompExprAdd(ctxt->comp, (ch), -1, (op), (val), (val2), 0 ,NULL ,NULL)
#define PUSH_BINARY_EXPR(op, ch1, ch2, val, val2) \
xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), \
(val), (val2), 0 ,NULL ,NULL)
/************************************************************************
* *
* Debugging related functions *
* *
************************************************************************/
#define STRANGE \
xmlGenericError(xmlGenericErrorContext, \
"Internal error at %s:%d\n", \
__FILE__, __LINE__);
#ifdef LIBXML_DEBUG_ENABLED
static void
xmlXPathDebugDumpNode(FILE *output, xmlNodePtr cur, int depth) {
int i;
char shift[100];
for (i = 0;((i < depth) && (i < 25));i++)
shift[2 * i] = shift[2 * i + 1] = ' ';
shift[2 * i] = shift[2 * i + 1] = 0;
if (cur == NULL) {
fprintf(output, 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
*/
extern int
valuePush(xmlXPathParserContextPtr ctxt, xmlXPathObjectPtr value)
{
if (ctxt->valueNr >= ctxt->valueMax) {
ctxt->valueMax *= 2;
ctxt->valueTab =
(xmlXPathObjectPtr *) xmlRealloc(ctxt->valueTab,
ctxt->valueMax *
sizeof(ctxt->valueTab[0]));
if (ctxt->valueTab == NULL) {
xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
return (0);
}
}
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(l,b,i,v) \
if (l == 1) b[i++] = (xmlChar) v; \
else i += xmlCopyChar(l,&b[i],v)
#define NEXTL(l) ctxt->cur += l
#define SKIP_BLANKS \
while (IS_BLANK_CH(*(ctxt->cur))) NEXT
#define CURRENT (*ctxt->cur)
#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
#ifndef DBL_DIG
#define DBL_DIG 16
#endif
#ifndef DBL_EPSILON
#define DBL_EPSILON 1E-9
#endif
#define UPPER_DOUBLE 1E9
#define LOWER_DOUBLE 1E-5
#define INTEGER_DIGITS DBL_DIG
#define FRACTION_DIGITS (DBL_DIG + 1)
#define EXPONENT_DIGITS (3 + 2)
/**
* xmlXPathFormatNumber:
* @number: number to format
* @buffer: output buffer
* @buffersize: size of output buffer
*
* Convert the number into a string representation.
*/
static void
xmlXPathFormatNumber(double number, char buffer[], int buffersize)
{
switch (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')
;
if (*ptr != '.')
ptr++;
while ((*ptr++ = *after_fraction++) != 0);
/* Finally copy result back to caller */
size = strlen(work) + 1;
if (size > buffersize) {
work[buffersize - 1] = 0;
size = buffersize;
}
memmove(buffer, work, size);
}
break;
}
}
/************************************************************************
* *
* Routines to handle NodeSets *
* *
************************************************************************/
/**
* xmlXPathOrderDocElems:
* @doc: an input document
*
* Call this routine to speed up XPath computation on static documents.
* This stamps all the element nodes with the document order
* Like for line information, the order is kept in the element->content
* field, the value stored is actually - the node number (starting at -1)
* to be able to differentiate from line numbers.
*
* Returns the number of elements found in the document or -1 in case
* of error.
*/
long
xmlXPathOrderDocElems(xmlDocPtr doc) {
long count = 0;
xmlNodePtr cur;
if (doc == NULL)
return(-1);
cur = doc->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
cur->content = (void *) (-(++count));
if (cur->children != NULL) {
cur = cur->children;
continue;
}
}
if (cur->next != NULL) {
cur = cur->next;
continue;
}
do {
cur = cur->parent;
if (cur == NULL)
break;
if (cur == (xmlNodePtr) doc) {
cur = NULL;
break;
}
if (cur->next != NULL) {
cur = cur->next;
break;
}
} while (cur != NULL);
}
return(count);
}
/**
* xmlXPathCmpNodes:
* @node1: the first node
* @node2: the second node
*
* Compare two nodes w.r.t document order
*
* Returns -2 in case of error 1 if first point < second point, 0 if
* it's the same node, -1 otherwise
*/
int
xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
int depth1, depth2;
int attr1 = 0, attr2 = 0;
xmlNodePtr attrNode1 = NULL, attrNode2 = NULL;
xmlNodePtr cur, root;
if ((node1 == NULL) || (node2 == NULL))
return(-2);
/*
* a couple of optimizations which will avoid computations in most cases
*/
if (node1->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->next)
return(-1);
for (cur = node1->next;cur != NULL;cur = cur->next)
if (cur == node2)
return(1);
return(-1); /* assume there is no sibling list corruption */
}
/**
* xmlXPathNodeSetSort:
* @set: the node set
*
* Sort the node set in document order
*/
void
xmlXPathNodeSetSort(xmlNodeSetPtr set) {
int i, j, incr, len;
xmlNodePtr tmp;
if (set == NULL)
return;
/* Use Shell's sort to sort the node-set */
len = set->nodeNr;
for (incr = len / 2; incr > 0; incr /= 2) {
for (i = incr; i < len; i++) {
j = i - incr;
while (j >= 0) {
if (xmlXPathCmpNodes(set->nodeTab[j],
set->nodeTab[j + incr]) == -1) {
tmp = set->nodeTab[j];
set->nodeTab[j] = set->nodeTab[j + incr];
set->nodeTab[j + incr] = tmp;
j -= incr;
} else
break;
}
}
}
}
#define XML_NODESET_DEFAULT 10
/**
* 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.
*/
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, "duplicating namespace\n");
return(NULL);
}
memset(cur, 0, sizeof(xmlNs));
cur->type = XML_NAMESPACE_DECL;
if (ns->href != NULL)
cur->href = xmlStrdup(ns->href);
if (ns->prefix != NULL)
cur->prefix = xmlStrdup(ns->prefix);
cur->next = (xmlNsPtr) node;
return((xmlNodePtr) cur);
}
/**
* 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
*/
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.
*/
xmlNodeSetPtr
xmlXPathNodeSetCreate(xmlNodePtr val) {
xmlNodeSetPtr ret;
ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
if (ret == NULL) {
xmlXPathErrMemory(NULL, "creating nodeset\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
if (val != NULL) {
ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
sizeof(xmlNodePtr));
if (ret->nodeTab == NULL) {
xmlXPathErrMemory(NULL, "creating nodeset\n");
xmlFree(ret);
return(NULL);
}
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;
ret->nodeTab[ret->nodeNr++] =
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
} else
ret->nodeTab[ret->nodeNr++] = val;
}
return(ret);
}
/**
* xmlXPathNodeSetContains:
* @cur: the node-set
* @val: the node
*
* checks whether @cur contains @val
*
* Returns true (1) if @cur contains @val, false (0) otherwise
*/
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);
}
/**
* 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, "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, "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, "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, "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;
#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, "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, "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, "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, "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, "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, "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, "creating nodeset\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_NODESET;
ret->boolval = 0;
ret->nodesetval = xmlXPathNodeSetCreate(val);
/* @@ 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, "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;
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, "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)
return(-1);
if (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));
return(xmlHashAddEntry2(ctxt->funcHash, name, ns_uri, (void *) f));
}
/**
* 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) {
xmlXPathObjectPtr ret;
ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
(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) {
xmlXPathObjectPtr ret;
ret = ((xmlXPathVariableLookupFunc)ctxt->varLookupFunc)
(ctxt->varLookupData, name, ns_uri);
if (ret != NULL) return(ret);
}
if (ctxt->varHash == NULL)
return(NULL);
if (name == NULL)
return(NULL);
return(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) {
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 == NULL)
return(xmlHashRemoveEntry(ctxt->nsHash, prefix,
(xmlHashDeallocator)xmlFree));
return(xmlHashUpdateEntry(ctxt->nsHash, prefix, (void *) xmlStrdup(ns_uri),
(xmlHashDeallocator)xmlFree));
}
/**
* xmlXPathNsLookup:
* @ctxt: the XPath context
* @prefix: the namespace prefix value
*
* Search in the namespace declaration array of the context for the given
* namespace name associated to the given prefix
*
* Returns the value or NULL if not found
*/
const xmlChar *
xmlXPathNsLookup(xmlXPathContextPtr ctxt, const xmlChar *prefix) {
if (ctxt == NULL)
return(NULL);
if (prefix == NULL)
return(NULL);
#ifdef XML_XML_NAMESPACE
if (xmlStrEqual(prefix, (const xmlChar *) "xml"))
return(XML_XML_NAMESPACE);
#endif
if (ctxt->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, "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, "creating boolean object\n");
return(NULL);
}
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->