mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2025-01-12 09:17:37 +03:00
fc708e2b7c
Daniel
4586 lines
123 KiB
C
4586 lines
123 KiB
C
/*
|
|
* 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 Working Draft internal 5 July 1999
|
|
* http://www.w3.org/Style/XSL/Group/1999/07/xpath-19990705.html
|
|
* Public reference:
|
|
* http://www.w3.org/TR/WD-xpath/
|
|
*
|
|
* See COPYRIGHT for the status of this software
|
|
*
|
|
* Author: Daniel.Veillard@w3.org
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
#include "win32config.h"
|
|
#else
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "xmlversion.h"
|
|
#ifdef LIBXML_XPATH_ENABLED
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_MATH_H
|
|
#include <math.h>
|
|
#endif
|
|
#ifdef HAVE_MATH_H
|
|
#include <float.h>
|
|
#endif
|
|
#ifdef HAVE_IEEEFP_H
|
|
#include <ieeefp.h>
|
|
#endif
|
|
#ifdef HAVE_NAN_H
|
|
#include <nan.h>
|
|
#endif
|
|
#ifdef HAVE_CTYPE_H
|
|
#include <ctype.h>
|
|
#endif
|
|
|
|
#include <libxml/xmlmemory.h>
|
|
#include <libxml/tree.h>
|
|
#include <libxml/valid.h>
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/parserInternals.h>
|
|
|
|
/* #define DEBUG */
|
|
/* #define DEBUG_STEP */
|
|
/* #define DEBUG_EXPR */
|
|
|
|
/*
|
|
* Setup stuff for floating point
|
|
* The lack of portability of this section of the libc is annoying !
|
|
*/
|
|
double xmlXPathNAN = 0;
|
|
double xmlXPathPINF = 1;
|
|
double xmlXPathMINF = -1;
|
|
|
|
#ifndef isinf
|
|
#ifndef HAVE_ISINF
|
|
|
|
#if HAVE_FPCLASS
|
|
|
|
int isinf(double d) {
|
|
fpclass_t type = fpclass(d);
|
|
switch (type) {
|
|
case FP_NINF:
|
|
return(-1);
|
|
case FP_PINF:
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
#elif defined(HAVE_FP_CLASS) || defined(HAVE_FP_CLASS_D)
|
|
|
|
#if HAVE_FP_CLASS_H
|
|
#include <fp_class.h>
|
|
#endif
|
|
|
|
int isinf(double d) {
|
|
#if HAVE_FP_CLASS
|
|
int fpclass = fp_class(d);
|
|
#else
|
|
int fpclass = fp_class_d(d);
|
|
#endif
|
|
if (fpclass == FP_POS_INF)
|
|
return(1);
|
|
if (fpclass == FP_NEG_INF)
|
|
return(-1);
|
|
return(0);
|
|
}
|
|
|
|
#elif defined(HAVE_CLASS)
|
|
|
|
int isinf(double d) {
|
|
int fpclass = class(d);
|
|
if (fpclass == FP_PLUS_INF)
|
|
return(1);
|
|
if (fpclass == FP_MINUS_INF)
|
|
return(-1);
|
|
return(0);
|
|
}
|
|
#elif defined(finite) || defined(HAVE_FINITE)
|
|
int isinf(double x) { return !finite(x) && x==x; }
|
|
#elif defined(HUGE_VAL)
|
|
int isinf(double x)
|
|
{
|
|
if (x == HUGE_VAL)
|
|
return(1);
|
|
if (x == -HUGE_VAL)
|
|
return(-1);
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
#endif /* ! HAVE_ISINF */
|
|
#endif /* ! defined(isinf) */
|
|
|
|
#ifndef isnan
|
|
#ifndef HAVE_ISNAN
|
|
|
|
#ifdef HAVE_ISNAND
|
|
#define isnan(f) isnand(f)
|
|
#endif /* HAVE_iSNAND */
|
|
|
|
#endif /* ! HAVE_iSNAN */
|
|
#endif /* ! defined(isnan) */
|
|
|
|
/**
|
|
* xmlXPathInit:
|
|
*
|
|
* Initialize the XPath environment
|
|
*/
|
|
void
|
|
xmlXPathInit(void) {
|
|
static int initialized = 0;
|
|
|
|
if (initialized) return;
|
|
|
|
xmlXPathNAN = 0;
|
|
xmlXPathNAN /= 0;
|
|
|
|
xmlXPathPINF = 1;
|
|
xmlXPathPINF /= 0;
|
|
|
|
xmlXPathMINF = -1;
|
|
xmlXPathMINF /= 0;
|
|
|
|
initialized = 1;
|
|
}
|
|
|
|
FILE *xmlXPathDebug = NULL;
|
|
|
|
#define TODO \
|
|
fprintf(xmlXPathDebug, "Unimplemented block at %s:%d\n", \
|
|
__FILE__, __LINE__);
|
|
|
|
#define STRANGE \
|
|
fprintf(xmlXPathDebug, "Internal error at %s:%d\n", \
|
|
__FILE__, __LINE__);
|
|
|
|
double xmlXPathStringEvalNumber(const xmlChar *str);
|
|
void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Parser stacks related functions and macros *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/*
|
|
* Generic function for accessing stacks in the Parser Context
|
|
*/
|
|
|
|
#define PUSH_AND_POP(type, name) \
|
|
extern int name##Push(xmlXPathParserContextPtr ctxt, type value) { \
|
|
if (ctxt->name##Nr >= ctxt->name##Max) { \
|
|
ctxt->name##Max *= 2; \
|
|
ctxt->name##Tab = (void *) xmlRealloc(ctxt->name##Tab, \
|
|
ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
|
|
if (ctxt->name##Tab == NULL) { \
|
|
fprintf(xmlXPathDebug, "realloc failed !\n"); \
|
|
return(0); \
|
|
} \
|
|
} \
|
|
ctxt->name##Tab[ctxt->name##Nr] = value; \
|
|
ctxt->name = value; \
|
|
return(ctxt->name##Nr++); \
|
|
} \
|
|
extern type name##Pop(xmlXPathParserContextPtr ctxt) { \
|
|
type ret; \
|
|
if (ctxt->name##Nr <= 0) return(0); \
|
|
ctxt->name##Nr--; \
|
|
if (ctxt->name##Nr > 0) \
|
|
ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
|
|
else \
|
|
ctxt->name = NULL; \
|
|
ret = ctxt->name##Tab[ctxt->name##Nr]; \
|
|
ctxt->name##Tab[ctxt->name##Nr] = 0; \
|
|
return(ret); \
|
|
} \
|
|
|
|
PUSH_AND_POP(xmlXPathObjectPtr, value)
|
|
|
|
/*
|
|
* Macros for accessing the content. Those should be used only by the parser,
|
|
* and not exported.
|
|
*
|
|
* Dirty macros, i.e. one need to make assumption on the context to use them
|
|
*
|
|
* CUR_PTR return the current pointer to the xmlChar to be parsed.
|
|
* CUR returns the current xmlChar value, i.e. a 8 bit value
|
|
* in ISO-Latin or UTF-8.
|
|
* This should be used internally by the parser
|
|
* only to compare to ASCII values otherwise it would break when
|
|
* running with UTF-8 encoding.
|
|
* NXT(n) returns the n'th next xmlChar. Same as CUR is should be used only
|
|
* to compare on ASCII based substring.
|
|
* SKIP(n) Skip n xmlChar, and must also be used only to skip ASCII defined
|
|
* strings within the parser.
|
|
* CURRENT Returns the current char value, with the full decoding of
|
|
* UTF-8 if we are using this mode. It returns an int.
|
|
* NEXT Skip to the next character, this does the proper decoding
|
|
* in UTF-8 mode. It also pop-up unfinished entities on the fly.
|
|
* It returns the pointer to the current xmlChar.
|
|
*/
|
|
|
|
#define CUR (*ctxt->cur)
|
|
#define SKIP(val) ctxt->cur += (val)
|
|
#define NXT(val) ctxt->cur[(val)]
|
|
#define CUR_PTR ctxt->cur
|
|
|
|
#define SKIP_BLANKS \
|
|
while (IS_BLANK(*(ctxt->cur))) NEXT
|
|
|
|
#define CURRENT (*ctxt->cur)
|
|
#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Error handling routines *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#define XPATH_EXPRESSION_OK 0
|
|
#define XPATH_NUMBER_ERROR 1
|
|
#define XPATH_UNFINISHED_LITERAL_ERROR 2
|
|
#define XPATH_START_LITERAL_ERROR 3
|
|
#define XPATH_VARIABLE_REF_ERROR 4
|
|
#define XPATH_UNDEF_VARIABLE_ERROR 5
|
|
#define XPATH_INVALID_PREDICATE_ERROR 6
|
|
#define XPATH_EXPR_ERROR 7
|
|
#define XPATH_UNCLOSED_ERROR 8
|
|
#define XPATH_UNKNOWN_FUNC_ERROR 9
|
|
#define XPATH_INVALID_OPERAND 10
|
|
#define XPATH_INVALID_TYPE 11
|
|
#define XPATH_INVALID_ARITY 12
|
|
|
|
const char *xmlXPathErrorMessages[] = {
|
|
"Ok",
|
|
"Number encoding",
|
|
"Unfinished litteral",
|
|
"Start of litteral",
|
|
"Expected $ for variable reference",
|
|
"Undefined variable",
|
|
"Invalid predicate",
|
|
"Invalid expression",
|
|
"Missing closing curly brace",
|
|
"Unregistered function",
|
|
"Invalid operand",
|
|
"Invalid type",
|
|
"Invalid number of arguments",
|
|
};
|
|
|
|
/**
|
|
* xmlXPathError:
|
|
* @ctxt: the XPath Parser context
|
|
* @file: the file name
|
|
* @line: the line number
|
|
* @no: the error number
|
|
*
|
|
* Create a new xmlNodeSetPtr of type double and of value @val
|
|
*
|
|
* Returns the newly created object.
|
|
*/
|
|
void
|
|
xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
|
|
int line, int no) {
|
|
int n;
|
|
const xmlChar *cur;
|
|
const xmlChar *base;
|
|
|
|
fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line,
|
|
xmlXPathErrorMessages[no]);
|
|
|
|
cur = ctxt->cur;
|
|
base = ctxt->base;
|
|
while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) {
|
|
cur--;
|
|
}
|
|
n = 0;
|
|
while ((n++ < 80) && (cur > base) && (*cur != '\n') && (*cur != '\r'))
|
|
cur--;
|
|
if ((*cur == '\n') || (*cur == '\r')) cur++;
|
|
base = cur;
|
|
n = 0;
|
|
while ((*cur != 0) && (*cur != '\n') && (*cur != '\r') && (n < 79)) {
|
|
fprintf(xmlXPathDebug, "%c", (unsigned char) *cur++);
|
|
n++;
|
|
}
|
|
fprintf(xmlXPathDebug, "\n");
|
|
cur = ctxt->cur;
|
|
while ((*cur == '\n') || (*cur == '\r'))
|
|
cur--;
|
|
n = 0;
|
|
while ((cur != base) && (n++ < 80)) {
|
|
fprintf(xmlXPathDebug, " ");
|
|
base++;
|
|
}
|
|
fprintf(xmlXPathDebug,"^\n");
|
|
}
|
|
|
|
#define CHECK_ERROR \
|
|
if (ctxt->error != XPATH_EXPRESSION_OK) return
|
|
|
|
#define ERROR(X) \
|
|
{ xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
|
|
ctxt->error = (X); return; }
|
|
|
|
#define ERROR0(X) \
|
|
{ xmlXPatherror(ctxt, __FILE__, __LINE__, X); \
|
|
ctxt->error = (X); return(0); }
|
|
|
|
#define CHECK_TYPE(typeval) \
|
|
if ((ctxt->value == NULL) || (ctxt->value->type != typeval)) \
|
|
ERROR(XPATH_INVALID_TYPE) \
|
|
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Routines to handle NodeSets *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#define XML_NODESET_DEFAULT 10
|
|
/**
|
|
* xmlXPathNodeSetCreate:
|
|
* @val: an initial xmlNodePtr, or NULL
|
|
*
|
|
* Create a new xmlNodeSetPtr of type double and of value @val
|
|
*
|
|
* Returns the newly created object.
|
|
*/
|
|
xmlNodeSetPtr
|
|
xmlXPathNodeSetCreate(xmlNodePtr val) {
|
|
xmlNodeSetPtr ret;
|
|
|
|
ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
|
|
if (ret == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlNodeSet));
|
|
if (val != NULL) {
|
|
ret->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
|
|
sizeof(xmlNodePtr));
|
|
if (ret->nodeTab == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewNodeSet: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret->nodeTab, 0 ,
|
|
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
|
|
ret->nodeMax = XML_NODESET_DEFAULT;
|
|
ret->nodeTab[ret->nodeNr++] = val;
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNodeSetAdd:
|
|
* @cur: the initial node set
|
|
* @val: a new xmlNodePtr
|
|
*
|
|
* add a new xmlNodePtr ot an existing NodeSet
|
|
*/
|
|
void
|
|
xmlXPathNodeSetAdd(xmlNodeSetPtr cur, xmlNodePtr val) {
|
|
int i;
|
|
|
|
if (val == NULL) return;
|
|
|
|
/*
|
|
* check against doublons
|
|
*/
|
|
for (i = 0;i < cur->nodeNr;i++)
|
|
if (cur->nodeTab[i] == val) return;
|
|
|
|
/*
|
|
* grow the nodeTab if needed
|
|
*/
|
|
if (cur->nodeMax == 0) {
|
|
cur->nodeTab = (xmlNodePtr *) xmlMalloc(XML_NODESET_DEFAULT *
|
|
sizeof(xmlNodePtr));
|
|
if (cur->nodeTab == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
|
|
return;
|
|
}
|
|
memset(cur->nodeTab, 0 ,
|
|
XML_NODESET_DEFAULT * (size_t) sizeof(xmlNodePtr));
|
|
cur->nodeMax = XML_NODESET_DEFAULT;
|
|
} else if (cur->nodeNr == cur->nodeMax) {
|
|
xmlNodePtr *temp;
|
|
|
|
cur->nodeMax *= 2;
|
|
temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax *
|
|
sizeof(xmlNodePtr));
|
|
if (temp == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
|
|
return;
|
|
}
|
|
cur->nodeTab = temp;
|
|
}
|
|
cur->nodeTab[cur->nodeNr++] = val;
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNodeSetMerge:
|
|
* @val1: the first NodeSet
|
|
* @val2: the second NodeSet
|
|
*
|
|
* Merges two nodesets, all nodes from @val2 are added to @val1
|
|
*
|
|
* Returns val1 once extended or NULL in case of error.
|
|
*/
|
|
xmlNodeSetPtr
|
|
xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
|
|
int i;
|
|
|
|
if (val1 == NULL) return(NULL);
|
|
if (val2 == NULL) return(val1);
|
|
|
|
/*
|
|
* !!!!! this can be optimized a lot, knowing that both
|
|
* val1 and val2 already have unicity of their values.
|
|
*/
|
|
|
|
for (i = 0;i < val2->nodeNr;i++)
|
|
xmlXPathNodeSetAdd(val1, val2->nodeTab[i]);
|
|
|
|
return(val1);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNodeSetDel:
|
|
* @cur: the initial node set
|
|
* @val: an xmlNodePtr
|
|
*
|
|
* Removes an xmlNodePtr from an existing NodeSet
|
|
*/
|
|
void
|
|
xmlXPathNodeSetDel(xmlNodeSetPtr cur, xmlNodePtr val) {
|
|
int i;
|
|
|
|
if (cur == NULL) return;
|
|
if (val == NULL) return;
|
|
|
|
/*
|
|
* check against doublons
|
|
*/
|
|
for (i = 0;i < cur->nodeNr;i++)
|
|
if (cur->nodeTab[i] == val) break;
|
|
|
|
if (i >= cur->nodeNr) {
|
|
#ifdef DEBUG
|
|
fprintf(xmlXPathDebug,
|
|
"xmlXPathNodeSetDel: Node %s wasn't found in NodeList\n",
|
|
val->name);
|
|
#endif
|
|
return;
|
|
}
|
|
cur->nodeNr--;
|
|
for (;i < cur->nodeNr;i++)
|
|
cur->nodeTab[i] = cur->nodeTab[i + 1];
|
|
cur->nodeTab[cur->nodeNr] = NULL;
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNodeSetRemove:
|
|
* @cur: the initial node set
|
|
* @val: the index to remove
|
|
*
|
|
* Removes an entry from an existing NodeSet list.
|
|
*/
|
|
void
|
|
xmlXPathNodeSetRemove(xmlNodeSetPtr cur, int val) {
|
|
if (cur == NULL) return;
|
|
if (val >= cur->nodeNr) return;
|
|
cur->nodeNr--;
|
|
for (;val < cur->nodeNr;val++)
|
|
cur->nodeTab[val] = cur->nodeTab[val + 1];
|
|
cur->nodeTab[cur->nodeNr] = NULL;
|
|
}
|
|
|
|
/**
|
|
* xmlXPathFreeNodeSet:
|
|
* @obj: the xmlNodeSetPtr to free
|
|
*
|
|
* Free the NodeSet compound (not the actual nodes !).
|
|
*/
|
|
void
|
|
xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
|
|
if (obj == NULL) return;
|
|
if (obj->nodeTab != NULL) {
|
|
#ifdef DEBUG
|
|
memset(obj->nodeTab, 0xB , (size_t) sizeof(xmlNodePtr) * obj->nodeMax);
|
|
#endif
|
|
xmlFree(obj->nodeTab);
|
|
}
|
|
#ifdef DEBUG
|
|
memset(obj, 0xB , (size_t) sizeof(xmlNodeSet));
|
|
#endif
|
|
xmlFree(obj);
|
|
}
|
|
|
|
#if defined(DEBUG) || defined(DEBUG_STEP)
|
|
/**
|
|
* xmlXPathDebugNodeSet:
|
|
* @output: a FILE * for the output
|
|
* @obj: the xmlNodeSetPtr to free
|
|
*
|
|
* Quick display of a NodeSet
|
|
*/
|
|
void
|
|
xmlXPathDebugNodeSet(FILE *output, xmlNodeSetPtr obj) {
|
|
int i;
|
|
|
|
if (output == NULL) output = xmlXPathDebug;
|
|
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
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Routines to handle Variable *
|
|
* *
|
|
* UNIMPLEMENTED CURRENTLY *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/**
|
|
* xmlXPathVariablelookup:
|
|
* @ctxt: the XPath Parser context
|
|
* @prefix: the variable name namespace if any
|
|
* @name: the variable name
|
|
*
|
|
* Search in the Variable array of the context for the given
|
|
* variable value.
|
|
*
|
|
* UNIMPLEMENTED: always return NULL.
|
|
*
|
|
* Returns the value or NULL if not found
|
|
*/
|
|
xmlXPathObjectPtr
|
|
xmlXPathVariablelookup(xmlXPathParserContextPtr ctxt,
|
|
const xmlChar *prefix, const xmlChar *name) {
|
|
return(NULL);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Routines to handle Values *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/* Allocations are terrible, one need to optimize all this !!! */
|
|
|
|
/**
|
|
* xmlXPathNewFloat:
|
|
* @val: the double value
|
|
*
|
|
* Create a new xmlXPathObjectPtr of type double and of value @val
|
|
*
|
|
* Returns the newly created object.
|
|
*/
|
|
xmlXPathObjectPtr
|
|
xmlXPathNewFloat(double val) {
|
|
xmlXPathObjectPtr ret;
|
|
|
|
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
|
|
if (ret == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
|
|
ret->type = XPATH_NUMBER;
|
|
ret->floatval = val;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNewBoolean:
|
|
* @val: the boolean value
|
|
*
|
|
* Create a new xmlXPathObjectPtr of type boolean and of value @val
|
|
*
|
|
* Returns the newly created object.
|
|
*/
|
|
xmlXPathObjectPtr
|
|
xmlXPathNewBoolean(int val) {
|
|
xmlXPathObjectPtr ret;
|
|
|
|
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
|
|
if (ret == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
|
|
ret->type = XPATH_BOOLEAN;
|
|
ret->boolval = (val != 0);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNewString:
|
|
* @val: the xmlChar * value
|
|
*
|
|
* Create a new xmlXPathObjectPtr of type string and of value @val
|
|
*
|
|
* Returns the newly created object.
|
|
*/
|
|
xmlXPathObjectPtr
|
|
xmlXPathNewString(const xmlChar *val) {
|
|
xmlXPathObjectPtr ret;
|
|
|
|
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
|
|
if (ret == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
|
|
ret->type = XPATH_STRING;
|
|
ret->stringval = xmlStrdup(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) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
|
|
ret->type = XPATH_STRING;
|
|
ret->stringval = xmlStrdup(BAD_CAST val);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
|
|
ret->type = XPATH_NODESET;
|
|
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;
|
|
|
|
ret = (xmlXPathObjectPtr) xmlMalloc(sizeof(xmlXPathObject));
|
|
if (ret == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
|
|
ret->type = XPATH_NODESET;
|
|
ret->nodesetval = val;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathFreeNodeSetList:
|
|
* @obj: an existing NodeSetList object
|
|
*
|
|
* Free up the xmlXPathObjectPtr @obj but don't deallocate the objects in
|
|
* the list contrary to xmlXPathFreeObject().
|
|
*/
|
|
void
|
|
xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
|
|
if (obj == NULL) return;
|
|
#ifdef DEBUG
|
|
memset(obj, 0xB , (size_t) sizeof(xmlXPathObject));
|
|
#endif
|
|
xmlFree(obj);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathFreeObject:
|
|
* @obj: the object to free
|
|
*
|
|
* Free up an xmlXPathObjectPtr object.
|
|
*/
|
|
void
|
|
xmlXPathFreeObject(xmlXPathObjectPtr obj) {
|
|
if (obj == NULL) return;
|
|
if (obj->nodesetval != NULL)
|
|
xmlXPathFreeNodeSet(obj->nodesetval);
|
|
if (obj->stringval != NULL)
|
|
xmlFree(obj->stringval);
|
|
#ifdef DEBUG
|
|
memset(obj, 0xB , (size_t) sizeof(xmlXPathObject));
|
|
#endif
|
|
xmlFree(obj);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Routines to handle XPath contexts *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/**
|
|
* xmlXPathNewContext:
|
|
* @doc: the XML document
|
|
*
|
|
* Create a new xmlXPathContext
|
|
*
|
|
* Returns the xmlXPathContext just allocated.
|
|
*/
|
|
xmlXPathContextPtr
|
|
xmlXPathNewContext(xmlDocPtr doc) {
|
|
xmlXPathContextPtr ret;
|
|
|
|
ret = (xmlXPathContextPtr) xmlMalloc(sizeof(xmlXPathContext));
|
|
if (ret == NULL) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewContext: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathContext));
|
|
ret->doc = doc;
|
|
/***********
|
|
ret->node = (xmlNodePtr) doc;
|
|
ret->nodelist = xmlXPathNodeSetCreate(ret->node);
|
|
***********/
|
|
ret->node = NULL;
|
|
ret->nodelist = NULL;
|
|
|
|
ret->nb_variables = 0;
|
|
ret->max_variables = 0;
|
|
ret->variables = NULL;
|
|
|
|
ret->nb_types = 0;
|
|
ret->max_types = 0;
|
|
ret->types = NULL;
|
|
|
|
ret->nb_funcs = 0;
|
|
ret->max_funcs = 0;
|
|
ret->funcs = NULL;
|
|
|
|
ret->nb_axis = 0;
|
|
ret->max_axis = 0;
|
|
ret->axis = NULL;
|
|
|
|
ret->namespaces = NULL;
|
|
ret->user = NULL;
|
|
ret->nsNr = 0;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathFreeContext:
|
|
* @ctxt: the context to free
|
|
*
|
|
* Free up an xmlXPathContext
|
|
*/
|
|
void
|
|
xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
|
|
if (ctxt->namespaces != NULL)
|
|
xmlFree(ctxt->namespaces);
|
|
|
|
/***********
|
|
if (ctxt->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->nodelist);
|
|
***********/
|
|
#ifdef DEBUG
|
|
memset(ctxt, 0xB , (size_t) sizeof(xmlXPathContext));
|
|
#endif
|
|
xmlFree(ctxt);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Routines to handle XPath parser contexts *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#define CHECK_CTXT \
|
|
if (ctxt == NULL) { \
|
|
fprintf(xmlXPathDebug, "%s:%d Internal error: ctxt == NULL\n", \
|
|
__FILE__, __LINE__); \
|
|
} \
|
|
|
|
|
|
#define CHECK_CONTEXT \
|
|
if (ctxt == NULL) { \
|
|
fprintf(xmlXPathDebug, "%s:%d Internal error: no context\n", \
|
|
__FILE__, __LINE__); \
|
|
} \
|
|
if (ctxt->doc == NULL) { \
|
|
fprintf(xmlXPathDebug, "%s:%d Internal error: no document\n", \
|
|
__FILE__, __LINE__); \
|
|
} \
|
|
if (ctxt->doc->children == NULL) { \
|
|
fprintf(xmlXPathDebug, \
|
|
"%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) {
|
|
fprintf(xmlXPathDebug, "xmlXPathNewParserContext: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
memset(ret, 0 , (size_t) sizeof(xmlXPathParserContext));
|
|
ret->cur = ret->base = str;
|
|
ret->context = ctxt;
|
|
|
|
/* Allocate the value stack */
|
|
ret->valueTab = (xmlXPathObjectPtr *)
|
|
xmlMalloc(10 * sizeof(xmlXPathObjectPtr));
|
|
ret->valueNr = 0;
|
|
ret->valueMax = 10;
|
|
ret->value = NULL;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathFreeParserContext:
|
|
* @ctxt: the context to free
|
|
*
|
|
* Free up an xmlXPathParserContext
|
|
*/
|
|
void
|
|
xmlXPathFreeParserContext(xmlXPathParserContextPtr ctxt) {
|
|
if (ctxt->valueTab != NULL) {
|
|
#ifdef DEBUG
|
|
memset(ctxt->valueTab, 0xB , 10 * (size_t) sizeof(xmlXPathObjectPtr));
|
|
#endif
|
|
xmlFree(ctxt->valueTab);
|
|
}
|
|
#ifdef DEBUG
|
|
memset(ctxt, 0xB , (size_t) sizeof(xmlXPathParserContext));
|
|
#endif
|
|
xmlFree(ctxt);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* The implicit core function library *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/*
|
|
* Auto-pop and cast to a number
|
|
*/
|
|
void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
|
|
|
|
#define CHECK_ARITY(x) \
|
|
if (nargs != (x)) { \
|
|
ERROR(XPATH_INVALID_ARITY); \
|
|
} \
|
|
|
|
|
|
#define POP_FLOAT \
|
|
arg = valuePop(ctxt); \
|
|
if (arg == NULL) { \
|
|
ERROR(XPATH_INVALID_OPERAND); \
|
|
} \
|
|
if (arg->type != XPATH_NUMBER) { \
|
|
valuePush(ctxt, arg); \
|
|
xmlXPathNumberFunction(ctxt, 1); \
|
|
arg = valuePop(ctxt); \
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEqualNodeSetString
|
|
* @arg: the nodeset object argument
|
|
* @str: the string to compare to.
|
|
*
|
|
* Implement the equal operation on XPath objects content: @arg1 == @arg2
|
|
* If one object to be compared is a node-set and the other is a string,
|
|
* then the comparison will be true if and only if there is a node in
|
|
* the node-set such that the result of performing the comparison on the
|
|
* string-value of the node and the other string is true.
|
|
*
|
|
* Returns 0 or 1 depending on the results of the test.
|
|
*/
|
|
int
|
|
xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) {
|
|
int i;
|
|
xmlNodeSetPtr ns;
|
|
xmlChar *str2;
|
|
|
|
if ((str == NULL) || (arg == NULL) || (arg->type != XPATH_NODESET))
|
|
return(0);
|
|
ns = arg->nodesetval;
|
|
for (i = 0;i < ns->nodeNr;i++) {
|
|
str2 = xmlNodeGetContent(ns->nodeTab[i]);
|
|
if ((str2 != NULL) && (!xmlStrcmp(str, str2))) {
|
|
xmlFree(str2);
|
|
return(1);
|
|
}
|
|
xmlFree(str2);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEqualNodeSetFloat
|
|
* @arg: the nodeset object argument
|
|
* @f: the float to compare to
|
|
*
|
|
* Implement the equal operation on XPath objects content: @arg1 == @arg2
|
|
* If one object to be compared is a node-set and the other is a number,
|
|
* then the comparison will be true if and only if there is a node in
|
|
* the node-set such that the result of performing the comparison on the
|
|
* number to be compared and on the result of converting the string-value
|
|
* of that node to a number using the number function is true.
|
|
*
|
|
* Returns 0 or 1 depending on the results of the test.
|
|
*/
|
|
int
|
|
xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, float f) {
|
|
char buf[100] = "";
|
|
|
|
if ((arg == NULL) || (arg->type != XPATH_NODESET))
|
|
return(0);
|
|
|
|
if (isnan(f))
|
|
sprintf(buf, "NaN");
|
|
else if (isinf(f) > 0)
|
|
sprintf(buf, "+Infinity");
|
|
else if (isinf(f) < 0)
|
|
sprintf(buf, "-Infinity");
|
|
else
|
|
sprintf(buf, "%0g", f);
|
|
|
|
return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
|
|
}
|
|
|
|
|
|
/**
|
|
* xmlXPathEqualNodeSets
|
|
* @arg1: first nodeset object argument
|
|
* @arg2: second nodeset object argument
|
|
*
|
|
* Implement the equal operation on XPath nodesets: @arg1 == @arg2
|
|
* If both objects to be compared are node-sets, then the comparison
|
|
* will be true if and only if there is a node in the first node-set and
|
|
* a node in the second node-set such that the result of performing the
|
|
* comparison on the string-values of the two nodes is true.
|
|
*
|
|
* (needless to say, this is a costly operation)
|
|
*
|
|
* Returns 0 or 1 depending on the results of the test.
|
|
*/
|
|
int
|
|
xmlXPathEqualNodeSets(xmlXPathObjectPtr arg1, xmlXPathObjectPtr arg2) {
|
|
int i;
|
|
xmlNodeSetPtr ns;
|
|
xmlChar *str;
|
|
|
|
if ((arg1 == NULL) || (arg1->type != XPATH_NODESET))
|
|
return(0);
|
|
if ((arg2 == NULL) || (arg2->type != XPATH_NODESET))
|
|
return(0);
|
|
|
|
ns = arg1->nodesetval;
|
|
for (i = 0;i < ns->nodeNr;i++) {
|
|
str = xmlNodeGetContent(ns->nodeTab[i]);
|
|
if ((str != NULL) && (xmlXPathEqualNodeSetString(arg2, str))) {
|
|
xmlFree(str);
|
|
return(1);
|
|
}
|
|
xmlFree(str);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEqualValues:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the equal operation on XPath objects content: @arg1 == @arg2
|
|
*
|
|
* Returns 0 or 1 depending on the results of the test.
|
|
*/
|
|
int
|
|
xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathObjectPtr arg1, arg2;
|
|
int ret = 0;
|
|
|
|
arg1 = valuePop(ctxt);
|
|
if (arg1 == NULL)
|
|
ERROR0(XPATH_INVALID_OPERAND);
|
|
|
|
arg2 = valuePop(ctxt);
|
|
if (arg2 == NULL) {
|
|
xmlXPathFreeObject(arg1);
|
|
ERROR0(XPATH_INVALID_OPERAND);
|
|
}
|
|
|
|
if (arg1 == arg2) {
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Equal: by pointer\n");
|
|
#endif
|
|
return(1);
|
|
}
|
|
|
|
switch (arg1->type) {
|
|
case XPATH_UNDEFINED:
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Equal: undefined\n");
|
|
#endif
|
|
break;
|
|
case XPATH_NODESET:
|
|
switch (arg2->type) {
|
|
case XPATH_UNDEFINED:
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Equal: undefined\n");
|
|
#endif
|
|
break;
|
|
case XPATH_NODESET:
|
|
ret = xmlXPathEqualNodeSets(arg1, arg2);
|
|
break;
|
|
case XPATH_BOOLEAN:
|
|
if ((arg1->nodesetval == NULL) ||
|
|
(arg1->nodesetval->nodeNr == 0)) ret = 0;
|
|
else
|
|
ret = 1;
|
|
ret = (ret == arg2->boolval);
|
|
break;
|
|
case XPATH_NUMBER:
|
|
ret = xmlXPathEqualNodeSetFloat(arg1, arg2->floatval);
|
|
break;
|
|
case XPATH_STRING:
|
|
ret = xmlXPathEqualNodeSetString(arg1, arg2->stringval);
|
|
break;
|
|
}
|
|
break;
|
|
case XPATH_BOOLEAN:
|
|
switch (arg2->type) {
|
|
case XPATH_UNDEFINED:
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Equal: undefined\n");
|
|
#endif
|
|
break;
|
|
case XPATH_NODESET:
|
|
if ((arg2->nodesetval == NULL) ||
|
|
(arg2->nodesetval->nodeNr == 0)) ret = 0;
|
|
else
|
|
ret = 1;
|
|
break;
|
|
case XPATH_BOOLEAN:
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Equal: %d boolean %d \n",
|
|
arg1->boolval, arg2->boolval);
|
|
#endif
|
|
ret = (arg1->boolval == arg2->boolval);
|
|
break;
|
|
case XPATH_NUMBER:
|
|
if (arg2->floatval) ret = 1;
|
|
else ret = 0;
|
|
ret = (arg1->boolval == ret);
|
|
break;
|
|
case XPATH_STRING:
|
|
if ((arg2->stringval == NULL) ||
|
|
(arg2->stringval[0] == 0)) ret = 0;
|
|
else
|
|
ret = 1;
|
|
ret = (arg1->boolval == ret);
|
|
break;
|
|
}
|
|
break;
|
|
case XPATH_NUMBER:
|
|
switch (arg2->type) {
|
|
case XPATH_UNDEFINED:
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Equal: undefined\n");
|
|
#endif
|
|
break;
|
|
case XPATH_NODESET:
|
|
ret = xmlXPathEqualNodeSetFloat(arg2, arg1->floatval);
|
|
break;
|
|
case XPATH_BOOLEAN:
|
|
if (arg1->floatval) ret = 1;
|
|
else ret = 0;
|
|
ret = (arg2->boolval == ret);
|
|
break;
|
|
case XPATH_STRING:
|
|
valuePush(ctxt, arg2);
|
|
xmlXPathNumberFunction(ctxt, 1);
|
|
arg2 = valuePop(ctxt);
|
|
/* no break on purpose */
|
|
case XPATH_NUMBER:
|
|
ret = (arg1->floatval == arg2->floatval);
|
|
break;
|
|
}
|
|
break;
|
|
case XPATH_STRING:
|
|
switch (arg2->type) {
|
|
case XPATH_UNDEFINED:
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Equal: undefined\n");
|
|
#endif
|
|
break;
|
|
case XPATH_NODESET:
|
|
ret = xmlXPathEqualNodeSetString(arg2, arg1->stringval);
|
|
break;
|
|
case XPATH_BOOLEAN:
|
|
if ((arg1->stringval == NULL) ||
|
|
(arg1->stringval[0] == 0)) ret = 0;
|
|
else
|
|
ret = 1;
|
|
ret = (arg2->boolval == ret);
|
|
break;
|
|
case XPATH_STRING:
|
|
ret = !xmlStrcmp(arg1->stringval, arg2->stringval);
|
|
break;
|
|
case XPATH_NUMBER:
|
|
valuePush(ctxt, arg1);
|
|
xmlXPathNumberFunction(ctxt, 1);
|
|
arg1 = valuePop(ctxt);
|
|
ret = (arg1->floatval == arg2->floatval);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
xmlXPathFreeObject(arg1);
|
|
xmlXPathFreeObject(arg2);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathCompareValues:
|
|
* @ctxt: the XPath Parser context
|
|
* @inf: less than (1) or greater than (2)
|
|
* @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.
|
|
*/
|
|
int
|
|
xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
|
|
int ret = 0;
|
|
xmlXPathObjectPtr arg1, arg2;
|
|
|
|
arg2 = valuePop(ctxt);
|
|
if ((arg2 == NULL) || (arg2->type == XPATH_NODESET)) {
|
|
if (arg2 != NULL)
|
|
xmlXPathFreeObject(arg2);
|
|
ERROR0(XPATH_INVALID_OPERAND);
|
|
}
|
|
|
|
arg1 = valuePop(ctxt);
|
|
if ((arg1 == NULL) || (arg1->type == XPATH_NODESET)) {
|
|
if (arg1 != NULL)
|
|
xmlXPathFreeObject(arg1);
|
|
xmlXPathFreeObject(arg2);
|
|
ERROR0(XPATH_INVALID_OPERAND);
|
|
}
|
|
|
|
if (arg1->type != XPATH_NUMBER) {
|
|
valuePush(ctxt, arg1);
|
|
xmlXPathNumberFunction(ctxt, 1);
|
|
arg1 = valuePop(ctxt);
|
|
}
|
|
if (arg1->type != XPATH_NUMBER) {
|
|
xmlXPathFreeObject(arg1);
|
|
xmlXPathFreeObject(arg2);
|
|
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);
|
|
ERROR0(XPATH_INVALID_OPERAND);
|
|
}
|
|
/*
|
|
* Add tests for infinity and nan
|
|
* => feedback on 3.4 for Inf and NaN
|
|
*/
|
|
if (inf && strict)
|
|
ret = (arg1->floatval < arg2->floatval);
|
|
else if (inf && !strict)
|
|
ret = (arg1->floatval <= arg2->floatval);
|
|
else if (!inf && strict)
|
|
ret = (arg1->floatval > arg2->floatval);
|
|
else if (!inf && !strict)
|
|
ret = (arg1->floatval >= arg2->floatval);
|
|
xmlXPathFreeObject(arg1);
|
|
xmlXPathFreeObject(arg2);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathValueFlipSign:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the unary - operation on an XPath object
|
|
* The numeric operators convert their operands to numbers as if
|
|
* by calling the number function.
|
|
*/
|
|
void
|
|
xmlXPathValueFlipSign(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathObjectPtr arg;
|
|
|
|
POP_FLOAT
|
|
arg->floatval = -arg->floatval;
|
|
valuePush(ctxt, arg);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathAddValues:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the add operation on XPath objects:
|
|
* The numeric operators convert their operands to numbers as if
|
|
* by calling the number function.
|
|
*/
|
|
void
|
|
xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathObjectPtr arg;
|
|
double val;
|
|
|
|
POP_FLOAT
|
|
val = arg->floatval;
|
|
xmlXPathFreeObject(arg);
|
|
|
|
POP_FLOAT
|
|
arg->floatval += val;
|
|
valuePush(ctxt, arg);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathSubValues:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the substraction operation on XPath objects:
|
|
* The numeric operators convert their operands to numbers as if
|
|
* by calling the number function.
|
|
*/
|
|
void
|
|
xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathObjectPtr arg;
|
|
double val;
|
|
|
|
POP_FLOAT
|
|
val = arg->floatval;
|
|
xmlXPathFreeObject(arg);
|
|
|
|
POP_FLOAT
|
|
arg->floatval -= val;
|
|
valuePush(ctxt, arg);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathMultValues:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the multiply operation on XPath objects:
|
|
* The numeric operators convert their operands to numbers as if
|
|
* by calling the number function.
|
|
*/
|
|
void
|
|
xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathObjectPtr arg;
|
|
double val;
|
|
|
|
POP_FLOAT
|
|
val = arg->floatval;
|
|
xmlXPathFreeObject(arg);
|
|
|
|
POP_FLOAT
|
|
arg->floatval *= val;
|
|
valuePush(ctxt, arg);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathDivValues:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the div operation on XPath objects:
|
|
* The numeric operators convert their operands to numbers as if
|
|
* by calling the number function.
|
|
*/
|
|
void
|
|
xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathObjectPtr arg;
|
|
double val;
|
|
|
|
POP_FLOAT
|
|
val = arg->floatval;
|
|
xmlXPathFreeObject(arg);
|
|
|
|
POP_FLOAT
|
|
arg->floatval /= val;
|
|
valuePush(ctxt, arg);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathModValues:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the div 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 val;
|
|
|
|
POP_FLOAT
|
|
val = arg->floatval;
|
|
xmlXPathFreeObject(arg);
|
|
|
|
POP_FLOAT
|
|
arg->floatval /= val;
|
|
valuePush(ctxt, arg);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* The traversal functions *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#define AXIS_ANCESTOR 1
|
|
#define AXIS_ANCESTOR_OR_SELF 2
|
|
#define AXIS_ATTRIBUTE 3
|
|
#define AXIS_CHILD 4
|
|
#define AXIS_DESCENDANT 5
|
|
#define AXIS_DESCENDANT_OR_SELF 6
|
|
#define AXIS_FOLLOWING 7
|
|
#define AXIS_FOLLOWING_SIBLING 8
|
|
#define AXIS_NAMESPACE 9
|
|
#define AXIS_PARENT 10
|
|
#define AXIS_PRECEDING 11
|
|
#define AXIS_PRECEDING_SIBLING 12
|
|
#define AXIS_SELF 13
|
|
|
|
/*
|
|
* 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);
|
|
|
|
/**
|
|
* mlXPathNextSelf:
|
|
* @ctxt: the XPath Parser context
|
|
* @cur: the current node in the traversal
|
|
*
|
|
* Traversal function for the "self" direction
|
|
* he 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);
|
|
}
|
|
|
|
/**
|
|
* mlXPathNextChild:
|
|
* @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:
|
|
return(((xmlDocPtr) ctxt->context->node)->children);
|
|
case XML_ELEMENT_DECL:
|
|
case XML_ATTRIBUTE_DECL:
|
|
case XML_ENTITY_DECL:
|
|
case XML_ATTRIBUTE_NODE:
|
|
return(NULL);
|
|
}
|
|
return(NULL);
|
|
}
|
|
if ((cur->type == XML_DOCUMENT_NODE) ||
|
|
(cur->type == XML_HTML_DOCUMENT_NODE))
|
|
return(NULL);
|
|
return(cur->next);
|
|
}
|
|
|
|
/**
|
|
* mlXPathNextDescendant:
|
|
* @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)
|
|
return(NULL);
|
|
|
|
if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
|
|
return(ctxt->context->doc->children);
|
|
return(ctxt->context->node->children);
|
|
}
|
|
|
|
if (cur->children != NULL) return(cur->children);
|
|
if (cur->next != NULL) return(cur->next);
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* mlXPathNextDescendantOrSelf:
|
|
* @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)
|
|
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_ENTITY_DECL:
|
|
if (ctxt->context->node->parent == NULL)
|
|
return((xmlNodePtr) ctxt->context->doc);
|
|
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:
|
|
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:
|
|
if (ctxt->context->node->parent == NULL)
|
|
return((xmlNodePtr) ctxt->context->doc);
|
|
return(ctxt->context->node->parent);
|
|
case XML_ATTRIBUTE_NODE: {
|
|
xmlAttrPtr cur = (xmlAttrPtr) ctxt->context->node;
|
|
|
|
return(cur->parent);
|
|
}
|
|
case XML_DOCUMENT_NODE:
|
|
case XML_DOCUMENT_TYPE_NODE:
|
|
case XML_DOCUMENT_FRAG_NODE:
|
|
case XML_HTML_DOCUMENT_NODE:
|
|
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:
|
|
return(cur->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:
|
|
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 (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 (cur == (xmlNodePtr) ctxt->context->doc)
|
|
return(NULL);
|
|
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 == (xmlNodePtr) ctxt->context->doc)
|
|
return(NULL);
|
|
if (cur == NULL)
|
|
return(ctxt->context->node->next);; /* !!!!!!!!! */
|
|
if (cur->children != NULL) return(cur->children);
|
|
if (cur->next != NULL) return(cur->next);
|
|
|
|
do {
|
|
cur = cur->parent;
|
|
if (cur == NULL) return(NULL);
|
|
if (cur == ctxt->context->doc->children) return(NULL);
|
|
if (cur->next != NULL) {
|
|
cur = cur->next;
|
|
return(cur);
|
|
}
|
|
} while (cur != NULL);
|
|
return(cur);
|
|
}
|
|
|
|
/**
|
|
* 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 == (xmlNodePtr) ctxt->context->doc)
|
|
return(NULL);
|
|
if (cur == NULL)
|
|
return(ctxt->context->node->prev); /* !!!!!!!!! */
|
|
if (cur->last != NULL) return(cur->last);
|
|
if (cur->prev != NULL) return(cur->prev);
|
|
|
|
do {
|
|
cur = cur->parent;
|
|
if (cur == NULL) return(NULL);
|
|
if (cur == ctxt->context->doc->children) return(NULL);
|
|
if (cur->prev != NULL) {
|
|
cur = cur->prev;
|
|
return(cur);
|
|
}
|
|
} while (cur != NULL);
|
|
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
|
|
*
|
|
* Returns the next element following that axis
|
|
*/
|
|
xmlNsPtr
|
|
xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
|
|
if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
|
|
if (ctxt->context->namespaces != NULL)
|
|
xmlFree(ctxt->context->namespaces);
|
|
ctxt->context->namespaces =
|
|
xmlGetNsList(ctxt->context->doc, ctxt->context->node);
|
|
if (ctxt->context->namespaces == NULL) return(NULL);
|
|
ctxt->context->nsNr = 0;
|
|
}
|
|
return(ctxt->context->namespaces[ctxt->context->nsNr++]);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
xmlAttrPtr
|
|
xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
|
|
if (cur == NULL) {
|
|
if (ctxt->context->node == (xmlNodePtr) ctxt->context->doc)
|
|
return(NULL);
|
|
return(ctxt->context->node->properties);
|
|
}
|
|
return(cur->next);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* NodeTest Functions *
|
|
* *
|
|
************************************************************************/
|
|
|
|
#define NODE_TEST_NONE 0
|
|
#define NODE_TEST_TYPE 1
|
|
#define NODE_TEST_PI 2
|
|
#define NODE_TEST_ALL 3
|
|
#define NODE_TEST_NS 4
|
|
#define NODE_TEST_NAME 5
|
|
|
|
#define NODE_TYPE_COMMENT 50
|
|
#define NODE_TYPE_TEXT 51
|
|
#define NODE_TYPE_PI 52
|
|
#define NODE_TYPE_NODE 53
|
|
|
|
#define IS_FUNCTION 200
|
|
|
|
/**
|
|
* xmlXPathNodeCollectAndTest:
|
|
* @ctxt: the XPath Parser context
|
|
* @cur: the current node to test
|
|
*
|
|
* 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.
|
|
*
|
|
* Returns the new NodeSet resulting from the search.
|
|
*/
|
|
xmlNodeSetPtr
|
|
xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, int axis,
|
|
int test, int type, const xmlChar *prefix, const xmlChar *name) {
|
|
#ifdef DEBUG_STEP
|
|
int n = 0, t = 0;
|
|
#endif
|
|
int i;
|
|
xmlNodeSetPtr ret;
|
|
xmlXPathTraversalFunction next = NULL;
|
|
xmlNodePtr cur = NULL;
|
|
|
|
if (ctxt->context->nodelist == NULL) {
|
|
if (ctxt->context->node == NULL) {
|
|
fprintf(xmlXPathDebug,
|
|
"xmlXPathNodeCollectAndTest %s:%d : nodelist and node are NULL\n",
|
|
__FILE__, __LINE__);
|
|
return(NULL);
|
|
}
|
|
STRANGE
|
|
return(NULL);
|
|
}
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "new step : ");
|
|
#endif
|
|
switch (axis) {
|
|
case AXIS_ANCESTOR:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'ancestors' ");
|
|
#endif
|
|
next = xmlXPathNextAncestor; break;
|
|
case AXIS_ANCESTOR_OR_SELF:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'ancestors-or-self' ");
|
|
#endif
|
|
next = xmlXPathNextAncestorOrSelf; break;
|
|
case AXIS_ATTRIBUTE:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'attributes' ");
|
|
#endif
|
|
next = (xmlXPathTraversalFunction) xmlXPathNextAttribute; break;
|
|
break;
|
|
case AXIS_CHILD:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'child' ");
|
|
#endif
|
|
next = xmlXPathNextChild; break;
|
|
case AXIS_DESCENDANT:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'descendant' ");
|
|
#endif
|
|
next = xmlXPathNextDescendant; break;
|
|
case AXIS_DESCENDANT_OR_SELF:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'descendant-or-self' ");
|
|
#endif
|
|
next = xmlXPathNextDescendantOrSelf; break;
|
|
case AXIS_FOLLOWING:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'following' ");
|
|
#endif
|
|
next = xmlXPathNextFollowing; break;
|
|
case AXIS_FOLLOWING_SIBLING:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'following-siblings' ");
|
|
#endif
|
|
next = xmlXPathNextFollowingSibling; break;
|
|
case AXIS_NAMESPACE:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'namespace' ");
|
|
#endif
|
|
next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
|
|
break;
|
|
case AXIS_PARENT:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'parent' ");
|
|
#endif
|
|
next = xmlXPathNextParent; break;
|
|
case AXIS_PRECEDING:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'preceding' ");
|
|
#endif
|
|
next = xmlXPathNextPreceding; break;
|
|
case AXIS_PRECEDING_SIBLING:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'preceding-sibling' ");
|
|
#endif
|
|
next = xmlXPathNextPrecedingSibling; break;
|
|
case AXIS_SELF:
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "axis 'self' ");
|
|
#endif
|
|
next = xmlXPathNextSelf; break;
|
|
}
|
|
if (next == NULL) return(NULL);
|
|
ret = xmlXPathNodeSetCreate(NULL);
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, " context contains %d nodes\n",
|
|
ctxt->context->nodelist->nodeNr);
|
|
switch (test) {
|
|
case NODE_TEST_NONE:
|
|
fprintf(xmlXPathDebug, " searching for none !!!\n");
|
|
break;
|
|
case NODE_TEST_TYPE:
|
|
fprintf(xmlXPathDebug, " searching for type %d\n", type);
|
|
break;
|
|
case NODE_TEST_PI:
|
|
fprintf(xmlXPathDebug, " searching for PI !!!\n");
|
|
break;
|
|
case NODE_TEST_ALL:
|
|
fprintf(xmlXPathDebug, " searching for *\n");
|
|
break;
|
|
case NODE_TEST_NS:
|
|
fprintf(xmlXPathDebug, " searching for namespace %s\n",
|
|
prefix);
|
|
break;
|
|
case NODE_TEST_NAME:
|
|
fprintf(xmlXPathDebug, " searching for name %s\n", name);
|
|
if (prefix != NULL)
|
|
fprintf(xmlXPathDebug, " with namespace %s\n",
|
|
prefix);
|
|
break;
|
|
}
|
|
fprintf(xmlXPathDebug, "Testing : ");
|
|
#endif
|
|
for (i = 0;i < ctxt->context->nodelist->nodeNr; i++) {
|
|
ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
|
|
|
|
cur = NULL;
|
|
do {
|
|
cur = next(ctxt, cur);
|
|
if (cur == NULL) break;
|
|
#ifdef DEBUG_STEP
|
|
t++;
|
|
fprintf(xmlXPathDebug, " %s", cur->name);
|
|
#endif
|
|
switch (test) {
|
|
case NODE_TEST_NONE:
|
|
STRANGE
|
|
return(NULL);
|
|
case NODE_TEST_TYPE:
|
|
if ((cur->type == type) ||
|
|
((type == XML_ELEMENT_NODE) &&
|
|
((cur->type == XML_DOCUMENT_NODE) ||
|
|
(cur->type == XML_HTML_DOCUMENT_NODE)))) {
|
|
#ifdef DEBUG_STEP
|
|
n++;
|
|
#endif
|
|
xmlXPathNodeSetAdd(ret, cur);
|
|
}
|
|
break;
|
|
case NODE_TEST_PI:
|
|
if (cur->type == XML_PI_NODE) {
|
|
if ((name != NULL) &&
|
|
(xmlStrcmp(name, cur->name)))
|
|
break;
|
|
#ifdef DEBUG_STEP
|
|
n++;
|
|
#endif
|
|
xmlXPathNodeSetAdd(ret, cur);
|
|
}
|
|
break;
|
|
case NODE_TEST_ALL:
|
|
if ((cur->type == XML_ELEMENT_NODE) ||
|
|
(cur->type == XML_ATTRIBUTE_NODE)) {
|
|
/* !!! || (cur->type == XML_TEXT_NODE)) { */
|
|
#ifdef DEBUG_STEP
|
|
n++;
|
|
#endif
|
|
xmlXPathNodeSetAdd(ret, cur);
|
|
}
|
|
break;
|
|
case NODE_TEST_NS: {
|
|
TODO /* namespace search */
|
|
break;
|
|
}
|
|
case NODE_TEST_NAME:
|
|
switch (cur->type) {
|
|
case XML_ELEMENT_NODE:
|
|
if (!xmlStrcmp(name, cur->name) &&
|
|
(((prefix == NULL) ||
|
|
((cur->ns != NULL) &&
|
|
(!xmlStrcmp(prefix, cur->ns->href)))))) {
|
|
#ifdef DEBUG_STEP
|
|
n++;
|
|
#endif
|
|
xmlXPathNodeSetAdd(ret, cur);
|
|
}
|
|
break;
|
|
case XML_ATTRIBUTE_NODE: {
|
|
xmlAttrPtr attr = (xmlAttrPtr) cur;
|
|
if (!xmlStrcmp(name, attr->name)) {
|
|
#ifdef DEBUG_STEP
|
|
n++;
|
|
#endif
|
|
xmlXPathNodeSetAdd(ret, cur);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
}
|
|
} while (cur != NULL);
|
|
}
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug,
|
|
"\nExamined %d nodes, found %d nodes at that step\n", t, n);
|
|
#endif
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Implicit tree core function library *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/**
|
|
* xmlXPathRoot:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Initialize the context to the root of the document
|
|
*/
|
|
void
|
|
xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
|
|
if (ctxt->context->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->context->nodelist);
|
|
ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
|
|
ctxt->context->nodelist = xmlXPathNodeSetCreate(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
|
|
*
|
|
* Implement the last() XPath function
|
|
* 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->nodelist == NULL) ||
|
|
(ctxt->context->node == NULL) ||
|
|
(ctxt->context->nodelist->nodeNr == 0)) {
|
|
valuePush(ctxt, xmlXPathNewFloat((double) 0));
|
|
} else {
|
|
valuePush(ctxt,
|
|
xmlXPathNewFloat((double) ctxt->context->nodelist->nodeNr));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathPositionFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the position() XPath function
|
|
* The position function returns the position of the context node in the
|
|
* context node list. The first position is 1, and so the last positionr
|
|
* will be equal to last().
|
|
*/
|
|
void
|
|
xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
int i;
|
|
|
|
CHECK_ARITY(0);
|
|
if ((ctxt->context->nodelist == NULL) ||
|
|
(ctxt->context->node == NULL) ||
|
|
(ctxt->context->nodelist->nodeNr == 0)) {
|
|
valuePush(ctxt, xmlXPathNewFloat((double) 0));
|
|
}
|
|
for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) {
|
|
if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) {
|
|
valuePush(ctxt, xmlXPathNewFloat((double) i + 1));
|
|
return;
|
|
}
|
|
}
|
|
valuePush(ctxt, xmlXPathNewFloat((double) 0));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathCountFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the count() XPath function
|
|
*/
|
|
void
|
|
xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
CHECK_ARITY(1);
|
|
CHECK_TYPE(XPATH_NODESET);
|
|
cur = valuePop(ctxt);
|
|
|
|
valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
|
|
xmlXPathFreeObject(cur);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathIdFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the id() XPath function
|
|
* 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) {
|
|
const xmlChar *tokens;
|
|
const xmlChar *cur;
|
|
xmlChar *ID;
|
|
xmlAttrPtr attr;
|
|
xmlNodePtr elem = NULL;
|
|
xmlXPathObjectPtr ret, obj;
|
|
|
|
CHECK_ARITY(1);
|
|
obj = valuePop(ctxt);
|
|
if (obj == NULL) ERROR(XPATH_INVALID_OPERAND);
|
|
if (obj->type == XPATH_NODESET) {
|
|
TODO /* ID function in case of NodeSet */
|
|
}
|
|
if (obj->type != XPATH_STRING) {
|
|
valuePush(ctxt, obj);
|
|
xmlXPathStringFunction(ctxt, 1);
|
|
obj = valuePop(ctxt);
|
|
if (obj->type != XPATH_STRING) {
|
|
xmlXPathFreeObject(obj);
|
|
return;
|
|
}
|
|
}
|
|
tokens = obj->stringval;
|
|
|
|
ret = xmlXPathNewNodeSet(NULL);
|
|
valuePush(ctxt, ret);
|
|
if (tokens == NULL) {
|
|
xmlXPathFreeObject(obj);
|
|
return;
|
|
}
|
|
|
|
cur = tokens;
|
|
|
|
while (IS_BLANK(*cur)) cur++;
|
|
while (*cur != 0) {
|
|
while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
|
|
(*cur == '.') || (*cur == '-') ||
|
|
(*cur == '_') || (*cur == ':') ||
|
|
(IS_COMBINING(*cur)) ||
|
|
(IS_EXTENDER(*cur)))
|
|
cur++;
|
|
|
|
if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
|
|
|
|
ID = xmlStrndup(tokens, cur - tokens);
|
|
attr = xmlGetID(ctxt->context->doc, ID);
|
|
if (attr != NULL) {
|
|
elem = attr->parent;
|
|
xmlXPathNodeSetAdd(ret->nodesetval, elem);
|
|
}
|
|
if (ID != NULL)
|
|
xmlFree(ID);
|
|
|
|
while (IS_BLANK(*cur)) cur++;
|
|
tokens = cur;
|
|
}
|
|
xmlXPathFreeObject(obj);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* xmlXPathLocalPartFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the local-part() XPath function
|
|
* The local-part 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
|
|
xmlXPathLocalPartFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
CHECK_ARITY(1);
|
|
CHECK_TYPE(XPATH_NODESET);
|
|
cur = valuePop(ctxt);
|
|
|
|
if (cur->nodesetval->nodeNr == 0) {
|
|
valuePush(ctxt, xmlXPathNewCString(""));
|
|
} else {
|
|
int i = 0; /* Should be first in document order !!!!! */
|
|
valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
|
|
}
|
|
xmlXPathFreeObject(cur);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNamespaceFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the namespace() XPath function
|
|
* The namespace 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
|
|
xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
if (nargs == 0) {
|
|
valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
|
|
nargs = 1;
|
|
}
|
|
CHECK_ARITY(1);
|
|
CHECK_TYPE(XPATH_NODESET);
|
|
cur = valuePop(ctxt);
|
|
|
|
if (cur->nodesetval->nodeNr == 0) {
|
|
valuePush(ctxt, xmlXPathNewCString(""));
|
|
} else {
|
|
int i = 0; /* Should be first in document order !!!!! */
|
|
|
|
if (cur->nodesetval->nodeTab[i]->ns == NULL)
|
|
valuePush(ctxt, xmlXPathNewCString(""));
|
|
else
|
|
valuePush(ctxt, xmlXPathNewString(
|
|
cur->nodesetval->nodeTab[i]->ns->href));
|
|
}
|
|
xmlXPathFreeObject(cur);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNameFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the name() XPath function
|
|
* The name function returns a string containing a QName representing
|
|
* the name of the node in the argument node-set that is first in documenti
|
|
* 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.
|
|
*/
|
|
void
|
|
xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
CHECK_ARITY(1);
|
|
CHECK_TYPE(XPATH_NODESET);
|
|
cur = valuePop(ctxt);
|
|
|
|
if (cur->nodesetval->nodeNr == 0) {
|
|
valuePush(ctxt, xmlXPathNewCString(""));
|
|
} else {
|
|
int i = 0; /* Should be first in document order !!!!! */
|
|
|
|
if (cur->nodesetval->nodeTab[i]->ns == NULL)
|
|
valuePush(ctxt, xmlXPathNewString(
|
|
cur->nodesetval->nodeTab[i]->name));
|
|
|
|
else {
|
|
char name[2000];
|
|
sprintf(name, "%s:%s",
|
|
(char *) cur->nodesetval->nodeTab[i]->ns->prefix,
|
|
(char *) cur->nodesetval->nodeTab[i]->name);
|
|
valuePush(ctxt, xmlXPathNewCString(name));
|
|
}
|
|
}
|
|
xmlXPathFreeObject(cur);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathStringFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the string() XPath function
|
|
* he 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 immediatelyi
|
|
* 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.
|
|
*/
|
|
void
|
|
xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
CHECK_ARITY(1);
|
|
cur = valuePop(ctxt);
|
|
if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
|
|
switch (cur->type) {
|
|
case XPATH_NODESET:
|
|
if (cur->nodesetval->nodeNr == 0) {
|
|
valuePush(ctxt, xmlXPathNewCString(""));
|
|
} else {
|
|
xmlChar *res;
|
|
int i = 0; /* Should be first in document order !!!!! */
|
|
res = xmlNodeGetContent(cur->nodesetval->nodeTab[i]);
|
|
valuePush(ctxt, xmlXPathNewString(res));
|
|
xmlFree(res);
|
|
}
|
|
xmlXPathFreeObject(cur);
|
|
return;
|
|
case XPATH_STRING:
|
|
valuePush(ctxt, cur);
|
|
return;
|
|
case XPATH_BOOLEAN:
|
|
if (cur->boolval) valuePush(ctxt, xmlXPathNewCString("true"));
|
|
else valuePush(ctxt, xmlXPathNewCString("false"));
|
|
xmlXPathFreeObject(cur);
|
|
return;
|
|
case XPATH_NUMBER: {
|
|
char buf[100];
|
|
|
|
if (isnan(cur->floatval))
|
|
sprintf(buf, "NaN");
|
|
else if (isinf(cur->floatval) > 0)
|
|
sprintf(buf, "+Infinity");
|
|
else if (isinf(cur->floatval) < 0)
|
|
sprintf(buf, "-Infinity");
|
|
else
|
|
sprintf(buf, "%0g", cur->floatval);
|
|
valuePush(ctxt, xmlXPathNewCString(buf));
|
|
xmlXPathFreeObject(cur);
|
|
return;
|
|
}
|
|
}
|
|
STRANGE
|
|
}
|
|
|
|
/**
|
|
* xmlXPathStringLengthFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the string-length() XPath function
|
|
* 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 = xmlNodeGetContent(ctxt->context->node);
|
|
valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(content)));
|
|
xmlFree(content);
|
|
}
|
|
return;
|
|
}
|
|
CHECK_ARITY(1);
|
|
CHECK_TYPE(XPATH_STRING);
|
|
cur = valuePop(ctxt);
|
|
valuePush(ctxt, xmlXPathNewFloat(xmlStrlen(cur->stringval)));
|
|
xmlXPathFreeObject(cur);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathConcatFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the concat() XPath function
|
|
* The concat function returns the concatenation of its arguments.
|
|
*/
|
|
void
|
|
xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
xmlXPathObjectPtr cur, new;
|
|
xmlChar *tmp;
|
|
|
|
if (nargs < 2) {
|
|
CHECK_ARITY(2);
|
|
}
|
|
|
|
cur = valuePop(ctxt);
|
|
if ((cur == NULL) || (cur->type != XPATH_STRING)) {
|
|
xmlXPathFreeObject(cur);
|
|
return;
|
|
}
|
|
nargs--;
|
|
|
|
while (nargs > 0) {
|
|
new = valuePop(ctxt);
|
|
if ((new == NULL) || (new->type != XPATH_STRING)) {
|
|
xmlXPathFreeObject(new);
|
|
xmlXPathFreeObject(cur);
|
|
ERROR(XPATH_INVALID_TYPE);
|
|
}
|
|
tmp = xmlStrcat(new->stringval, cur->stringval);
|
|
new->stringval = cur->stringval;
|
|
cur->stringval = tmp;
|
|
|
|
xmlXPathFreeObject(new);
|
|
nargs--;
|
|
}
|
|
valuePush(ctxt, cur);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathContainsFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the contains() XPath function
|
|
* 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);
|
|
CHECK_TYPE(XPATH_STRING);
|
|
needle = valuePop(ctxt);
|
|
hay = valuePop(ctxt);
|
|
if ((hay == NULL) || (hay->type != XPATH_STRING)) {
|
|
xmlXPathFreeObject(hay);
|
|
xmlXPathFreeObject(needle);
|
|
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
|
|
*
|
|
* Implement the starts-with() XPath function
|
|
* 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);
|
|
CHECK_TYPE(XPATH_STRING);
|
|
needle = valuePop(ctxt);
|
|
hay = valuePop(ctxt);
|
|
if ((hay == NULL) || (hay->type != XPATH_STRING)) {
|
|
xmlXPathFreeObject(hay);
|
|
xmlXPathFreeObject(needle);
|
|
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
|
|
*
|
|
* Implement the substring() XPath function
|
|
* 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, in;
|
|
int i, l;
|
|
xmlChar *ret;
|
|
|
|
/*
|
|
* Conformance needs to be checked !!!!!
|
|
*/
|
|
if (nargs < 2) {
|
|
CHECK_ARITY(2);
|
|
}
|
|
if (nargs > 3) {
|
|
CHECK_ARITY(3);
|
|
}
|
|
if (nargs == 3) {
|
|
CHECK_TYPE(XPATH_NUMBER);
|
|
len = valuePop(ctxt);
|
|
le = len->floatval;
|
|
xmlXPathFreeObject(len);
|
|
} else {
|
|
le = 2000000000;
|
|
}
|
|
CHECK_TYPE(XPATH_NUMBER);
|
|
start = valuePop(ctxt);
|
|
in = start->floatval;
|
|
xmlXPathFreeObject(start);
|
|
CHECK_TYPE(XPATH_STRING);
|
|
str = valuePop(ctxt);
|
|
le += in;
|
|
|
|
/* integer index of the first char */
|
|
i = in;
|
|
if (((double)i) != in) i++;
|
|
|
|
/* integer index of the last char */
|
|
l = le;
|
|
if (((double)l) != le) l++;
|
|
|
|
/* back to a zero based len */
|
|
i--;
|
|
l--;
|
|
|
|
/* check against the string len */
|
|
if (l > 1024) {
|
|
l = xmlStrlen(str->stringval);
|
|
}
|
|
if (i < 0) {
|
|
i = 0;
|
|
}
|
|
|
|
/* number of chars to copy */
|
|
l -= i;
|
|
|
|
ret = xmlStrsub(str->stringval, i, l);
|
|
if (ret == NULL)
|
|
valuePush(ctxt, xmlXPathNewCString(""));
|
|
else {
|
|
valuePush(ctxt, xmlXPathNewString(ret));
|
|
xmlFree(ret);
|
|
}
|
|
xmlXPathFreeObject(str);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathSubstringBeforeFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the substring-before() XPath function
|
|
* 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) {
|
|
CHECK_ARITY(2);
|
|
TODO /* substring before */
|
|
}
|
|
|
|
/**
|
|
* xmlXPathSubstringAfterFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the substring-after() XPath function
|
|
* 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) {
|
|
CHECK_ARITY(2);
|
|
TODO /* substring after */
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNormalizeFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the normalize() XPath function
|
|
* The normalize 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) {
|
|
CHECK_ARITY(1);
|
|
TODO /* normalize isn't as boring as translate, but pretty much */
|
|
}
|
|
|
|
/**
|
|
* xmlXPathTranslateFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the translate() XPath function
|
|
* 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) {
|
|
CHECK_ARITY(3);
|
|
TODO /* translate is boring, waiting for UTF-8 representation too */
|
|
}
|
|
|
|
/**
|
|
* xmlXPathBooleanFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the boolean() XPath function
|
|
* he 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;
|
|
int res = 0;
|
|
|
|
CHECK_ARITY(1);
|
|
cur = valuePop(ctxt);
|
|
if (cur == NULL) ERROR(XPATH_INVALID_OPERAND);
|
|
switch (cur->type) {
|
|
case XPATH_NODESET:
|
|
if ((cur->nodesetval == NULL) ||
|
|
(cur->nodesetval->nodeNr == 0)) res = 0;
|
|
else
|
|
res = 1;
|
|
break;
|
|
case XPATH_STRING:
|
|
if ((cur->stringval == NULL) ||
|
|
(cur->stringval[0] == 0)) res = 0;
|
|
else
|
|
res = 1;
|
|
break;
|
|
case XPATH_BOOLEAN:
|
|
valuePush(ctxt, cur);
|
|
return;
|
|
case XPATH_NUMBER:
|
|
if (cur->floatval) res = 1;
|
|
break;
|
|
default:
|
|
STRANGE
|
|
}
|
|
xmlXPathFreeObject(cur);
|
|
valuePush(ctxt, xmlXPathNewBoolean(res));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNotFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the not() XPath function
|
|
* The not function returns true if its argument is false,
|
|
* and false otherwise.
|
|
*/
|
|
void
|
|
xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
CHECK_ARITY(1);
|
|
CHECK_TYPE(XPATH_BOOLEAN);
|
|
ctxt->value->boolval = ! ctxt->value->boolval;
|
|
}
|
|
|
|
/**
|
|
* xmlXPathTrueFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the true() XPath function
|
|
*/
|
|
void
|
|
xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
CHECK_ARITY(0);
|
|
valuePush(ctxt, xmlXPathNewBoolean(1));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathFalseFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the false() XPath function
|
|
*/
|
|
void
|
|
xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
CHECK_ARITY(0);
|
|
valuePush(ctxt, xmlXPathNewBoolean(0));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathLangFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the lang() XPath function
|
|
* 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);
|
|
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:
|
|
xmlXPathFreeObject(val);
|
|
valuePush(ctxt, xmlXPathNewBoolean(ret));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathNumberFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the number() XPath function
|
|
*/
|
|
void
|
|
xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
xmlXPathObjectPtr cur;
|
|
double res;
|
|
|
|
CHECK_ARITY(1);
|
|
cur = valuePop(ctxt);
|
|
switch (cur->type) {
|
|
case XPATH_NODESET:
|
|
valuePush(ctxt, cur);
|
|
xmlXPathStringFunction(ctxt, 1);
|
|
cur = valuePop(ctxt);
|
|
case XPATH_STRING:
|
|
res = xmlXPathStringEvalNumber(cur->stringval);
|
|
valuePush(ctxt, xmlXPathNewFloat(res));
|
|
xmlXPathFreeObject(cur);
|
|
return;
|
|
case XPATH_BOOLEAN:
|
|
if (cur->boolval) valuePush(ctxt, xmlXPathNewFloat(1.0));
|
|
else valuePush(ctxt, xmlXPathNewFloat(0.0));
|
|
xmlXPathFreeObject(cur);
|
|
return;
|
|
case XPATH_NUMBER:
|
|
valuePush(ctxt, cur);
|
|
return;
|
|
}
|
|
STRANGE
|
|
}
|
|
|
|
/**
|
|
* xmlXPathSumFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the sum() XPath function
|
|
* The sum function returns the sum of the values of the nodes in
|
|
* the argument node-set.
|
|
*/
|
|
void
|
|
xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|
CHECK_ARITY(1);
|
|
TODO /* BUG Sum : don't understand the definition */
|
|
}
|
|
|
|
/**
|
|
* xmlXPathFloorFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the floor() XPath function
|
|
* 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) {
|
|
CHECK_ARITY(1);
|
|
CHECK_TYPE(XPATH_NUMBER);
|
|
/* floor(0.999999999999) => 1.0 !!!!!!!!!!! */
|
|
ctxt->value->floatval = (double)((int) ctxt->value->floatval);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathCeilingFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the ceiling() XPath function
|
|
* 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);
|
|
CHECK_TYPE(XPATH_NUMBER);
|
|
f = (double)((int) ctxt->value->floatval);
|
|
if (f != ctxt->value->floatval)
|
|
ctxt->value->floatval = f + 1;
|
|
}
|
|
|
|
/**
|
|
* xmlXPathRoundFunction:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Implement the round() XPath function
|
|
* 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);
|
|
CHECK_TYPE(XPATH_NUMBER);
|
|
/* round(0.50000001) => 0 !!!!! */
|
|
f = (double)((int) ctxt->value->floatval);
|
|
if (ctxt->value->floatval < f + 0.5)
|
|
ctxt->value->floatval = f;
|
|
else if (ctxt->value->floatval == f + 0.5)
|
|
ctxt->value->floatval = f; /* !!!! Not following the spec here */
|
|
else
|
|
ctxt->value->floatval = f + 1;
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* The Parser *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/*
|
|
* a couple of forward declarations since we use a recursive call based
|
|
* implementation.
|
|
*/
|
|
void xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt);
|
|
void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt);
|
|
void xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt);
|
|
void xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt);
|
|
|
|
/**
|
|
* 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 *q;
|
|
xmlChar *ret = NULL;
|
|
|
|
if (!IS_LETTER(CUR) && (CUR != '_')) return(NULL);
|
|
q = NEXT;
|
|
|
|
while ((IS_LETTER(CUR)) || (IS_DIGIT(CUR)) ||
|
|
(CUR == '.') || (CUR == '-') ||
|
|
(CUR == '_') ||
|
|
(IS_COMBINING(CUR)) ||
|
|
(IS_EXTENDER(CUR)))
|
|
NEXT;
|
|
|
|
ret = xmlStrndup(q, CUR_PTR - q);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
|
|
xmlChar *
|
|
xmlXPathParseQName(xmlXPathParserContextPtr ctxt, xmlChar **prefix) {
|
|
xmlChar *ret = NULL;
|
|
|
|
*prefix = NULL;
|
|
ret = xmlXPathParseNCName(ctxt);
|
|
if (CUR == ':') {
|
|
*prefix = ret;
|
|
NEXT;
|
|
ret = xmlXPathParseNCName(ctxt);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathStringEvalNumber:
|
|
* @str: A string to scan
|
|
*
|
|
* [30] Number ::= Digits ('.' Digits)?
|
|
* | '.' Digits
|
|
* [31] Digits ::= [0-9]+
|
|
*
|
|
* Parse and evaluate a Number in the string
|
|
*
|
|
* BUG: "1.' is not valid ... James promised correction
|
|
* as Digits ('.' Digits?)?
|
|
*
|
|
* Returns the double value.
|
|
*/
|
|
double
|
|
xmlXPathStringEvalNumber(const xmlChar *str) {
|
|
const xmlChar *cur = str;
|
|
double ret = 0.0;
|
|
double mult = 1;
|
|
int ok = 0;
|
|
|
|
while (*cur == ' ') cur++;
|
|
if ((*cur != '.') && ((*cur < '0') || (*cur > '9'))) {
|
|
return(xmlXPathNAN);
|
|
}
|
|
while ((*cur >= '0') && (*cur <= '9')) {
|
|
ret = ret * 10 + (*cur - '0');
|
|
ok = 1;
|
|
cur++;
|
|
}
|
|
if (*cur == '.') {
|
|
cur++;
|
|
if (((*cur < '0') || (*cur > '9')) && (!ok)) {
|
|
return(xmlXPathNAN);
|
|
}
|
|
while ((*cur >= '0') && (*cur <= '9')) {
|
|
mult /= 10;
|
|
ret = ret + (*cur - '0') * mult;
|
|
cur++;
|
|
}
|
|
}
|
|
while (*cur == ' ') cur++;
|
|
if (*cur != 0) return(xmlXPathNAN);
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalNumber:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [30] Number ::= Digits ('.' Digits)?
|
|
* | '.' Digits
|
|
* [31] Digits ::= [0-9]+
|
|
*
|
|
* Parse and evaluate a Number, then push it on the stack
|
|
*
|
|
* BUG: "1.' is not valid ... James promised correction
|
|
* as Digits ('.' Digits?)?
|
|
*/
|
|
void
|
|
xmlXPathEvalNumber(xmlXPathParserContextPtr ctxt) {
|
|
double ret = 0.0;
|
|
double mult = 1;
|
|
int ok = 0;
|
|
|
|
CHECK_ERROR;
|
|
if ((CUR != '.') && ((CUR < '0') || (CUR > '9'))) {
|
|
ERROR(XPATH_NUMBER_ERROR);
|
|
}
|
|
while ((CUR >= '0') && (CUR <= '9')) {
|
|
ret = ret * 10 + (CUR - '0');
|
|
ok = 1;
|
|
NEXT;
|
|
}
|
|
if (CUR == '.') {
|
|
NEXT;
|
|
if (((CUR < '0') || (CUR > '9')) && (!ok)) {
|
|
ERROR(XPATH_NUMBER_ERROR);
|
|
}
|
|
while ((CUR >= '0') && (CUR <= '9')) {
|
|
mult /= 10;
|
|
ret = ret + (CUR - '0') * mult;
|
|
NEXT;
|
|
}
|
|
}
|
|
valuePush(ctxt, xmlXPathNewFloat(ret));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalLiteral:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Parse a Literal and push it on the stack.
|
|
*
|
|
* [29] Literal ::= '"' [^"]* '"'
|
|
* | "'" [^']* "'"
|
|
*
|
|
* TODO: xmlXPathEvalLiteral memory allocation could be improved.
|
|
*/
|
|
void
|
|
xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
|
|
const xmlChar *q;
|
|
xmlChar *ret = NULL;
|
|
|
|
if (CUR == '"') {
|
|
NEXT;
|
|
q = CUR_PTR;
|
|
while ((IS_CHAR(CUR)) && (CUR != '"'))
|
|
NEXT;
|
|
if (!IS_CHAR(CUR)) {
|
|
ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
|
|
} else {
|
|
ret = xmlStrndup(q, CUR_PTR - q);
|
|
NEXT;
|
|
}
|
|
} else if (CUR == '\'') {
|
|
NEXT;
|
|
q = CUR_PTR;
|
|
while ((IS_CHAR(CUR)) && (CUR != '\''))
|
|
NEXT;
|
|
if (!IS_CHAR(CUR)) {
|
|
ERROR(XPATH_UNFINISHED_LITERAL_ERROR);
|
|
} else {
|
|
ret = xmlStrndup(q, CUR_PTR - q);
|
|
NEXT;
|
|
}
|
|
} else {
|
|
ERROR(XPATH_START_LITERAL_ERROR);
|
|
}
|
|
if (ret == NULL) return;
|
|
valuePush(ctxt, xmlXPathNewString(ret));
|
|
xmlFree(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalVariableReference:
|
|
* @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
|
|
* 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
|
|
*/
|
|
void
|
|
xmlXPathEvalVariableReference(xmlXPathParserContextPtr ctxt) {
|
|
xmlChar *name;
|
|
xmlChar *prefix;
|
|
xmlXPathObjectPtr value;
|
|
|
|
if (CUR != '$') {
|
|
ERROR(XPATH_VARIABLE_REF_ERROR);
|
|
}
|
|
name = xmlXPathParseQName(ctxt, &prefix);
|
|
if (name == NULL) {
|
|
ERROR(XPATH_VARIABLE_REF_ERROR);
|
|
}
|
|
value = xmlXPathVariablelookup(ctxt, prefix, name);
|
|
if (value == NULL) {
|
|
ERROR(XPATH_UNDEF_VARIABLE_ERROR);
|
|
}
|
|
valuePush(ctxt, value);
|
|
if (prefix != NULL) xmlFree(prefix);
|
|
xmlFree(name);
|
|
}
|
|
|
|
|
|
/**
|
|
* xmlXPathFunctionLookup:
|
|
* @ctxt: the XPath Parser context
|
|
* @name: a name string
|
|
*
|
|
* Search for a function of the given name
|
|
*
|
|
* [35] FunctionName ::= QName - NodeType
|
|
*
|
|
* TODO: for the moment the function list is hardcoded from the spec !!!!
|
|
*
|
|
* Returns the xmlXPathFunction if found, or NULL otherwise
|
|
*/
|
|
xmlXPathFunction
|
|
xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, const xmlChar *name) {
|
|
switch (name[0]) {
|
|
case 'b':
|
|
if (!xmlStrcmp(name, BAD_CAST "boolean"))
|
|
return(xmlXPathBooleanFunction);
|
|
break;
|
|
case 'c':
|
|
if (!xmlStrcmp(name, BAD_CAST "ceiling"))
|
|
return(xmlXPathCeilingFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "count"))
|
|
return(xmlXPathCountFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "concat"))
|
|
return(xmlXPathConcatFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "contains"))
|
|
return(xmlXPathContainsFunction);
|
|
break;
|
|
case 'i':
|
|
if (!xmlStrcmp(name, BAD_CAST "id"))
|
|
return(xmlXPathIdFunction);
|
|
break;
|
|
case 'f':
|
|
if (!xmlStrcmp(name, BAD_CAST "false"))
|
|
return(xmlXPathFalseFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "floor"))
|
|
return(xmlXPathFloorFunction);
|
|
break;
|
|
case 'l':
|
|
if (!xmlStrcmp(name, BAD_CAST "last"))
|
|
return(xmlXPathLastFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "lang"))
|
|
return(xmlXPathLangFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "local-part"))
|
|
return(xmlXPathLocalPartFunction);
|
|
break;
|
|
case 'n':
|
|
if (!xmlStrcmp(name, BAD_CAST "not"))
|
|
return(xmlXPathNotFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "name"))
|
|
return(xmlXPathNameFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "namespace"))
|
|
return(xmlXPathNamespaceFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "normalize-space"))
|
|
return(xmlXPathNormalizeFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "normalize"))
|
|
return(xmlXPathNormalizeFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "number"))
|
|
return(xmlXPathNumberFunction);
|
|
break;
|
|
case 'p':
|
|
if (!xmlStrcmp(name, BAD_CAST "position"))
|
|
return(xmlXPathPositionFunction);
|
|
break;
|
|
case 'r':
|
|
if (!xmlStrcmp(name, BAD_CAST "round"))
|
|
return(xmlXPathRoundFunction);
|
|
break;
|
|
case 's':
|
|
if (!xmlStrcmp(name, BAD_CAST "string"))
|
|
return(xmlXPathStringFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "string-length"))
|
|
return(xmlXPathStringLengthFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "starts-with"))
|
|
return(xmlXPathStartsWithFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "substring"))
|
|
return(xmlXPathSubstringFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "substring-before"))
|
|
return(xmlXPathSubstringBeforeFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "substring-after"))
|
|
return(xmlXPathSubstringAfterFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "sum"))
|
|
return(xmlXPathSumFunction);
|
|
break;
|
|
case 't':
|
|
if (!xmlStrcmp(name, BAD_CAST "true"))
|
|
return(xmlXPathTrueFunction);
|
|
if (!xmlStrcmp(name, BAD_CAST "translate"))
|
|
return(xmlXPathTranslateFunction);
|
|
break;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalLocationPathName:
|
|
* @ctxt: the XPath Parser context
|
|
* @name: a name string
|
|
*
|
|
* Various names in the beginning of a LocationPath expression
|
|
* indicate whether that's an Axis, a node type,
|
|
*
|
|
* [6] AxisName ::= 'ancestor'
|
|
* | 'ancestor-or-self'
|
|
* | 'attribute'
|
|
* | 'child'
|
|
* | 'descendant'
|
|
* | 'descendant-or-self'
|
|
* | 'following'
|
|
* | 'following-sibling'
|
|
* | 'namespace'
|
|
* | 'parent'
|
|
* | 'preceding'
|
|
* | 'preceding-sibling'
|
|
* | 'self'
|
|
* [38] NodeType ::= 'comment'
|
|
* | 'text'
|
|
* | 'processing-instruction'
|
|
* | 'node'
|
|
*/
|
|
int
|
|
xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, const xmlChar *name) {
|
|
switch (name[0]) {
|
|
case 'a':
|
|
if (!xmlStrcmp(name, BAD_CAST "ancestor")) return(AXIS_ANCESTOR);
|
|
if (!xmlStrcmp(name, BAD_CAST "ancestor-or-self"))
|
|
return(AXIS_ANCESTOR_OR_SELF);
|
|
if (!xmlStrcmp(name, BAD_CAST "attribute")) return(AXIS_ATTRIBUTE);
|
|
break;
|
|
case 'c':
|
|
if (!xmlStrcmp(name, BAD_CAST "child")) return(AXIS_CHILD);
|
|
if (!xmlStrcmp(name, BAD_CAST "comment")) return(NODE_TYPE_COMMENT);
|
|
break;
|
|
case 'd':
|
|
if (!xmlStrcmp(name, BAD_CAST "descendant"))
|
|
return(AXIS_DESCENDANT);
|
|
if (!xmlStrcmp(name, BAD_CAST "descendant-or-self"))
|
|
return(AXIS_DESCENDANT_OR_SELF);
|
|
break;
|
|
case 'f':
|
|
if (!xmlStrcmp(name, BAD_CAST "following")) return(AXIS_FOLLOWING);
|
|
if (!xmlStrcmp(name, BAD_CAST "following-sibling"))
|
|
return(AXIS_FOLLOWING_SIBLING);
|
|
break;
|
|
case 'n':
|
|
if (!xmlStrcmp(name, BAD_CAST "namespace")) return(AXIS_NAMESPACE);
|
|
if (!xmlStrcmp(name, BAD_CAST "node")) return(NODE_TYPE_NODE);
|
|
break;
|
|
case 'p':
|
|
if (!xmlStrcmp(name, BAD_CAST "parent")) return(AXIS_PARENT);
|
|
if (!xmlStrcmp(name, BAD_CAST "preceding")) return(AXIS_PRECEDING);
|
|
if (!xmlStrcmp(name, BAD_CAST "preceding-sibling"))
|
|
return(AXIS_PRECEDING_SIBLING);
|
|
if (!xmlStrcmp(name, BAD_CAST "processing-instruction"))
|
|
return(NODE_TYPE_PI);
|
|
break;
|
|
case 's':
|
|
if (!xmlStrcmp(name, BAD_CAST "self")) return(AXIS_SELF);
|
|
break;
|
|
case 't':
|
|
if (!xmlStrcmp(name, BAD_CAST "text")) return(NODE_TYPE_TEXT);
|
|
break;
|
|
}
|
|
if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION);
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalFunctionCall:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [16] FunctionCall ::= FunctionName '(' ( Argument ( ',' Argument)*)? ')'
|
|
* [17] Argument ::= Expr
|
|
*
|
|
* Parse and evaluate a function call, the evaluation of all arguments are
|
|
* pushed on the stack
|
|
*/
|
|
void
|
|
xmlXPathEvalFunctionCall(xmlXPathParserContextPtr ctxt) {
|
|
xmlChar *name;
|
|
xmlChar *prefix;
|
|
xmlXPathFunction func;
|
|
int nbargs = 0;
|
|
|
|
name = xmlXPathParseQName(ctxt, &prefix);
|
|
if (name == NULL) {
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
SKIP_BLANKS;
|
|
func = xmlXPathIsFunction(ctxt, name);
|
|
if (func == NULL) {
|
|
xmlFree(name);
|
|
ERROR(XPATH_UNKNOWN_FUNC_ERROR);
|
|
}
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Calling function %s\n", name);
|
|
#endif
|
|
|
|
if (CUR != '(') {
|
|
xmlFree(name);
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
|
|
while (CUR != ')') {
|
|
xmlXPathEvalExpr(ctxt);
|
|
nbargs++;
|
|
if (CUR == ')') break;
|
|
if (CUR != ',') {
|
|
xmlFree(name);
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
}
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
xmlFree(name);
|
|
func(ctxt, nbargs);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalPrimaryExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [15] PrimaryExpr ::= VariableReference
|
|
* | '(' Expr ')'
|
|
* | Literal
|
|
* | Number
|
|
* | FunctionCall
|
|
*
|
|
* Parse and evaluate a primary expression, then push the result on the stack
|
|
*/
|
|
void
|
|
xmlXPathEvalPrimaryExpr(xmlXPathParserContextPtr ctxt) {
|
|
SKIP_BLANKS;
|
|
if (CUR == '$') xmlXPathEvalVariableReference(ctxt);
|
|
else if (CUR == '(') {
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalExpr(ctxt);
|
|
if (CUR != ')') {
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
} else if (IS_DIGIT(CUR)) {
|
|
xmlXPathEvalNumber(ctxt);
|
|
} else if ((CUR == '\'') || (CUR == '"')) {
|
|
xmlXPathEvalLiteral(ctxt);
|
|
} else {
|
|
xmlXPathEvalFunctionCall(ctxt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalFilterExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [20] FilterExpr ::= PrimaryExpr
|
|
* | FilterExpr Predicate
|
|
*
|
|
* Parse and evaluate a filter expression, then push the result on the stack
|
|
* 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.
|
|
*/
|
|
|
|
void
|
|
xmlXPathEvalFilterExpr(xmlXPathParserContextPtr ctxt) {
|
|
/****
|
|
xmlNodeSetPtr oldset = NULL;
|
|
xmlXPathObjectPtr arg;
|
|
****/
|
|
|
|
xmlXPathEvalPrimaryExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
|
|
if (CUR != '[') return;
|
|
|
|
CHECK_TYPE(XPATH_NODESET);
|
|
|
|
while (CUR == '[') {
|
|
xmlXPathEvalPredicate(ctxt);
|
|
SKIP_BLANKS;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/**
|
|
* xmlXPathScanName:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* Trickery: parse an XML name but without consuming the input flow
|
|
* Needed for rollback cases.
|
|
*
|
|
* [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
|
|
* CombiningChar | Extender
|
|
*
|
|
* [5] Name ::= (Letter | '_' | ':') (NameChar)*
|
|
*
|
|
* [6] Names ::= Name (S Name)*
|
|
*
|
|
* Returns the Name parsed or NULL
|
|
*/
|
|
|
|
xmlChar *
|
|
xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
|
|
xmlChar buf[XML_MAX_NAMELEN];
|
|
int len = 0;
|
|
|
|
SKIP_BLANKS;
|
|
if (!IS_LETTER(CUR) && (CUR != '_') &&
|
|
(CUR != ':')) {
|
|
return(NULL);
|
|
}
|
|
|
|
while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
|
|
(NXT(len) == '.') || (NXT(len) == '-') ||
|
|
(NXT(len) == '_') || (NXT(len) == ':') ||
|
|
(IS_COMBINING(NXT(len))) ||
|
|
(IS_EXTENDER(NXT(len)))) {
|
|
buf[len] = NXT(len);
|
|
len++;
|
|
if (len >= XML_MAX_NAMELEN) {
|
|
fprintf(stderr,
|
|
"xmlScanName: reached XML_MAX_NAMELEN limit\n");
|
|
while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
|
|
(NXT(len) == '.') || (NXT(len) == '-') ||
|
|
(NXT(len) == '_') || (NXT(len) == ':') ||
|
|
(IS_COMBINING(NXT(len))) ||
|
|
(IS_EXTENDER(NXT(len))))
|
|
len++;
|
|
break;
|
|
}
|
|
}
|
|
return(xmlStrndup(buf, len));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalPathExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [19] PathExpr ::= LocationPath
|
|
* | FilterExpr
|
|
* | FilterExpr '/' RelativeLocationPath
|
|
* | FilterExpr '//' RelativeLocationPath
|
|
*
|
|
* Parse and evaluate a path expression, then push the result on the stack
|
|
* 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()/.
|
|
*/
|
|
|
|
void
|
|
xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlNodeSetPtr newset = NULL;
|
|
|
|
SKIP_BLANKS;
|
|
if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
|
|
(CUR == '\'') || (CUR == '"')) {
|
|
xmlXPathEvalFilterExpr(ctxt);
|
|
CHECK_ERROR;
|
|
if ((CUR == '/') && (NXT(1) == '/')) {
|
|
SKIP(2);
|
|
SKIP_BLANKS;
|
|
if (ctxt->context->nodelist == NULL) {
|
|
STRANGE
|
|
xmlXPathRoot(ctxt);
|
|
}
|
|
newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
|
|
NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
|
|
if (ctxt->context->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->context->nodelist);
|
|
ctxt->context->nodelist = newset;
|
|
ctxt->context->node = NULL;
|
|
xmlXPathEvalRelativeLocationPath(ctxt);
|
|
} else if (CUR == '/') {
|
|
xmlXPathEvalRelativeLocationPath(ctxt);
|
|
}
|
|
} else {
|
|
/******* !!!!!!!!!! @attname */
|
|
xmlChar *name;
|
|
|
|
name = xmlXPathScanName(ctxt);
|
|
if ((name == NULL) || (!xmlXPathIsFunction(ctxt, name)))
|
|
xmlXPathEvalLocationPath(ctxt);
|
|
else
|
|
xmlXPathEvalFilterExpr(ctxt);
|
|
if (name != NULL)
|
|
xmlFree(name);
|
|
}
|
|
if (ctxt->context->nodelist != NULL)
|
|
valuePush(ctxt, xmlXPathNewNodeSetList(ctxt->context->nodelist));
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalUnionExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [18] UnionExpr ::= PathExpr
|
|
* | UnionExpr '|' PathExpr
|
|
*
|
|
* Parse and evaluate an union expression, then push the result on the stack
|
|
*/
|
|
|
|
void
|
|
xmlXPathEvalUnionExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathEvalPathExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
if (CUR == '|') {
|
|
xmlNodeSetPtr old = ctxt->context->nodelist;
|
|
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalPathExpr(ctxt);
|
|
|
|
if (ctxt->context->nodelist == NULL)
|
|
ctxt->context->nodelist = old;
|
|
else {
|
|
ctxt->context->nodelist =
|
|
xmlXPathNodeSetMerge(ctxt->context->nodelist, old);
|
|
xmlXPathFreeNodeSet(old);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalUnaryExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [27] UnaryExpr ::= UnionExpr
|
|
* | '-' UnaryExpr
|
|
*
|
|
* Parse and evaluate an unary expression, then push the result on the stack
|
|
*/
|
|
|
|
void
|
|
xmlXPathEvalUnaryExpr(xmlXPathParserContextPtr ctxt) {
|
|
int minus = 0;
|
|
|
|
SKIP_BLANKS;
|
|
if (CUR == '-') {
|
|
minus = 1;
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
}
|
|
xmlXPathEvalUnionExpr(ctxt);
|
|
CHECK_ERROR;
|
|
if (minus) {
|
|
xmlXPathValueFlipSign(ctxt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalMultiplicativeExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [26] MultiplicativeExpr ::= UnaryExpr
|
|
* | MultiplicativeExpr MultiplyOperator UnaryExpr
|
|
* | MultiplicativeExpr 'div' UnaryExpr
|
|
* | MultiplicativeExpr 'mod' UnaryExpr
|
|
* [34] MultiplyOperator ::= '*'
|
|
*
|
|
* Parse and evaluate an Additive expression, then push the result on the stack
|
|
*/
|
|
|
|
void
|
|
xmlXPathEvalMultiplicativeExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathEvalUnaryExpr(ctxt);
|
|
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;
|
|
|
|
if (CUR == '*') {
|
|
op = 0;
|
|
NEXT;
|
|
} else if (CUR == 'd') {
|
|
op = 1;
|
|
SKIP(3);
|
|
} else if (CUR == 'm') {
|
|
op = 2;
|
|
SKIP(3);
|
|
}
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalUnaryExpr(ctxt);
|
|
CHECK_ERROR;
|
|
switch (op) {
|
|
case 0:
|
|
xmlXPathMultValues(ctxt);
|
|
break;
|
|
case 1:
|
|
xmlXPathDivValues(ctxt);
|
|
break;
|
|
case 2:
|
|
xmlXPathModValues(ctxt);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalAdditiveExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [25] AdditiveExpr ::= MultiplicativeExpr
|
|
* | AdditiveExpr '+' MultiplicativeExpr
|
|
* | AdditiveExpr '-' MultiplicativeExpr
|
|
*
|
|
* Parse and evaluate an Additive expression, then push the result on the stack
|
|
*/
|
|
|
|
void
|
|
xmlXPathEvalAdditiveExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathEvalMultiplicativeExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
while ((CUR == '+') || (CUR == '-')) {
|
|
int plus;
|
|
|
|
if (CUR == '+') plus = 1;
|
|
else plus = 0;
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalMultiplicativeExpr(ctxt);
|
|
CHECK_ERROR;
|
|
if (plus) xmlXPathAddValues(ctxt);
|
|
else xmlXPathSubValues(ctxt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalRelationalExpr:
|
|
* @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.
|
|
*
|
|
* Parse and evaluate a Relational expression, then push the result
|
|
* on the stack
|
|
*/
|
|
|
|
void
|
|
xmlXPathEvalRelationalExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathEvalAdditiveExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
while ((CUR == '<') ||
|
|
(CUR == '>') ||
|
|
((CUR == '<') && (NXT(1) == '=')) ||
|
|
((CUR == '>') && (NXT(1) == '='))) {
|
|
int inf, strict, ret;
|
|
|
|
if (CUR == '<') inf = 1;
|
|
else inf = 0;
|
|
if (NXT(1) == '=') strict = 0;
|
|
else strict = 1;
|
|
NEXT;
|
|
if (!strict) NEXT;
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalAdditiveExpr(ctxt);
|
|
CHECK_ERROR;
|
|
ret = xmlXPathCompareValues(ctxt, inf, strict);
|
|
valuePush(ctxt, xmlXPathNewBoolean(ret));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalEqualityExpr:
|
|
* @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.
|
|
*
|
|
* Parse and evaluate an Equality expression, then push the result on the stack
|
|
*
|
|
*/
|
|
void
|
|
xmlXPathEvalEqualityExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathEvalRelationalExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
while ((CUR == '=') || ((CUR == '!') && (NXT(1) == '='))) {
|
|
xmlXPathObjectPtr res;
|
|
int eq, equal;
|
|
|
|
if (CUR == '=') eq = 1;
|
|
else eq = 0;
|
|
NEXT;
|
|
if (!eq) NEXT;
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalRelationalExpr(ctxt);
|
|
CHECK_ERROR;
|
|
equal = xmlXPathEqualValues(ctxt);
|
|
if (eq) res = xmlXPathNewBoolean(equal);
|
|
else res = xmlXPathNewBoolean(!equal);
|
|
valuePush(ctxt, res);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalAndExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [22] AndExpr ::= EqualityExpr
|
|
* | AndExpr 'and' EqualityExpr
|
|
*
|
|
* Parse and evaluate an AND expression, then push the result on the stack
|
|
*
|
|
*/
|
|
void
|
|
xmlXPathEvalAndExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathEvalEqualityExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
while ((CUR == 'a') && (NXT(1) == 'n') && (NXT(2) == 'n')) {
|
|
xmlXPathObjectPtr arg1, arg2;
|
|
|
|
SKIP(3);
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalEqualityExpr(ctxt);
|
|
CHECK_ERROR;
|
|
arg2 = valuePop(ctxt);
|
|
arg1 = valuePop(ctxt);
|
|
arg1->boolval &= arg2->boolval;
|
|
valuePush(ctxt, arg1);
|
|
xmlXPathFreeObject(arg2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalExpr:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [14] Expr ::= OrExpr
|
|
* [21] OrExpr ::= AndExpr
|
|
* | OrExpr 'or' AndExpr
|
|
*
|
|
* Parse and evaluate an expression, then push the result on the stack
|
|
*
|
|
*/
|
|
void
|
|
xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
|
|
xmlXPathEvalAndExpr(ctxt);
|
|
CHECK_ERROR;
|
|
SKIP_BLANKS;
|
|
while ((CUR == 'o') && (NXT(1) == 'r')) {
|
|
xmlXPathObjectPtr arg1, arg2;
|
|
|
|
SKIP(2);
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalAndExpr(ctxt);
|
|
CHECK_ERROR;
|
|
arg2 = valuePop(ctxt);
|
|
arg1 = valuePop(ctxt);
|
|
arg1->boolval |= arg2->boolval;
|
|
valuePush(ctxt, arg1);
|
|
xmlXPathFreeObject(arg2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvaluatePredicateResult:
|
|
* @ctxt: the XPath Parser context
|
|
* @res: the Predicate Expression evaluation result
|
|
* @index: index of the current node in the current list
|
|
*
|
|
* 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.
|
|
*/
|
|
int
|
|
xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt,
|
|
xmlXPathObjectPtr res, int index) {
|
|
if (res == NULL) return(0);
|
|
switch (res->type) {
|
|
case XPATH_BOOLEAN:
|
|
return(res->boolval);
|
|
case XPATH_NUMBER:
|
|
return(res->floatval == index);
|
|
case XPATH_NODESET:
|
|
return(res->nodesetval->nodeNr != 0);
|
|
case XPATH_STRING:
|
|
return((res->stringval != NULL) &&
|
|
(xmlStrlen(res->stringval) != 0));
|
|
default:
|
|
STRANGE
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalPredicate:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [8] Predicate ::= '[' PredicateExpr ']'
|
|
* [9] PredicateExpr ::= Expr
|
|
*
|
|
* Parse and evaluate a predicate for all the elements of the
|
|
* current node list. Then refine the list by removing all
|
|
* nodes where the predicate is false.
|
|
*/
|
|
void
|
|
xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) {
|
|
const xmlChar *cur;
|
|
xmlXPathObjectPtr res;
|
|
xmlNodeSetPtr newset = NULL;
|
|
int i;
|
|
|
|
SKIP_BLANKS;
|
|
if (CUR != '[') {
|
|
ERROR(XPATH_INVALID_PREDICATE_ERROR);
|
|
}
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
if ((ctxt->context->nodelist == NULL) ||
|
|
(ctxt->context->nodelist->nodeNr == 0)) {
|
|
ctxt->context->node = NULL;
|
|
xmlXPathEvalExpr(ctxt);
|
|
CHECK_ERROR;
|
|
res = valuePop(ctxt);
|
|
if (res != NULL)
|
|
xmlXPathFreeObject(res);
|
|
} else {
|
|
cur = ctxt->cur;
|
|
newset = xmlXPathNodeSetCreate(NULL);
|
|
for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) {
|
|
ctxt->cur = cur;
|
|
ctxt->context->node = ctxt->context->nodelist->nodeTab[i];
|
|
xmlXPathEvalExpr(ctxt);
|
|
CHECK_ERROR;
|
|
res = valuePop(ctxt);
|
|
if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1))
|
|
xmlXPathNodeSetAdd(newset,
|
|
ctxt->context->nodelist->nodeTab[i]);
|
|
if (res != NULL)
|
|
xmlXPathFreeObject(res);
|
|
}
|
|
if (ctxt->context->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->context->nodelist);
|
|
ctxt->context->nodelist = newset;
|
|
ctxt->context->node = NULL;
|
|
}
|
|
if (CUR != ']') {
|
|
ERROR(XPATH_INVALID_PREDICATE_ERROR);
|
|
}
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "After predicate : ");
|
|
xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalBasis:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [5] Basis ::= AxisName '::' NodeTest
|
|
* | AbbreviatedBasis
|
|
* [13] AbbreviatedBasis ::= NodeTest
|
|
* | '@' NodeTest
|
|
* [7] NodeTest ::= WildcardName
|
|
* | NodeType '(' ')'
|
|
* | 'processing-instruction' '(' Literal ')'
|
|
* [37] WildcardName ::= '*'
|
|
* | NCName ':' '*'
|
|
* | QName
|
|
*
|
|
* Evaluate one step in a Location Path
|
|
*/
|
|
void
|
|
xmlXPathEvalBasis(xmlXPathParserContextPtr ctxt) {
|
|
xmlChar *name = NULL;
|
|
xmlChar *prefix = NULL;
|
|
int type = 0;
|
|
int axis = AXIS_CHILD; /* the default on abbreviated syntax */
|
|
int nodetest = NODE_TEST_NONE;
|
|
int nodetype = 0;
|
|
xmlNodeSetPtr newset = NULL;
|
|
|
|
if (CUR == '@') {
|
|
NEXT;
|
|
axis = AXIS_ATTRIBUTE;
|
|
goto parse_NodeTest;
|
|
} else if (CUR == '*') {
|
|
NEXT;
|
|
nodetest = NODE_TEST_ALL;
|
|
} else {
|
|
name = xmlXPathParseNCName(ctxt);
|
|
if (name == NULL) {
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
type = xmlXPathGetNameType(ctxt, name);
|
|
switch (type) {
|
|
case IS_FUNCTION: {
|
|
xmlXPathFunction func;
|
|
int nbargs = 0;
|
|
xmlXPathObjectPtr top;
|
|
|
|
top = ctxt->value;
|
|
func = xmlXPathIsFunction(ctxt, name);
|
|
if (func == NULL) {
|
|
xmlFree(name);
|
|
ERROR(XPATH_UNKNOWN_FUNC_ERROR);
|
|
}
|
|
#ifdef DEBUG_EXPR
|
|
fprintf(xmlXPathDebug, "Calling function %s\n", name);
|
|
#endif
|
|
|
|
if (CUR != '(') {
|
|
xmlFree(name);
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
NEXT;
|
|
|
|
while (CUR != ')') {
|
|
xmlXPathEvalExpr(ctxt);
|
|
nbargs++;
|
|
if (CUR == ')') break;
|
|
if (CUR != ',') {
|
|
xmlFree(name);
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
NEXT;
|
|
}
|
|
NEXT;
|
|
xmlFree(name);
|
|
func(ctxt, nbargs);
|
|
if ((ctxt->value != top) &&
|
|
(ctxt->value != NULL) &&
|
|
(ctxt->value->type == XPATH_NODESET)) {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
cur = valuePop(ctxt);
|
|
ctxt->context->nodelist = cur->nodesetval;
|
|
ctxt->context->node = NULL;
|
|
cur->nodesetval = NULL;
|
|
xmlXPathFreeObject(cur);
|
|
}
|
|
return;
|
|
}
|
|
/*
|
|
* Simple case: no axis seach all given node types.
|
|
*/
|
|
case NODE_TYPE_COMMENT:
|
|
if ((CUR != '(') || (NXT(1) != ')')) break;
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_TYPE;
|
|
nodetype = XML_COMMENT_NODE;
|
|
goto search_nodes;
|
|
case NODE_TYPE_TEXT:
|
|
if ((CUR != '(') || (NXT(1) != ')')) break;
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_TYPE;
|
|
nodetype = XML_TEXT_NODE;
|
|
goto search_nodes;
|
|
case NODE_TYPE_NODE:
|
|
if ((CUR != '(') || (NXT(1) != ')')) {
|
|
nodetest = NODE_TEST_NAME;
|
|
break;
|
|
}
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_TYPE;
|
|
nodetype = XML_ELEMENT_NODE;
|
|
goto search_nodes;
|
|
case NODE_TYPE_PI:
|
|
if (CUR != '(') break;
|
|
if (name != NULL) xmlFree(name);
|
|
name = NULL;
|
|
if (NXT(1) != ')') {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
/*
|
|
* Specific case: search a PI by name.
|
|
*/
|
|
NEXT;
|
|
nodetest = NODE_TEST_PI;
|
|
xmlXPathEvalLiteral(ctxt);
|
|
CHECK_ERROR;
|
|
if (CUR != ')')
|
|
ERROR(XPATH_UNCLOSED_ERROR);
|
|
NEXT;
|
|
xmlXPathStringFunction(ctxt, 1);
|
|
CHECK_ERROR;
|
|
cur = valuePop(ctxt);
|
|
name = xmlStrdup(cur->stringval);
|
|
xmlXPathFreeObject(cur);
|
|
} else
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_PI;
|
|
goto search_nodes;
|
|
|
|
/*
|
|
* Handling of the compund form: got the axis.
|
|
*/
|
|
case AXIS_ANCESTOR:
|
|
case AXIS_ANCESTOR_OR_SELF:
|
|
case AXIS_ATTRIBUTE:
|
|
case AXIS_CHILD:
|
|
case AXIS_DESCENDANT:
|
|
case AXIS_DESCENDANT_OR_SELF:
|
|
case AXIS_FOLLOWING:
|
|
case AXIS_FOLLOWING_SIBLING:
|
|
case AXIS_NAMESPACE:
|
|
case AXIS_PARENT:
|
|
case AXIS_PRECEDING:
|
|
case AXIS_PRECEDING_SIBLING:
|
|
case AXIS_SELF:
|
|
if ((CUR != ':') || (NXT(1) != ':')) {
|
|
nodetest = NODE_TEST_NAME;
|
|
break;
|
|
}
|
|
SKIP(2);
|
|
axis = type;
|
|
break;
|
|
|
|
/*
|
|
* Default: abbreviated syntax the axis is AXIS_CHILD
|
|
*/
|
|
default:
|
|
nodetest = NODE_TEST_NAME;
|
|
}
|
|
parse_NodeTest:
|
|
if (nodetest == NODE_TEST_NONE) {
|
|
if (CUR == '*') {
|
|
NEXT;
|
|
nodetest = NODE_TEST_ALL;
|
|
} else {
|
|
if (name != NULL)
|
|
xmlFree(name);
|
|
name = xmlXPathParseQName(ctxt, &prefix);
|
|
if (name == NULL) {
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
type = xmlXPathGetNameType(ctxt, name);
|
|
switch (type) {
|
|
/*
|
|
* Simple case: no axis seach all given node types.
|
|
*/
|
|
case NODE_TYPE_COMMENT:
|
|
if ((CUR != '(') || (NXT(1) != ')')) break;
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_TYPE;
|
|
nodetype = XML_COMMENT_NODE;
|
|
goto search_nodes;
|
|
case NODE_TYPE_TEXT:
|
|
if ((CUR != '(') || (NXT(1) != ')')) break;
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_TYPE;
|
|
nodetype = XML_TEXT_NODE;
|
|
goto search_nodes;
|
|
case NODE_TYPE_NODE:
|
|
if ((CUR != '(') || (NXT(1) != ')')) {
|
|
nodetest = NODE_TEST_NAME;
|
|
break;
|
|
}
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_TYPE;
|
|
nodetype = XML_ELEMENT_NODE;
|
|
goto search_nodes;
|
|
case NODE_TYPE_PI:
|
|
if (CUR != '(') break;
|
|
if (name != NULL) xmlFree(name);
|
|
name = NULL;
|
|
if (NXT(1) != ')') {
|
|
xmlXPathObjectPtr cur;
|
|
|
|
/*
|
|
* Specific case: search a PI by name.
|
|
*/
|
|
NEXT;
|
|
nodetest = NODE_TEST_PI;
|
|
xmlXPathEvalLiteral(ctxt);
|
|
CHECK_ERROR;
|
|
if (CUR != ')')
|
|
ERROR(XPATH_UNCLOSED_ERROR);
|
|
NEXT;
|
|
xmlXPathStringFunction(ctxt, 1);
|
|
CHECK_ERROR;
|
|
cur = valuePop(ctxt);
|
|
name = xmlStrdup(cur->stringval);
|
|
xmlXPathFreeObject(cur);
|
|
} else
|
|
SKIP(2);
|
|
nodetest = NODE_TEST_PI;
|
|
goto search_nodes;
|
|
}
|
|
nodetest = NODE_TEST_NAME;
|
|
}
|
|
} else if ((CUR == ':') && (nodetest == NODE_TEST_NAME)) {
|
|
NEXT;
|
|
prefix = name;
|
|
if (CUR == '*') {
|
|
NEXT;
|
|
nodetest = NODE_TEST_ALL;
|
|
} else
|
|
name = xmlXPathParseNCName(ctxt);
|
|
} else if (name == NULL)
|
|
ERROR(XPATH_EXPR_ERROR);
|
|
}
|
|
|
|
search_nodes:
|
|
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "Basis : computing new set\n");
|
|
#endif
|
|
newset = xmlXPathNodeCollectAndTest(ctxt, axis, nodetest, nodetype,
|
|
prefix, name);
|
|
if (ctxt->context->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->context->nodelist);
|
|
ctxt->context->nodelist = newset;
|
|
ctxt->context->node = NULL;
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "Basis : ");
|
|
xmlXPathDebugNodeSet(stdout, ctxt->context->nodelist);
|
|
#endif
|
|
if (name != NULL) xmlFree(name);
|
|
if (prefix != NULL) xmlFree(prefix);
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalStep:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [4] Step ::= Basis Predicate*
|
|
* | AbbreviatedStep
|
|
* [12] AbbreviatedStep ::= '.'
|
|
* | '..'
|
|
*
|
|
* Evaluate 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.
|
|
*/
|
|
void
|
|
xmlXPathEvalStep(xmlXPathParserContextPtr ctxt) {
|
|
xmlNodeSetPtr newset = NULL;
|
|
|
|
SKIP_BLANKS;
|
|
if ((CUR == '.') && (NXT(1) == '.')) {
|
|
SKIP(2);
|
|
SKIP_BLANKS;
|
|
if (ctxt->context->nodelist == NULL) {
|
|
STRANGE
|
|
xmlXPathRoot(ctxt);
|
|
}
|
|
newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_PARENT,
|
|
NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
|
|
if (ctxt->context->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->context->nodelist);
|
|
ctxt->context->nodelist = newset;
|
|
ctxt->context->node = NULL;
|
|
} else if (CUR == '.') {
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
} else {
|
|
xmlXPathEvalBasis(ctxt);
|
|
SKIP_BLANKS;
|
|
while (CUR == '[') {
|
|
xmlXPathEvalPredicate(ctxt);
|
|
}
|
|
}
|
|
#ifdef DEBUG_STEP
|
|
fprintf(xmlXPathDebug, "Step : ");
|
|
xmlXPathDebugNodeSet(xmlXPathDebug, ctxt->context->nodelist);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalRelativeLocationPath:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [3] RelativeLocationPath ::= Step
|
|
* | RelativeLocationPath '/' Step
|
|
* | AbbreviatedRelativeLocationPath
|
|
* [11] AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
|
|
*
|
|
*/
|
|
void
|
|
xmlXPathEvalRelativeLocationPath(xmlXPathParserContextPtr ctxt) {
|
|
xmlNodeSetPtr newset = NULL;
|
|
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalStep(ctxt);
|
|
SKIP_BLANKS;
|
|
while (CUR == '/') {
|
|
if ((CUR == '/') && (NXT(1) == '/')) {
|
|
SKIP(2);
|
|
SKIP_BLANKS;
|
|
if (ctxt->context->nodelist == NULL) {
|
|
STRANGE
|
|
xmlXPathRoot(ctxt);
|
|
}
|
|
newset = xmlXPathNodeCollectAndTest(ctxt, AXIS_DESCENDANT_OR_SELF,
|
|
NODE_TEST_TYPE, XML_ELEMENT_NODE, NULL, NULL);
|
|
if (ctxt->context->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->context->nodelist);
|
|
ctxt->context->nodelist = newset;
|
|
ctxt->context->node = NULL;
|
|
xmlXPathEvalStep(ctxt);
|
|
} else if (CUR == '/') {
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
xmlXPathEvalStep(ctxt);
|
|
}
|
|
SKIP_BLANKS;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEvalLocationPath:
|
|
* @ctxt: the XPath Parser context
|
|
*
|
|
* [1] LocationPath ::= RelativeLocationPath
|
|
* | AbsoluteLocationPath
|
|
* [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
|
|
* | AbbreviatedAbsoluteLocationPath
|
|
* [10] AbbreviatedAbsoluteLocationPath ::=
|
|
* '//' RelativeLocationPath
|
|
*
|
|
* // 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.
|
|
*/
|
|
void
|
|
xmlXPathEvalLocationPath(xmlXPathParserContextPtr ctxt) {
|
|
xmlNodeSetPtr newset = NULL;
|
|
|
|
SKIP_BLANKS;
|
|
if (CUR != '/') {
|
|
xmlXPathEvalRelativeLocationPath(ctxt);
|
|
} else {
|
|
while (CUR == '/') {
|
|
if ((CUR == '/') && (NXT(1) == '/')) {
|
|
SKIP(2);
|
|
SKIP_BLANKS;
|
|
if (ctxt->context->nodelist == NULL)
|
|
xmlXPathRoot(ctxt);
|
|
newset = xmlXPathNodeCollectAndTest(ctxt,
|
|
AXIS_DESCENDANT_OR_SELF, NODE_TEST_TYPE,
|
|
XML_ELEMENT_NODE, NULL, NULL);
|
|
if (ctxt->context->nodelist != NULL)
|
|
xmlXPathFreeNodeSet(ctxt->context->nodelist);
|
|
ctxt->context->nodelist = newset;
|
|
ctxt->context->node = NULL;
|
|
xmlXPathEvalRelativeLocationPath(ctxt);
|
|
} else if (CUR == '/') {
|
|
NEXT;
|
|
SKIP_BLANKS;
|
|
xmlXPathRoot(ctxt);
|
|
if (CUR != 0)
|
|
xmlXPathEvalRelativeLocationPath(ctxt);
|
|
} else {
|
|
xmlXPathEvalRelativeLocationPath(ctxt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* xmlXPathEval:
|
|
* @str: the XPath expression
|
|
* @ctxt: the XPath context
|
|
*
|
|
* Evaluate the XPath Location Path in the given context.
|
|
*
|
|
* Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
|
|
* the caller has to free the object.
|
|
*/
|
|
xmlXPathObjectPtr
|
|
xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctxt) {
|
|
xmlXPathParserContextPtr pctxt;
|
|
xmlXPathObjectPtr res = NULL, tmp;
|
|
int stack = 0;
|
|
|
|
xmlXPathInit();
|
|
|
|
CHECK_CONTEXT
|
|
|
|
if (xmlXPathDebug == NULL)
|
|
xmlXPathDebug = stderr;
|
|
pctxt = xmlXPathNewParserContext(str, ctxt);
|
|
if (str[0] == '/')
|
|
xmlXPathRoot(pctxt);
|
|
xmlXPathEvalLocationPath(pctxt);
|
|
|
|
/* TODO: cleanup nodelist, res = valuePop(pctxt); */
|
|
do {
|
|
tmp = valuePop(pctxt);
|
|
if (tmp != NULL) {
|
|
xmlXPathFreeObject(tmp);
|
|
stack++;
|
|
}
|
|
} while (tmp != NULL);
|
|
if (stack != 0) {
|
|
fprintf(xmlXPathDebug, "xmlXPathEval: %d object left on the stack\n",
|
|
stack);
|
|
}
|
|
if (pctxt->error == XPATH_EXPRESSION_OK)
|
|
res = xmlXPathNewNodeSetList(pctxt->context->nodelist);
|
|
else
|
|
res = NULL;
|
|
xmlXPathFreeParserContext(pctxt);
|
|
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
|
|
|
|
if (xmlXPathDebug == NULL)
|
|
xmlXPathDebug = stderr;
|
|
pctxt = xmlXPathNewParserContext(str, ctxt);
|
|
xmlXPathEvalExpr(pctxt);
|
|
|
|
res = valuePop(pctxt);
|
|
do {
|
|
tmp = valuePop(pctxt);
|
|
if (tmp != NULL) {
|
|
xmlXPathFreeObject(tmp);
|
|
stack++;
|
|
}
|
|
} while (tmp != NULL);
|
|
if (stack != 0) {
|
|
fprintf(xmlXPathDebug, "xmlXPathEval: %d object left on the stack\n",
|
|
stack);
|
|
}
|
|
xmlXPathFreeParserContext(pctxt);
|
|
return(res);
|
|
}
|
|
|
|
#endif /* LIBXML_XPATH_ENABLED */
|