blob: a0f6a2a686a982100daada79a09df5a16a248a34 [file] [log] [blame]
/*
* xpath.c: XML Path Language implementation
* XPath is a language for addressing parts of an XML document,
* designed to be used by both XSLT and XPointer.
*
* Reference: W3C 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
*/
#include <config.h>
#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
#include <stdio.h>
#include <ctype.h>
#include "xmlmemory.h"
#include "tree.h"
#include "valid.h"
#include "xpath.h"
#include "parserInternals.h"
/*
* 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);
default:
return(0);
}
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)
static 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;
}
/* #define DEBUG */
/* #define DEBUG_STEP */
/* #define DEBUG_EXPR */
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 CHAR *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"); \
exit(1); \
} \
} \
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 CHAR to be parsed.
* CUR returns the current CHAR value, i.e. a 8 bit value if compiled
* in ISO-Latin or UTF-8, and the current 16 bit value if compiled
* in UNICODE mode. 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 CHAR. Same as CUR is should be used only
* to compare on ASCII based substring.
* SKIP(n) Skip n CHAR, 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 CHAR.
*/
#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
#ifndef USE_UTF_8
#define CURRENT (*ctxt->cur)
#define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
#else
#endif
/************************************************************************
* *
* 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 CHAR *cur;
const CHAR *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)
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 CHAR *prefix, const CHAR *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 CHAR * value
*
* Create a new xmlXPathObjectPtr of type string and of value @val
*
* Returns the newly created object.
*/
xmlXPathObjectPtr
xmlXPathNewString(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(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);
}
/**
* 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
* @variables: the variable list
* @functions: the function list
*
* 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->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);
#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->root == 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 CHAR *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 CHAR *str) {
int i;
xmlNodeSetPtr ns;
CHAR *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;
CHAR *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;
#ifdef DEBUG_EXPR
fprintf(xmlXPathDebug, "Equal: %s string %s \n",
arg1->stringval, arg2->stringval);
#endif
ret = !xmlStrcmp(arg1->stringval, arg2->stringval);
}
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 == (xmlNodePtr) ctxt->context->doc)
return(ctxt->context->doc->root);
return(ctxt->context->node->childs);
}
if (ctxt->context->node->type == XML_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 == (xmlNodePtr) ctxt->context->doc)
return(ctxt->context->doc->root);
return(ctxt->context->node->childs);
}
if (cur->childs != NULL) return(cur->childs);
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)
return(ctxt->context->node);
if (cur == (xmlNodePtr) ctxt->context->doc)
return(ctxt->context->doc->root);
if (cur->childs != NULL) return(cur->childs);
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);
}
/**
* 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
*/
if (cur == NULL) {
if (ctxt->context->node->parent == NULL)
return((xmlNodePtr) ctxt->context->doc);
return(ctxt->context->node->parent);
}
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->parent == NULL)
return((xmlNodePtr) ctxt->context->doc);
return(ctxt->context->node->parent);
}
if (cur == ctxt->context->doc->root)
return((xmlNodePtr) ctxt->context->doc);
if (cur == (xmlNodePtr) ctxt->context->doc)
return(NULL);
return(cur->parent);
}
/**
* 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) {
/*
* !!!!!!!!!!!!!
* the parent of an attribute or namespace node is the element
* to which the attribute or namespace node is attached
*/
if (cur == NULL)
return(ctxt->context->node);
if (cur == ctxt->context->doc->root)
return((xmlNodePtr) ctxt->context->doc);
if (cur == (xmlNodePtr) ctxt->context->doc)
return(NULL);
return(cur->parent);
}
/**
* 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->childs != NULL) return(cur->childs);
if (cur->next != NULL) return(cur->next);
do {
cur = cur->parent;
if (cur == NULL) return(NULL);
if (cur == ctxt->context->doc->root) 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->root) 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
*
* 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 CHAR *prefix, const CHAR *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, " seaching for none !!!\n");
break;
case NODE_TEST_TYPE:
fprintf(xmlXPathDebug, " seaching for type %d\n", type);
break;
case NODE_TEST_PI:
fprintf(xmlXPathDebug, " seaching for PI !!!\n");
break;
case NODE_TEST_ALL:
fprintf(xmlXPathDebug, " seaching for *\n");
break;
case NODE_TEST_NS:
fprintf(xmlXPathDebug, " seaching for namespace %s\n",
prefix);
break;
case NODE_TEST_NAME:
fprintf(xmlXPathDebug, " seaching 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) {
#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 CHAR *tokens;
const CHAR *cur;
CHAR *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->node;
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 {
CHAR *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 {
CHAR *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;
CHAR *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;
CHAR *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 CHAR *theLang;
const CHAR *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
*/
CHAR *
xmlXPathParseNCName(xmlXPathParserContextPtr ctxt) {
const CHAR *q;
CHAR *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 CHAR **
*
* 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.
*/
CHAR *
xmlXPathParseQName(xmlXPathParserContextPtr ctxt, CHAR **prefix) {
CHAR *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 CHAR *str) {
const CHAR *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 CHAR *q;
CHAR *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) {
CHAR *name;
CHAR *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 CHAR *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"))
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'