/*----------------------------------------------------------------------------
| Copyright (c) 1999-2001 Jochen Loewer (loewerj@hotmail.com)
|-----------------------------------------------------------------------------
|
| $Id$
|
|
| A XPath implementation (lexer/parser/evaluator) for tDOM,
| the DOM implementation for Tcl.
| Based on November 16 1999 Recommendation of the W3C
| (http://www.w3.org/TR/1999/REC-xslt-19991116)
|
|
| The contents of this file are subject to the Mozilla Public License
| Version 2.0 (the "License"); you may not use this file except in
| compliance with the License. You may obtain a copy of the License at
| http://www.mozilla.org/MPL/
|
| Software distributed under the License is distributed on an "AS IS"
| basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
| License for the specific language governing rights and limitations
| under the License.
|
| The Original Code is tDOM.
|
| The Initial Developer of the Original Code is Jochen Loewer
| Portions created by Jochen Loewer are Copyright (C) 1999 - 2001
| Jochen Loewer. All Rights Reserved.
|
| Portions created by Zoran Vasiljevic are Copyright (C) 2000-2002
| Zoran Vasiljevic. All Rights Reserved.
|
| Portions created by Rolf Ade are Copyright (C) 1999-2007
| Rolf Ade. All Rights Reserved.
|
| Contributor(s):
| April00 Rolf Ade Add support for following/preceding/
| precedingSibling axis plus several
| bug fixes
|
| Aug00 Rolf Ade Rewrite of comparisons plus several
| bug fixes/reports
|
| Aug01 Rolf Ade id(), unparsed-entity(), lang(), fixes
|
| 2002 Rolf Ade Namespace aware nodetests and NS wildcard
| expr, namespace aware variables, keys and
| function, made lexer utf-8 aware, node sets
| could now include nodes of different types,
| better IEEE 754 rules support, code
| restructured, serveral optimizations and bug
| fixes.
|
| written by Jochen Loewer
| July, 1999
|
\---------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
| Includes
|
\---------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <math.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>
#include <dom.h>
#include <domxpath.h>
#include <domxslt.h>
/*----------------------------------------------------------------------------
| Macros
|
\---------------------------------------------------------------------------*/
#define JDBG(x)
#define DBG(x)
#define DDBG(x)
#define TRACE(x) DDBG(fprintf(stderr,(x)))
#define TRACE1(x,a) DDBG(fprintf(stderr,(x),(a)))
#define TRACE2(x,a,b) DDBG(fprintf(stderr,(x),(a),(b)))
#define TRACE3(x,a,b,c) DDBG(fprintf(stderr,(x),(a),(b),(c)))
#define INITIAL_SIZE 100
#define ADD_TOKEN(t) if ((l+1)>=allocated) { \
tokens=(XPathTokens)REALLOC((char*)tokens, 2*allocated\
*sizeof(XPathToken)); \
allocated = allocated * 2; \
} \
tokens[l].token = (t); \
tokens[l++].pos = i; \
tokens[l].token = EOS; \
tokens[l].strvalue = NULL; \
tokens[l].intvalue = 0; \
tokens[l].realvalue = 0.0;
#define DeclProduction(name) static ast name (int *l,XPathTokens tokens,char **errMsg)
#define Production(name) static ast name (int *l,XPathTokens tokens,char **errMsg) \
{ char *__func = #name; \
ast a = NULL; \
TRACE2("\nProduction "#name": start l=%d next:%s\n", \
*l,token2str[tokens[*l].token]);
#define EndProduction TRACE3("EndProd %s: start l=%d next:%s\n", \
__func, *l,token2str[tokens[*l].token]); \
DDBG(printAst(0,a);) \
return a; \
}
#define LA tokens[*l].token
#define LA2 tokens[*l+1].token
#define LA3 tokens[*l+2].token
/* #define Recurse(p) rc=p(l,tokens,errMsg);if(rc==NULL)return rc;*/
#define Recurse(p) p(l,tokens,errMsg)
#define Consume(tk) if (tokens[*l].token == tk) { \
TRACE2("Production %s: %s consumed\n", \
__func, token2str[tokens[*l].token]); \
(*l)++; \
} else { \
if (*errMsg==NULL) {ErrExpected(#tk);} \
else return a; \
}
#define STRVAL tokens[(*l)-1].strvalue
#define INTVAL tokens[(*l)-1].intvalue
#define REALVAL tokens[(*l)-1].realvalue
#define NEWCONS ((ast)MALLOC(sizeof(astElem)))
#define IS_STR(c,s) (c==*(tokens[(*l)-1].strvalue))&&(strcmp(tokens[(*l)-1].strvalue,s)==0)
#define IS_FUNC(c,s) ((*(step->strvalue)==(c)) && (strcmp((s),step->strvalue)==0))
#define ErrExpected(msg) *errMsg = (char*)MALLOC(255); \
**errMsg = '\0'; \
strcpy(*errMsg, __func); \
strcat(*errMsg, ": Expected " #msg); \
return a;
#define CHECK_RC if (rc) return rc
#define checkRsAddNode(rs,node) if (useFastAdd) rsAddNodeFast( rs,node); \
else rsAddNode (rs,node);
/*----------------------------------------------------------------------------
| Types for Lexer
|
\---------------------------------------------------------------------------*/
typedef enum {
LPAR, RPAR, LBRACKET, RBRACKET, DOT, DOTDOT, ATTRIBUTEPREFIX,
ATTRIBUTE, COMMA, COLONCOLON, LITERAL, NSPREFIX, NSWC,
INTNUMBER, REALNUMBER, SLASH, SLASHSLASH,
PIPE, PLUS, MINUS, EQUAL, NOTEQ, LT, LTE, GT, GTE,
AND, OR, MOD, DIV, MULTIPLY, FUNCTION, VARIABLE,
FQVARIABLE, WCARDNAME, COMMENT, TEXT, PINSTR, NODE, AXISNAME,
EOS
} Token;
static char *token2str[] = {
"LPAR", "RPAR", "LBRACKET", "RBRACKET", "DOT", "DOTDOT", "ATTRIBUTEPREFIX",
"ATTRIBUTE", "COMMA", "COLONCOLON", "LITERAL", "NSPREFIX", "NSWC",
"INTNUMBER", "REALNUMBER", "SLASH", "SLASHSLASH",
"PIPE", "PLUS", "MINUS", "EQUAL", "NOTEQ", "LT", "LTE", "GT", "GTE",
"AND", "OR", "MOD", "DIV", "MULTIPLY", "FUNCTION", "VARIABLE",
"FQVARIABLE", "WCARDNAME", "COMMENT", "TEXT", "PI", "NODE", "AXISNAME",
"EOS"
};
typedef struct {
Token token;
char *strvalue;
long intvalue;
double realvalue;
int pos;
} XPathToken;
typedef XPathToken *XPathTokens;
/*----------------------------------------------------------------------------
| Types for abstract syntax trees
|
\---------------------------------------------------------------------------*/
static char *astType2str[] = {
"Int", "Real", "Mult", "Div", "Mod", "UnaryMinus", "IsNSElement",
"IsNode", "IsComment", "IsText", "IsPI", "IsSpecificPI", "IsElement",
"IsFQElement", "GetVar", "GetFQVar", "Literal", "ExecFunction", "Pred",
"EvalSteps", "SelectRoot", "CombineSets", "Add", "Subtract", "Less",
"LessOrEq", "Greater", "GreaterOrEq", "Equal", "NotEqual", "And", "Or",
"IsNSAttr", "IsAttr", "AxisAncestor", "AxisAncestorOrSelf",
"AxisAttribute", "AxisChild",
"AxisDescendant", "AxisDescendantOrSelf", "AxisFollowing",
"AxisFollowingSibling", "AxisNamespace", "AxisParent",
"AxisPreceding", "AxisPrecedingSilbing", "AxisSelf",
"GetContextNode", "GetParentNode", "AxisDescendantOrSelfLit",
"AxisDescendantLit", "SlashSlash",
"CombinePath", "IsRoot", "ToParent", "ToAncestors", "FillNodeList",
"FillWithCurrentNode",
"ExecIdKey"
};
/*----------------------------------------------------------------------------
| functionTag
|
\---------------------------------------------------------------------------*/
typedef enum {
f_unknown = 1,
f_boolean, f_ceiling, f_concat, f_contains, f_count, f_false, f_floor,
f_generateId, f_id, f_lang, f_last, f_localName, f_name, f_namespaceUri,
f_normalizeSpace, f_not, f_number, f_position, f_round, f_startsWith,
f_string, f_stringLength, f_substring, f_substringAfter,
f_substringBefore, f_sum, f_translate, f_true, f_unparsedEntityUri,
f_fqfunction
} functionTag;
/*----------------------------------------------------------------------------
| Prototypes / Forwards
|
\---------------------------------------------------------------------------*/
DeclProduction(OrExpr);
DeclProduction(Predicate);
DeclProduction(RelativeLocationPath);
DeclProduction(AbsoluteLocationPath);
char *xpathFuncString (xpathResultSet *rs );
static int xpathEvalStep (ast step, domNode *ctxNode, domNode *exprContext,
int position, xpathResultSet *nodeList,
xpathCBs *cbs, xpathResultSet *result,
int *docOrder, char **errMsg);
static int xpathEvalPredicate (ast steps, domNode *exprContext,
xpathResultSet *result,
xpathResultSet *stepResult,
xpathCBs *cbs, int *docOrder, char **errMsg);
/*----------------------------------------------------------------------------
| XPath result set functions
|
\---------------------------------------------------------------------------*/
void xpathRSFree ( xpathResultSet *rs ) {
if (rs->type == xNodeSetResult) {
if (!rs->intvalue) {
if (rs->nodes) FREE((char*)rs->nodes);
}
rs->nr_nodes = 0;
} else
if (rs->type == StringResult) {
if (rs->string) FREE((char*)rs->string);
}
rs->type = EmptyResult;
}
void rsPrint ( xpathResultSet *rs ) {
int i = 0,l; char tmp[80];
switch (rs->type) {
case EmptyResult:
fprintf(stderr, "empty result \n");
break;
case BoolResult:
fprintf(stderr, "boolean result: %ld \n", rs->intvalue);
break;
case IntResult:
fprintf(stderr, "int result: %ld \n", rs->intvalue);
break;
case RealResult:
fprintf(stderr, "real result: %f \n", rs->realvalue);
break;
case StringResult:
fprintf(stderr, "string result: -%*s-\n", rs->string_len,
rs->string);
break;
case xNodeSetResult:
if (!i) fprintf(stderr,"nodeSet result (len %d):\n",rs->nr_nodes);
for (i=0; i<rs->nr_nodes; i++) {
if (rs->nodes[i]->nodeType == ELEMENT_NODE) {
fprintf(stderr, "%2d domNode%p %s ",
i, (void *)rs->nodes[i], rs->nodes[i]->nodeName);
if (rs->nodes[i]->firstChild &&
rs->nodes[i]->firstChild->nodeType == TEXT_NODE)
{
l = ((domTextNode*)rs->nodes[i]->firstChild)->valueLength;
if (l > 25) l = 25;
memcpy(tmp, ((domTextNode*)rs->nodes[i]->firstChild)->nodeValue, l);
tmp[l] = '\0';
fprintf(stderr, "'%s'", tmp);
}
fprintf(stderr, "\n");
} else
if (rs->nodes[i]->nodeType == TEXT_NODE) {
l = ((domTextNode*)rs->nodes[i])->valueLength;
if (l > 60) l = 60;
memcpy(tmp, ((domTextNode*)rs->nodes[i])->nodeValue, l);
tmp[l] = '\0';
fprintf(stderr, "%2d domNode%p text:'%s' \n",
i, (void *)rs->nodes[i], tmp);
} else
if (rs->nodes[i]->nodeType == COMMENT_NODE) {
l = ((domTextNode*)rs->nodes[i])->valueLength;
memcpy (tmp, "<!--", 4);
if (l > 60) l = 60;
memcpy(&tmp[4], ((domTextNode*)rs->nodes[i])->nodeValue, l);
memcpy(&tmp[4+l], "-->", 3);
tmp[7+l] = '\0';
fprintf(stderr, "%2d domNode%p text:'%s' \n",
i, (void *)rs->nodes[i], tmp);
} else
if (rs->nodes[i]->nodeType == ATTRIBUTE_NODE) {
fprintf(stderr, "%2d Attr %s='%*s'\n", i,
((domAttrNode*)rs->nodes[i])->nodeName,
((domAttrNode*)rs->nodes[i])->valueLength,
((domAttrNode*)rs->nodes[i])->nodeValue);
}
}
break;
case NaNResult: fprintf (stderr, "NaN result\n"); break;
case InfResult: fprintf (stderr, "Inf result\n"); break;
case NInfResult: fprintf (stderr, "-Inf result\n"); break;
default:
fprintf (stderr, "unknown result type: '%d'!!!\n", rs->type);
break;
}
}
void rsSetReal ( xpathResultSet *rs, double d) {
rs->type = RealResult;
rs->realvalue = d;
}
void rsSetNaN ( xpathResultSet *rs ) {
rs->type = NaNResult;
}
void rsSetInf ( xpathResultSet *rs ) {
rs->type = InfResult;
}
void rsSetNInf ( xpathResultSet *rs ) {
rs->type = NInfResult;
}
void rsSetInt ( xpathResultSet *rs, long i) {
rs->type = IntResult;
rs->intvalue = i;
}
void rsSetBool ( xpathResultSet *rs, long i) {
rs->type = BoolResult;
rs->intvalue = (i ? 1 : 0);
}
void rsSetString ( xpathResultSet *rs, const char *s) {
rs->type = StringResult;
if (s) {
rs->string = tdomstrdup(s);
rs->string_len = strlen(s);
} else {
rs->string = tdomstrdup("");
rs->string_len = 0;
}
rs->nr_nodes = 0;
}
void rsAddNode ( xpathResultSet *rs, domNode *node) {
if ((rs->type != EmptyResult) && (rs->type != xNodeSetResult)) {
domPanic("Can not add node to non NodeSetResult xpathResultSet!");
}
if (rs->type == EmptyResult) {
rs->type = xNodeSetResult;
rs->nodes = (domNode**)MALLOC(INITIAL_SIZE * sizeof(domNode*));
rs->allocated = INITIAL_SIZE;
rs->nr_nodes = 1;
rs->nodes[0] = node;
} else {
int insertIndex;
int i;
if (rs->intvalue) {
/* we must do a copy-on-write */
domNode **nodes;
nodes = (domNode**)MALLOC(rs->allocated * sizeof(domNode*));
memcpy (nodes, rs->nodes, sizeof(domNode*) * rs->nr_nodes);
rs->nodes = nodes;
rs->intvalue = 0;
}
insertIndex = rs->nr_nodes;
for (i = rs->nr_nodes - 1; i >= 0; i--) {
if (node == rs->nodes[i]) return;
if (!domPrecedes (node, rs->nodes[i])) {
break;
}
insertIndex--;
}
if ((rs->nr_nodes+1) >= rs->allocated) {
rs->nodes = (domNode**)REALLOC((void*)rs->nodes,
2 * rs->allocated * sizeof(domNode*));
rs->allocated = rs->allocated * 2;
}
if (insertIndex == rs->nr_nodes) {
rs->nodes[rs->nr_nodes++] = node;
} else {
for (i = rs->nr_nodes - 1; i >= insertIndex; i--) {
rs->nodes[i+1] = rs->nodes[i];
}
rs->nodes[insertIndex] = node;
rs->nr_nodes++;
}
}
}
void rsAddNodeFast ( xpathResultSet *rs, domNode *node) {
if ((rs->type != EmptyResult) && (rs->type != xNodeSetResult)) {
domPanic("Can not add node to non NodeSetResult xpathResultSet!");
}
if (rs->type == EmptyResult) {
rs->type = xNodeSetResult;
rs->nodes = (domNode**)MALLOC(INITIAL_SIZE * sizeof(domNode*));
rs->allocated = INITIAL_SIZE;
rs->nr_nodes = 1;
rs->nodes[0] = node;
} else {
if ((rs->nr_nodes+1) >= rs->allocated) {
rs->nodes = (domNode**)REALLOC((void*)rs->nodes,
2 * rs->allocated * sizeof(domNode*));
rs->allocated = rs->allocated * 2;
}
rs->nodes[rs->nr_nodes++] = node;
}
}
void rsCopy ( xpathResultSet *to, xpathResultSet *from ) {
int i;
to->type = from->type;
to->intvalue = from->intvalue;
if (from->type == RealResult) {
to->realvalue = from->realvalue;
} else
if (from->type == StringResult) {
to->string = tdomstrdup(from->string);
to->string_len = from->string_len;
} else
if (from->type == xNodeSetResult) {
to->nr_nodes = from->nr_nodes;
to->nodes = (domNode**)MALLOC(from->nr_nodes * sizeof(domNode*));
for (i=0; i<from->nr_nodes; i++)
to->nodes[i] = from->nodes[i];
to->intvalue = 0;
}
}
/*----------------------------------------------------------------------------
| AST construct functions
|
\---------------------------------------------------------------------------*/
static ast New( astType type ) {
ast t = NEWCONS;
t->type = type;
t->next = t->child = NULL;
t->strvalue = NULL;
t->intvalue = 0;
t->realvalue = 0.0;
return t;
}
static ast New1( astType type, ast a) {
ast t = NEWCONS;
t->type = type;
t->next = NULL;
t->child = a;
t->strvalue = NULL;
t->intvalue = 0;
t->realvalue = 0.0;
return t;
}
static ast New1WithEvalSteps( astType type, ast a) {
ast t = NEWCONS;
t->type = type;
t->next = NULL;
if (a && a->next) {
t->child = New1(EvalSteps,a);
} else {
t->child = a;
}
t->strvalue = NULL;
t->intvalue = 0;
t->realvalue = 0.0;
return t;
}
static ast New2( astType type, ast a, ast b ) {
ast t = NEWCONS;
t->type = type;
t->next = NULL;
t->strvalue = NULL;
t->intvalue = 0;
t->realvalue = 0.0;
if (a && a->next) {
t->child = New1(EvalSteps,a);
} else {
t->child = a;
}
if (b && b->next) {
t->child->next = New1(EvalSteps, b);
} else {
t->child->next = b;
}
return t;
}
static ast NewInt( long i ) {
ast t = NEWCONS;
t->type = Int;
t->strvalue = NULL;
t->intvalue = i;
t->realvalue = 0.0;
t->next = t->child = NULL;
return t;
}
static ast NewReal( double r ) {
ast t = NEWCONS;
t->type = Real;
t->strvalue = NULL;
t->intvalue = 0;
t->realvalue = r;
t->next = t->child = NULL;
return t;
}
static ast NewStr( astType type, char *str ) {
ast t = NEWCONS;
t->type = type;
t->strvalue = tdomstrdup(str);
t->intvalue = 0;
t->realvalue = 0.0;
t->next = t->child = NULL;
return t;
}
static ast Append( ast m, ast n ) {
if (!n) return NULL;
if (!m) return NULL;
while (m->next != NULL) m = m->next;
m->next = n;
return m;
}
static ast AddChild( ast m, ast child ) {
if (!child) return NULL;
if (!m) return NULL;
if (m->child == NULL) {
m->child = child;
} else {
ast c = m->child;
while (c->next != NULL) c = c->next;
c->next = child;
}
return m;
}
static ast AddChildWithEvalSteps( ast m, ast child ) {
if (!child) return NULL;
if (!m) return NULL;
if (child->next) {
child = New1(EvalSteps, child);
}
if (m->child == NULL) {
m->child = child;
} else {
ast c = m->child;
while (c->next != NULL) c = c->next;
c->next = child;
}
/*child->next = NULL;*/
return m;
}
static void freeAst (ast t)
{
ast tmp;
while (t) {
tmp = t->next;
if (t->strvalue) FREE(t->strvalue);
if (t->child) freeAst (t->child);
FREE((char*)t);
t = tmp;
}
}
void printAst (int depth, ast t)
{
int i;
while (t) {
for (i=0; i<depth; i++) fprintf(stderr, " ");
fprintf(stderr, "%s ", astType2str[t->type]);
switch (t->type) {
case Int : fprintf(stderr, "%ld", t->intvalue); break;
case Real: fprintf(stderr, "%f", t->realvalue); break;
case IsElement:
case IsFQElement:
case IsNSAttr:
case IsAttr:
case ExecFunction:
case Literal:
case GetFQVar:
case GetVar: fprintf(stderr, "'%s'", t->strvalue); break;
default: break;
}
fprintf(stderr, "\n");
if (t->child) printAst (depth+1, t->child);
t = t->next;
}
}
/*----------------------------------------------------------------------------
| xpathFreeAst
|
\---------------------------------------------------------------------------*/
void xpathFreeAst(
ast t
)
{
freeAst(t);
}
/*----------------------------------------------------------------------------
| xpathLexer
|
\---------------------------------------------------------------------------*/
static XPathTokens xpathLexer (
char *xpath,
domNode *exprContext,
char **prefixMappings,
int *useNamespaceAxis,
xpathParseVarCB *varParseCB,
char **errMsg
)
{
int l, allocated;
int i, k, start, offset;
char delim, *ps, save, tmpErr[80], *tailptr;
const char *uri;
XPathTokens tokens;
int token = EOS;
tokens = (XPathTokens)MALLOC(INITIAL_SIZE * sizeof(XPathToken));
if (tokens == NULL) {
*errMsg = tdomstrdup("Unable to alloc initial memory!");
return NULL;
}
allocated = INITIAL_SIZE;
l = 0;
tokens[l].token = EOS;
tokens[l].strvalue = NULL;
tokens[l].intvalue = 0;
tokens[l].realvalue = 0.0;
i = 0;
while (xpath[i]) {
switch (xpath[i]) {
case ' ' :
case '\n':
case '\r':
case '\t': i++; continue;
case '(': token = LPAR; break;
case ')': token = RPAR; break;
case '[': token = LBRACKET; break;
case ']': token = RBRACKET; break;
case '@': i++;
while (xpath[i] && IS_XML_WHITESPACE(xpath[i])) i++;
if ( isNCNameStart (&xpath[i]) ) {
ps = &(xpath[i]);
i += UTF8_CHAR_LEN (xpath[i]);
while (xpath[i] && isNCNameChar (&xpath[i]))
i += UTF8_CHAR_LEN (xpath[i]);
save = xpath[i];
xpath[i] = '\0';
if (save == ':' && xpath[i+1] != ':') {
uri = domLookupPrefixWithMappings (
exprContext, ps, prefixMappings);
if (!uri) {
xpath[i] = save;
*errMsg = tdomstrdup ("Prefix doesn't"
" resolve");
return tokens;
}
tokens[l].strvalue = tdomstrdup (uri);
xpath[i] = save;
token = ATTRIBUTEPREFIX;
ADD_TOKEN (token);
if (xpath[i+1] == '*') {
token = ATTRIBUTE;
tokens[l].strvalue = tdomstrdup("*");
i++;
} else {
ps = &(xpath[++i]);
if (!(isNCNameStart (&xpath[i]))) {
*errMsg =
tdomstrdup ("Illegal attribute"
" name");
return tokens;
}
i += UTF8_CHAR_LEN (xpath[i]);
while (xpath[i] && isNCNameChar (&xpath[i]))
i += UTF8_CHAR_LEN (xpath[i]);
save = xpath[i];
xpath[i] = '\0';
token = ATTRIBUTE;
tokens[l].strvalue = tdomstrdup(ps);
xpath[i--] = save;
}
} else {
tokens[l].strvalue = tdomstrdup(ps);
xpath[i] = save;
token = ATTRIBUTE;
i--;
}
} else if (xpath[i]=='*') {
tokens[l].strvalue = tdomstrdup("*");
token = ATTRIBUTE;
} else {
*errMsg = tdomstrdup("Expected attribute name");
return tokens;
}; break;
case ',': token = COMMA; break;
case ':': if (xpath[i+1] == ':') {
token = COLONCOLON;
i++;
} else {
*errMsg = tdomstrdup("Unexpected token ':'");
return tokens;
}; break;
case '"' :
case '\'': delim = xpath[i]; start = ++i;
while (xpath[i] && (xpath[i] != delim)) i++;
if (!xpath[i]) {
*errMsg = tdomstrdup("Undetermined string");
return tokens;
}
xpath[i] = '\0'; /* terminate string */
tokens[l].strvalue = tdomstrdup(&xpath[start]);
token = LITERAL;
xpath[i] = delim;
break;
case '/': if (xpath[i+1] == '/') {
token = SLASHSLASH;
i++;
} else {
token = SLASH;
};
break;
case '|': token = PIPE; break;
case '+': token = PLUS; break;
case '-': token = MINUS; break;
case '=': token = EQUAL; break;
case '!': if (xpath[i+1] == '=') {
token = NOTEQ;
i++;
} else {
*errMsg = tdomstrdup("Unexpected token '!'");
return tokens;
}; break;
case '<': if (xpath[i+1] == '=') {
token = LTE;
i++;
} else {
token = LT;
};break;
case '>': if (xpath[i+1] == '=') {
token = GTE;
i++;
} else {
token = GT;
}; break;
case '*': if ((l>0)
&& (tokens[l-1].token != COLONCOLON)
&& (tokens[l-1].token != LPAR)
&& (tokens[l-1].token != LBRACKET)
&& (tokens[l-1].token != COMMA)
&& (tokens[l-1].token != SLASH)
&& (tokens[l-1].token != SLASHSLASH)
) {
token = MULTIPLY;
} else {
token = WCARDNAME;
tokens[l].strvalue = tdomstrdup("*");
}; break;
case '$': if (varParseCB) {
ps = (varParseCB->parseVarCB) (
varParseCB->parseVarClientData, &xpath[i],
&offset, errMsg
);
if (ps) {
token = LITERAL;
tokens[l].strvalue = tdomstrdup (ps);
i += offset - 1;
} else {
return tokens;
}
} else {
i++;
if ( isNCNameStart (&xpath[i])) {
ps = &(xpath[i]);
i += UTF8_CHAR_LEN (xpath[i]);
while (xpath[i] && isNCNameChar(&xpath[i]))
i += UTF8_CHAR_LEN(xpath[i]);
if (xpath[i] == ':' && xpath[i+1] != ':') {
token = FQVARIABLE;
save = xpath[i];
xpath[i] = '\0';
uri = domLookupPrefixWithMappings (
exprContext, ps, prefixMappings);
if (!uri) {
xpath[i] = save;
*errMsg = tdomstrdup ("Prefix doesn't"
" resolve");
return tokens;
}
tokens[l].strvalue = tdomstrdup (uri);
xpath[i] = save;
ADD_TOKEN (token);
ps = &(xpath[++i]);
if (!isNCNameStart (&xpath[i])) {
*errMsg = tdomstrdup ("Illegal variable"
" name");
return tokens;
}
i += UTF8_CHAR_LEN (xpath[i]);
while (xpath[i] && isNCNameChar (&xpath[i]))
i += UTF8_CHAR_LEN (xpath[i]);
}
token = VARIABLE;
save = xpath[i];
xpath[i] = '\0';
tokens[l].strvalue = tdomstrdup(ps);
xpath[i--] = save;
} else {
*errMsg = tdomstrdup("Expected variable name");
return tokens;
}
}
break;
case '%': if (!varParseCB) {
*errMsg = tdomstrdup ("Unexpected char '%'");
return tokens;
}
ps = (varParseCB->parseVarCB) (
varParseCB->parseVarClientData, &xpath[i],
&offset, errMsg
);
if (ps) {
token = WCARDNAME;
tokens[l].strvalue = tdomstrdup (ps);
/* We kind of misuse the (in case of
* WCARDNAME token otherwise not used)
* intvalue to mark this WCARDNAME token
* to be meant as literal - this is to
* distinguish between '*' as wildcard and
* as literal element name. */
tokens[l].intvalue = 1;
i += offset - 1;
} else {
return tokens;
}
break;
case '.': if (xpath[i+1] == '.') {
token = DOTDOT;
i++;
break;
} else if (!isdigit((unsigned char)xpath[i+1])) {
token = DOT;
break;
}
/* DOT followed by digit, ie a REAL.
Handled by default. Fall throu */
default: if ( isNCNameStart (&xpath[i])) {
ps = &(xpath[i]);
i += UTF8_CHAR_LEN (xpath[i]);
while (xpath[i] && isNCNameChar(&xpath[i])) {
i += UTF8_CHAR_LEN(xpath[i]);
}
k = i;
if (xpath[i] == ':') {
if (xpath[i+1] == '*') {
save = xpath[i];
xpath[i] = '\0'; /* terminate */
token = NSWC;
uri = domLookupPrefixWithMappings (
exprContext, ps, prefixMappings);
if (!uri) {
xpath[i] = save;
*errMsg = tdomstrdup ("Prefix doesn't"
" resolve");
return tokens;
}
tokens[l].strvalue = tdomstrdup (uri);
xpath[i] = save;
i++;
break;
}
if (xpath[i+1] != ':') {
save = xpath[i];
xpath[i] = '\0'; /* terminate */
token = NSPREFIX;
uri = domLookupPrefixWithMappings (
exprContext, ps, prefixMappings);
if (!uri) {
xpath[i] = save;
*errMsg = tdomstrdup ("Prefix doesn't"
" resolve");
return tokens;
}
tokens[l].strvalue = tdomstrdup (uri);
xpath[i] = save;
ADD_TOKEN (token);
ps = &(xpath[++i]);
if (!(isNCNameStart (&xpath[i]))) {
*errMsg =
tdomstrdup ("Illegal character in"
" localname");
return tokens;
}
i += UTF8_CHAR_LEN (xpath[i]);
while (xpath[i] && isNCNameChar (&xpath[i]))
i += UTF8_CHAR_LEN (xpath[i]);
k = i;
}
}
/* read over white space */
while ((xpath[k] == ' ') ||
(xpath[k] == '\n') ||
(xpath[k] == '\r') ||
(xpath[k] == '\t') ) k++;
if (l>0 && tokens[l-1].token == NSPREFIX) {
if (xpath[k]!='(') token = WCARDNAME;
else token = FUNCTION;
save = xpath[i];
xpath[i] = '\0';
tokens[l].strvalue = tdomstrdup(ps);
xpath[i--] = save;
break;
}
if (xpath[k]=='(') {
save = xpath[i];
xpath[i] = '\0'; /* terminate */
if (strcmp(ps,"text")==0) {
token = TEXT;
} else if (strcmp(ps,"node")==0) {
token = NODE;
} else if (strcmp(ps,"comment")==0) {
token = COMMENT;
} else if (strcmp(ps,"processing-instruction")==0) {
token = PINSTR;
} else {
if ((save!='(') && (strcmp(ps,"and")==0)) token = AND;
else
if ((save!='(') && (strcmp(ps,"or")==0)) token = OR;
else
if ((save!='(') && (strcmp(ps,"mod")==0)) token = MOD;
else
if ((save!='(') && (strcmp(ps,"div")==0)) token = DIV;
else {
token = FUNCTION;
tokens[l].strvalue = tdomstrdup(ps);
}
}
xpath[i] = save;
} else if ((xpath[k]==':') && (xpath[k+1]==':')) {
token = AXISNAME;
save = xpath[i];
xpath[i] = '\0'; /* terminate */
tokens[l].strvalue = tdomstrdup(ps);
if (ps[0] == 'n'&& strcmp(ps, "namespace")==0) {
*useNamespaceAxis = 1;
}
xpath[i] = save;
} else {
save = xpath[i];
xpath[i] = '\0';
if ((l>0)
&& (tokens[l-1].token != COLONCOLON)
&& (tokens[l-1].token != LPAR)
&& (tokens[l-1].token != LBRACKET)
&& (tokens[l-1].token != COMMA)
&& (tokens[l-1].token != SLASH)
&& (tokens[l-1].token != SLASHSLASH)
&& (tokens[l-1].token != PIPE)
&& (tokens[l-1].token != PLUS)
&& (tokens[l-1].token != MINUS)
&& (tokens[l-1].token != EQUAL)
&& (tokens[l-1].token != NOTEQ)
&& (tokens[l-1].token != LT)
&& (tokens[l-1].token != LTE)
&& (tokens[l-1].token != GT)
&& (tokens[l-1].token != GTE)
&& (tokens[l-1].token != AND)
&& (tokens[l-1].token != OR)
&& (tokens[l-1].token != MOD)
&& (tokens[l-1].token != DIV)
&& (tokens[l-1].token != MULTIPLY)
) {
if (strcmp(ps,"and")==0) {
token = AND;
} else if (strcmp(ps,"or")==0) {
token = OR;
} else if (strcmp(ps,"mod")==0) {
token = MOD;
} else if (strcmp(ps,"div")==0) {
token = DIV;
} else {
token = WCARDNAME;
tokens[l].strvalue = tdomstrdup(ps);
}
} else {
token = WCARDNAME;
tokens[l].strvalue = tdomstrdup(ps);
}
xpath[i] = save;
}
i--;
} else if (isdigit((unsigned char)xpath[i])
|| (xpath[i] == '.')) {
if (xpath[i] == '.') {
token = REALNUMBER;
} else {
token = INTNUMBER;
}
ps = &(xpath[i++]);
while (xpath[i] && isdigit((unsigned char)xpath[i]))
i++;
if (xpath[i]=='.') {
if (token == REALNUMBER) {
sprintf (tmpErr, "Unexpected character "
"'%c' at position %d", xpath[i],
i);
*errMsg = tdomstrdup (tmpErr);
return tokens;
}
token = REALNUMBER;
i++;
while (xpath[i]
&& isdigit((unsigned char)xpath[i])) i++;
}
save = xpath[i];
xpath[i] = '\0';
if (token == INTNUMBER) {
errno = 0;
tokens[l].intvalue = strtol(ps, NULL, 10);
if (errno == ERANGE
&& ( tokens[l].intvalue == LONG_MAX
|| tokens[l].intvalue == LONG_MIN)) {
token = REALNUMBER;
}
}
tokens[l].realvalue = strtod(ps, &tailptr);
xpath[i--] = save;
if (tokens[l].realvalue == 0.0 && tailptr == ps) {
sprintf (tmpErr, "Number value too large "
"at position %d", i);
*errMsg = tdomstrdup (tmpErr);
return tokens;
}
} else {
sprintf (tmpErr, "Unexpected character '%c' at "
"position %d", xpath[i], i);
*errMsg = tdomstrdup (tmpErr);
return tokens;
}
break;
} /* switch */
ADD_TOKEN(token);
i++;
}
ADD_TOKEN(EOS);
return tokens;
} /* xpathLexer */
/*-----------------------------------------------------------------
| NodeTest production
|
\----------------------------------------------------------------*/
Production(NodeTest)
if (LA==NODE) {
Consume(NODE);
Consume(LPAR);
Consume(RPAR);
a = New (IsNode);
} else
if (LA==TEXT) {
Consume(TEXT);
Consume(LPAR);
Consume(RPAR);
a = New (IsText);
} else
if (LA==COMMENT) {
Consume(COMMENT);
Consume(LPAR);
Consume(RPAR);
a = New (IsComment);
} else
if (LA==PINSTR) {
Consume(PINSTR);
Consume(LPAR);
if (LA==LITERAL) {
Consume(LITERAL);
a = NewStr (IsSpecificPI, STRVAL);
} else {
a = New (IsPI);
}
Consume(RPAR);
} else
if (LA==MULTIPLY) {
Consume(MULTIPLY);
a = NewStr (IsElement, "*");
} else
if (LA==NSPREFIX) {
ast b;
Consume (NSPREFIX);
a = NewStr (IsFQElement, STRVAL);
Consume (WCARDNAME);
b = NewStr (IsElement, STRVAL);
AddChild (a, b);
} else
if (LA==NSWC) {
Consume (NSWC);
a = NewStr (IsNSElement, STRVAL);
} else {
Consume(WCARDNAME);
a = NewStr (IsElement, STRVAL);
a->intvalue = INTVAL;
}
EndProduction
/*-----------------------------------------------------------------
| AbbreviatedBasis production
|
\----------------------------------------------------------------*/
Production(AbbreviatedBasis)
if (LA==ATTRIBUTE) {
Consume(ATTRIBUTE);
a = New1( AxisAttribute, NewStr(IsAttr, STRVAL) );
} else
if (LA==ATTRIBUTEPREFIX) {
ast b, c;
Consume(ATTRIBUTEPREFIX);
a = New (AxisAttribute);
b = NewStr (IsNSAttr, STRVAL);
AddChild (a, b);
Consume(ATTRIBUTE);
c = NewStr (IsAttr, STRVAL);
AddChild (b, c);
}
else {
a = New1( AxisChild, Recurse(NodeTest));
}
EndProduction
/*-----------------------------------------------------------------
| getFunctionTag
|
\----------------------------------------------------------------*/
static functionTag
getFunctionTag (char *funcName)
{
switch (funcName[0]) {
case 'b':
if (strcmp (funcName, "boolean")==0) return f_boolean;
break;
case 'c':
if (strcmp (funcName, "ceiling")==0) return f_ceiling;
else if (strcmp (funcName, "concat")==0) return f_concat;
else if (strcmp (funcName, "contains")==0) return f_contains;
else if (strcmp (funcName, "count")==0) return f_count;
break;
case 'f':
if (strcmp (funcName, "false")==0) return f_false;
else if (strcmp (funcName, "floor")==0) return f_floor;
break;
case 'g':
if (strcmp (funcName, "generate-id")==0) return f_generateId;
break;
case 'i':
if (strcmp (funcName, "id")==0) return f_id;
case 'l':
if (strcmp (funcName, "lang")==0) return f_lang;
else if (strcmp (funcName, "last")==0) return f_last;
else if (strcmp (funcName, "local-name")==0) return f_localName;
break;
case 'n':
if (strcmp (funcName, "name")==0) return f_name;
else if (strcmp (funcName, "namespace-uri")==0) return f_namespaceUri;
else if (strcmp (funcName, "normalize-space")==0) return f_normalizeSpace;
else if (strcmp (funcName, "not")==0) return f_not;
else if (strcmp (funcName, "number")==0) return f_number;
break;
case 'p':
if (strcmp (funcName, "position")==0) return f_position;
break;
case 'r':
if (strcmp (funcName, "round")==0) return f_round;
break;
case 's':
if (strcmp (funcName, "starts-with")==0) return f_startsWith;
else if (strcmp (funcName, "string")==0) return f_string;
else if (strcmp (funcName, "string-length")==0) return f_stringLength;
else if (strcmp (funcName, "substring")==0) return f_substring;
else if (strcmp (funcName, "substring-after")==0) return f_substringAfter;
else if (strcmp (funcName, "substring-before")==0) return f_substringBefore;
else if (strcmp (funcName, "sum")==0) return f_sum;
break;
case 't':
if (strcmp (funcName, "translate")==0) return f_translate;
else if (strcmp (funcName, "true")==0) return f_true;
break;
case 'u':
if (strcmp (funcName, "unparsed-entity-uri")==0) return f_unparsedEntityUri;
break;
default:
break;
}
return f_unknown;
}
/*-----------------------------------------------------------------
| FilterExpr production
|
\----------------------------------------------------------------*/
Production(FilterExpr)
if (LA==VARIABLE) {
Consume(VARIABLE);
a = NewStr( GetVar, STRVAL);
} else if (LA==FQVARIABLE) {
Consume(FQVARIABLE);
a = NewStr( GetFQVar, STRVAL);
Consume(VARIABLE);
AddChild (a, NewStr( GetVar, STRVAL));
} else if (LA==LPAR) {
Consume(LPAR);
a = New1(EvalSteps, Recurse(OrExpr));
Consume(RPAR);
} else if (LA==LITERAL) {
Consume(LITERAL);
a = NewStr( Literal, STRVAL);
} else if (LA==INTNUMBER) {
Consume(INTNUMBER);
a = NewInt( INTVAL );
} else if (LA==REALNUMBER) {
Consume(REALNUMBER);
a = NewReal( REALVAL );
} else if (LA==FUNCTION || LA==NSPREFIX) {
if (LA==FUNCTION) {
Consume(FUNCTION);
a = NewStr( ExecFunction, STRVAL);
a->intvalue = getFunctionTag (STRVAL);
} else {
Consume(NSPREFIX);
a = NewStr( ExecFunction, STRVAL);
a->intvalue = f_fqfunction;
Consume(FUNCTION);
AddChild (a, NewStr( ExecFunction, STRVAL));
}
Consume(LPAR);
if (LA!=RPAR) {
AddChildWithEvalSteps (a, Recurse(OrExpr));
while(LA==COMMA) {
Consume(COMMA);
AddChildWithEvalSteps (a, Recurse(OrExpr) );
}
}
Consume(RPAR);
} else {
ErrExpected("$var or (expr) or literal or number or func");
}
while (LA==LBRACKET) {
ast b;
b = Recurse(Predicate);
if (!b) return a;
Append( a, New1WithEvalSteps( Pred, b));
}
EndProduction
/*-----------------------------------------------------------------
| PathExpr production
|
\----------------------------------------------------------------*/
Production(PathExpr)
if ( (LA==VARIABLE)
||(LA==FQVARIABLE)
||(LA==LPAR)
||(LA==LITERAL)
||(LA==INTNUMBER)
||(LA==REALNUMBER)
||(LA==FUNCTION)
||(LA==NSPREFIX && LA2==FUNCTION)
) {
a = Recurse(FilterExpr);
if (LA==SLASH) {
Consume(SLASH);
Append(a, Recurse(RelativeLocationPath));
} else if (LA==SLASHSLASH) {
ast b;
Consume(SLASHSLASH);
b = Recurse(RelativeLocationPath);
if (!b) return a;
if (b->type == AxisChild) {
b->type = AxisDescendant;
} else {
Append(a, New( AxisDescendantOrSelf ) );
}
Append(a, b );
}
} else {
if ( (LA==SLASH) || (LA==SLASHSLASH)) {
return Recurse(AbsoluteLocationPath);
} else {
return Recurse(RelativeLocationPath);
}
}
EndProduction
/*-----------------------------------------------------------------
| UnionExpr production
|
\----------------------------------------------------------------*/
Production(UnionExpr)
a = Recurse(PathExpr);
while (LA==PIPE) {
Consume(PIPE);
a = New2( CombineSets, a, Recurse(PathExpr));
}
EndProduction
/*-----------------------------------------------------------------
| UnaryExpr production
|
\----------------------------------------------------------------*/
Production(UnaryExpr)
if (LA==MINUS) {
Consume(MINUS);
a = Recurse(UnionExpr);
if (!a) {
if (*errMsg == NULL) { ErrExpected("UnionExpr"); }
return NULL;
}
if ((a->type == Int) && (a->next == NULL)) {
a->intvalue = a->intvalue * -1;
} else
if ((a->type == Real) && (a->next == NULL)) {
a->realvalue = a->realvalue * -1;
} else {
a = New1( UnaryMinus, a);
}
} else {
a = Recurse(UnionExpr);
}
EndProduction
/*-----------------------------------------------------------------
| MultiplicativeExpr production
|
\----------------------------------------------------------------*/
Production(MultiplicativeExpr)
a = Recurse(UnaryExpr);
while ( (LA==MULTIPLY)
||(LA==DIV)
||(LA==MOD)
) {
if (LA==MULTIPLY) {
Consume(MULTIPLY);
a = New2( Mult, a, Recurse(UnaryExpr));
} else if (LA==DIV) {
Consume(DIV);
a = New2( Div, a, Recurse(UnaryExpr));
} else {
Consume(MOD);
a = New2( Mod, a, Recurse(UnaryExpr));
}
}
EndProduction
/*-----------------------------------------------------------------
| AdditiveExpr production
|
\----------------------------------------------------------------*/
Production(AdditiveExpr)
a = Recurse(MultiplicativeExpr);
while ( (LA==PLUS)
||(LA==MINUS)
) {
if (LA==PLUS) {
Consume(PLUS);
a = New2( Add, a, Recurse(MultiplicativeExpr));
} else {
Consume(MINUS);
a = New2( Subtract, a, Recurse(MultiplicativeExpr));
}
}
EndProduction
/*-----------------------------------------------------------------
| RelationalExpr production
|
\----------------------------------------------------------------*/
Production(RelationalExpr)
a = Recurse(AdditiveExpr);
while ( (LA==LT)
||(LA==LTE)
||(LA==GT)
||(LA==GTE)
) {
if (LA==LT) {
Consume(LT);
a = New2( Less, a, Recurse(AdditiveExpr));
} else if (LA==LTE) {
Consume(LTE);
a = New2( LessOrEq, a, Recurse(AdditiveExpr));
} else if (LA==GT) {
Consume(GT);
a = New2( Greater, a, Recurse(AdditiveExpr));
} else {
Consume(GTE);
a = New2( GreaterOrEq, a, Recurse(AdditiveExpr));
}
}
EndProduction
/*-----------------------------------------------------------------
| EqualityExpr production
|
\----------------------------------------------------------------*/
Production(EqualityExpr)
a = Recurse(RelationalExpr);
while ( (LA==EQUAL)
||(LA==NOTEQ)
) {
if (LA==EQUAL) {
Consume(EQUAL);
a = New2( Equal, a, Recurse(RelationalExpr));
} else {
Consume(NOTEQ);
a = New2( NotEqual, a, Recurse(RelationalExpr));
}
}
EndProduction
/*-----------------------------------------------------------------
| AndExpr production
|
\----------------------------------------------------------------*/
Production(AndExpr)
a = Recurse(EqualityExpr);
while (LA==AND) {
Consume(AND);
a = New2( And, a, Recurse(EqualityExpr));
}
EndProduction
/*-----------------------------------------------------------------
| OrExpr production
|
\----------------------------------------------------------------*/
Production(OrExpr)
a = Recurse(AndExpr);
while (LA==OR) {
Consume(OR);
a = New2( Or, a, Recurse(AndExpr));
}
EndProduction
/*-----------------------------------------------------------------
| Predicate production
|
\----------------------------------------------------------------*/
Production(Predicate)
Consume(LBRACKET);
a = Recurse(OrExpr);
Consume(RBRACKET);
EndProduction
/*-----------------------------------------------------------------
| Basis production
|
\----------------------------------------------------------------*/
Production(Basis)
if (LA==AXISNAME) {
astType t;
Consume(AXISNAME);
if (IS_STR('c',"child")) { t = AxisChild;
} else if (IS_STR('d',"descendant")) { t = AxisDescendantLit;
} else if (IS_STR('d',"descendant-or-self")) { t = AxisDescendantOrSelfLit;
} else if (IS_STR('s',"self")) { t = AxisSelf;
} else if (IS_STR('a',"attribute")) { t = AxisAttribute;
} else if (IS_STR('a',"ancestor")) { t = AxisAncestor;
} else if (IS_STR('a',"ancestor-or-self")) { t = AxisAncestorOrSelf;
} else if (IS_STR('f',"following")) { t = AxisFollowing;
} else if (IS_STR('f',"following-sibling")) { t = AxisFollowingSibling;
} else if (IS_STR('n',"namespace")) { t = AxisNamespace;
} else if (IS_STR('p',"parent")) { t = AxisParent;
} else if (IS_STR('p',"preceding")) { t = AxisPreceding;
} else if (IS_STR('p',"preceding-sibling")) { t = AxisPrecedingSibling;
} else {
ErrExpected("correct axis name");
}
a = New( t );
Consume(COLONCOLON);
AddChild( a, Recurse(NodeTest));
} else {
a = Recurse(AbbreviatedBasis);
}
EndProduction
/*-----------------------------------------------------------------
| Step production
|
\----------------------------------------------------------------*/
static long IsStepPredOptimizable (ast a) {
ast b;
long left;
/* Must be called with a != NULL */
DBG (
fprintf (stderr, "IsStepPredOptimizable:\n");
printAst (0,a);
)
switch (a->type) {
case Int: return a->intvalue;
case Less:
case LessOrEq:
b = a->child;
if (!b) return 0;
if (b->type != ExecFunction
|| b->intvalue != f_position) return 0;
b = b->next;
if (!b) return 0;
if (b->type != Int) return 0;
if (a->type == Less) return b->intvalue;
else return b->intvalue + 1;
case Greater:
case GreaterOrEq:
b = a->child;
if (!b) return 0;
if (b->type != Int) return 0;
left = b->intvalue;
b = b->next;
if (!b) return 0;
if (b->type != ExecFunction
|| b->intvalue != f_position) return 0;
if (a->type == Greater) return left;
else return left + 1;
case Equal:
b = a->child;
if (!b) return 0;
if (b->type == Int
&& b->next
&& b->next->type == ExecFunction
&& b->next->intvalue == f_position) return b->intvalue;
if (b->type == ExecFunction
&& b->intvalue == f_position
&& b->next
&& b->next->type == Int) return b->next->intvalue;
return 0;
default: return 0;
}
}
Production(Step)
if (LA==DOT) {
Consume(DOT);
a = New( GetContextNode );
/* a = New1( AxisSelf, New (IsNode)); */
} else if (LA==DOTDOT) {
Consume(DOTDOT);
a = New( GetParentNode );
/* a = New1( AxisParent, New (IsNode)); */
} else {
ast b;
int isFirst = 1;
a = Recurse(Basis);
if (LA==LBRACKET && !a) {
if (*errMsg == NULL) { ErrExpected("Step"); }
return NULL;
}
while (LA==LBRACKET) {
b = Recurse (Predicate);
if (!b) return a;
if (isFirst) {
a->intvalue = IsStepPredOptimizable (b);
DBG (fprintf (stderr, "step type %s, intvalue: %d\n", astType2str[a->type], a->intvalue);)
isFirst = 0;
}
Append( a, New1WithEvalSteps( Pred, b));
}
}
EndProduction
/*-----------------------------------------------------------------
| RelativeLocationPath production
|
\----------------------------------------------------------------*/
Production(RelativeLocationPath)
a = Recurse(Step);
if (!a) return NULL;
while ((LA==SLASH) || (LA==SLASHSLASH)) {
if (LA==SLASH) {
Consume(SLASH);
Append(a, Recurse(Step));
} else {
ast b;
Consume(SLASHSLASH);
b = Recurse(Step);
if (!b) return a;
if (b->type == AxisChild) {
b->type = AxisDescendant;
} else {
Append(a, New( AxisDescendantOrSelf ) );
}
Append(a, b );
}
}
EndProduction
/*-----------------------------------------------------------------
| AbsoluteLocationPath production
|
\----------------------------------------------------------------*/
Production(AbsoluteLocationPath)
if (LA==SLASH) {
ast b;
Consume(SLASH);
a = New(SelectRoot);
if ( (LA==AXISNAME)
||(LA==WCARDNAME)
||(LA==NSPREFIX)
||(LA==NSWC)
||(LA==NODE)
||(LA==TEXT)
||(LA==COMMENT)
||(LA==PINSTR)
||(LA==DOT)
||(LA==DOTDOT)
||(LA==ATTRIBUTE)
||(LA==ATTRIBUTEPREFIX)
) {
b = Recurse(RelativeLocationPath);
Append(a, b);
}
} else if (LA==SLASHSLASH) {
ast b;
Consume(SLASHSLASH);
a = New(SelectRoot);
b = Recurse(RelativeLocationPath);
if (!b) {
freeAst (a);
return NULL;
}
if (b->type == AxisChild) {
b->type = AxisDescendant;
} else {
Append(a, New( AxisDescendantOrSelf ) );
}
Append(a, b );
} else {
ErrExpected("/ or //");
}
EndProduction
/*-----------------------------------------------------------------
| XSLT StepPattern production
|
\----------------------------------------------------------------*/
static int usesPositionInformation ( ast a) {
while (a) {
if (a->type == ExecFunction
&& (a->intvalue == f_position
|| a->intvalue == f_last
|| a->intvalue == f_unknown)) {
return 1;
}
if (a->child) {
if (usesPositionInformation (a->child)) return 1;
}
a = a->next;
}
return 0;
}
/* Must be called with a != NULL */
static int checkStepPatternPredOptimizability ( ast a , long *max) {
ast b;
switch (a->type) {
case Literal:
case AxisAncestor:
case AxisAncestorOrSelf:
case AxisChild:
case AxisAttribute:
case AxisDescendant:
case AxisDescendantLit:
case AxisDescendantOrSelf:
case AxisDescendantOrSelfLit:
case AxisFollowing:
case AxisFollowingSibling:
case AxisNamespace:
case AxisParent:
case AxisPreceding:
case AxisPrecedingSibling:
case AxisSelf:
case IsNode:
case IsComment:
case IsText:
case IsPI:
case IsSpecificPI:
case IsElement:
case GetContextNode:
case GetParentNode:
break;
case And:
case Or:
if (usesPositionInformation(a->child)) return 0;
return 1;
case Less:
case LessOrEq:
b = a->child;
if (b
&& b->type == ExecFunction
&& b->intvalue == f_position
&& b->next
&& b->next->type == Int) {
if (a->type == Less) *max = b->next->intvalue;
else *max = b->next->intvalue + 1;
return 0;
}
if (usesPositionInformation(a->child)) return 0;
return 1;
case Greater:
case GreaterOrEq:
b = a->child;
if (b
&& b->type == Int
&& b->next
&& b->next->type == ExecFunction
&& b->next->intvalue == f_position) {
if (a->type == Greater) *max = b->intvalue;
else *max = b->intvalue + 1;
return 0;
}
if (usesPositionInformation(a->child)) return 0;
return 1;
case Equal:
b = a->child;
if (b
&& b->type == Int
&& b->next
&& b->next->type == ExecFunction
&& b->next->intvalue == f_position) {
*max = b->intvalue;
return 0;
}
if (b
&& b->type == ExecFunction
&& b->intvalue == f_position
&& b->next
&& b->next->type == Int) {
*max = b->next->intvalue;
return 0;
}
if (usesPositionInformation(a->child)) return 0;
return 1;
case NotEqual:
if (usesPositionInformation(a->child)) return 0;
return 1;
case Int:
*max = a->intvalue;
return 0;
case ExecFunction:
return !usesPositionInformation(a);
default:
return 0;
}
return 1;
}
/* Must be called with a != NULL */
static long IsStepPatternPredOptimizable ( ast a, long *max ) {
long f;
*max = 0;
f = checkStepPatternPredOptimizability(a, max);
DBG(
if (f) {
fprintf(stderr, "\nStepPattern Pred is optimizable:\n");
} else {
fprintf(stderr, "\nStepPattern Pred is not optimizable, max = %d:\n", *max);
}
printAst(0,a);
)
return f;
}
/*-----------------------------------------------------------------
| XSLT StepPattern production
|
\----------------------------------------------------------------*/
Production(StepPattern)
if (LA==AXISNAME) {
astType t;
Consume(AXISNAME);
if (IS_STR('c',"child")) { t = AxisChild;
} else if (IS_STR('a',"attribute")) { t = AxisAttribute;
} else {
ErrExpected("correct axis name (child/attribute)");
}
Consume(COLONCOLON);
a = New1( t, Recurse(NodeTest));
} else
if (LA==ATTRIBUTE) {
Consume(ATTRIBUTE);
a = New1( AxisAttribute, NewStr(IsAttr, STRVAL) );
} else
if (LA==ATTRIBUTEPREFIX) {
ast b, c;
Consume(ATTRIBUTEPREFIX);
a = New ( AxisAttribute);
b = NewStr (IsNSAttr, STRVAL);
AddChild (a, b);
Consume(ATTRIBUTE);
c = NewStr (IsAttr, STRVAL);
AddChild (b, c);
} else {
a = Recurse(NodeTest);
}
if (!a) {
if (*errMsg == NULL) { ErrExpected("StepPattern") };
return NULL;
}
{
ast b = NULL, c = NULL, aCopy = NULL;
int stepIsOptimizable = 1, isFirst = 1;
long max, savedmax;
while (LA==LBRACKET) {
b = Recurse (Predicate);
if (!b) return a;
if (stepIsOptimizable) {
if (!IsStepPatternPredOptimizable(b, &max))
stepIsOptimizable = 0;
}
if (isFirst) {
savedmax = max;
c = New1WithEvalSteps( Pred, b);
isFirst = 0;
} else {
Append (c, New1WithEvalSteps( Pred, b));
}
}
if (!isFirst) {
if (stepIsOptimizable) {
Append (a, New (FillWithCurrentNode));
} else {
/* copy the step before the Predicate */
aCopy = NEWCONS;
aCopy->type = a->type;
aCopy->next = NULL;
if (a->strvalue) aCopy->strvalue = tdomstrdup(a->strvalue);
else aCopy->strvalue = NULL;
aCopy->intvalue = a->intvalue;
aCopy->realvalue = a->realvalue;
aCopy->child = NULL;
if (a->child) {
ast aCopyChild = NEWCONS;
aCopyChild->type = a->child->type;
aCopyChild->next = NULL;
aCopyChild->child = NULL;
if (a->child->strvalue) {
aCopyChild->strvalue = tdomstrdup(a->child->strvalue);
} else {
aCopyChild->strvalue = NULL;
}
aCopyChild->intvalue = a->child->intvalue;
aCopyChild->realvalue = a->child->realvalue;
aCopy->child = aCopyChild;
}
b = New1( FillNodeList, aCopy);
b->intvalue = savedmax;
Append( a, b);
}
Append (a, c);
}
}
EndProduction
/*-----------------------------------------------------------------
| XSLT RelativePathPattern production
|
\----------------------------------------------------------------*/
Production(RelativePathPattern)
a = Recurse(StepPattern);
while ((LA==SLASH) || (LA==SLASHSLASH)) {
ast b;
if (LA==SLASH) {
Consume(SLASH);
b = Recurse(StepPattern);
if (b) {
Append(b, New(ToParent) );
Append(b, a);
a = b;
}
} else {
Consume(SLASHSLASH);
b = Recurse(StepPattern);
if (b) {
Append(b, New(ToAncestors) );
Append(b, a);
a = b;
}
}
}
EndProduction
/*-----------------------------------------------------------------
| XSLT IdKeyPattern production
|
\----------------------------------------------------------------*/
Production(IdKeyPattern)
Consume(FUNCTION);
if (strcmp(STRVAL, "id" )==0) {
ast b;
/* id */
a = NewStr( ExecIdKey, STRVAL);
a->intvalue = f_id;
Consume(LPAR);
Consume(LITERAL);
b = NewStr( Literal, STRVAL);
AddChild (a, b);
Consume(RPAR);
} else {
ast b;
/* key */
a = NewStr( ExecIdKey, STRVAL);
Consume(LPAR);
Consume(LITERAL);
b = NewStr( Literal, STRVAL);
AddChild (a, b);
Consume(COMMA);
Consume(LITERAL);
b = NewStr( Literal, STRVAL);
AddChild (a, b);
Consume(RPAR);
}
EndProduction
/*-----------------------------------------------------------------
| XSLT LocationPathPattern production
|
\----------------------------------------------------------------*/
Production(LocationPathPattern)
if (LA==SLASH) {
Consume(SLASH);
if (LA==EOS || LA==PIPE) {
a = New(IsRoot);
} else {
a = Recurse(RelativePathPattern);
if (a) {
Append( a, New(ToParent) );
Append( a, New(IsRoot) );
}
}
} else
if ((LA==FUNCTION)
&& ( (strcmp(tokens[*l].strvalue, "id" )==0)
||(strcmp(tokens[*l].strvalue, "key")==0) ) )
{
ast b;
b = Recurse(IdKeyPattern);
if (LA==SLASH) {
Consume(SLASH);
a = Recurse(RelativePathPattern);
if (a) { Append( a, New(ToParent) ); }
} else
if (LA==SLASHSLASH) {
Consume(SLASHSLASH);
a = Recurse(RelativePathPattern);
if (a) { Append( a, New(ToAncestors) ); }
}
if (!a) {
a = b;
} else {
Append( a, b);
}
} else {
if (LA==SLASHSLASH) {
Consume(SLASHSLASH);
a = Recurse(RelativePathPattern);
if (a) {
Append( a, New(ToAncestors) );
Append( a, New(IsRoot) );
}
} else {
a = Recurse(RelativePathPattern);
}
}
EndProduction
/*-----------------------------------------------------------------
| XSLT Pattern production
|
\----------------------------------------------------------------*/
Production(Pattern)
a = Recurse(LocationPathPattern);
while (LA==PIPE) {
Consume(PIPE);
a = New2( CombinePath, New1(EvalSteps, a),
New1(EvalSteps, Recurse(LocationPathPattern) ) );
}
EndProduction
/*----------------------------------------------------------------------------
| xpathFreeTokens
|
\---------------------------------------------------------------------------*/
static
int xpathFreeTokens (
XPathTokens tokens
)
{
int i;
for (i=0; tokens[i].token != EOS; i++) {
if (tokens[i].strvalue) {
FREE(tokens[i].strvalue);
}
}
FREE((char*)tokens);
return 0;
}
/*----------------------------------------------------------------------------
| xpathParsePostProcess
|
\---------------------------------------------------------------------------*/
static
int xpathParsePostProcess (
ast t,
xpathExprType type,
domNode *exprContext,
char **prefixMappings,
char **errMsg
)
{
const char *uri;
DBG(
fprintf(stderr, "xpathParsePostProcess start:\n");
printAst (0, t);
)
while (t) {
DBG(printAst (4, t);)
if (t->type == AxisNamespace) {
if (t->child->type == IsElement
&& t->child->strvalue[0] != '*'
&& t->child->intvalue == 0) {
uri = domLookupPrefixWithMappings (exprContext,
t->child->strvalue,
prefixMappings);
if (!uri) {
*errMsg = tdomstrdup ("Prefix doesn't resolve");
return 0;
}
FREE (t->child->strvalue);
t->child->strvalue = tdomstrdup (uri);
}
}
if (type != XPATH_EXPR) {
if (type != XPATH_KEY_USE_EXPR) {
/* 12.4: "It is an error to use the current function in a
pattern." */
if (t->type == ExecFunction
&& t->intvalue == f_unknown
&& (strcmp (t->strvalue, "current")==0)) {
*errMsg = tdomstrdup(
"The 'current' function is not allowed in Pattern."
);
return 0;
}
}
if (type == XPATH_KEY_MATCH_PATTERN
|| type == XPATH_KEY_USE_EXPR) {
/* 12.2: "It is an error for the value of either the use
attribute or the match attribute to contain a
VariableReference, or a call to the key function." */
if (t->type == ExecFunction
&& t->intvalue == f_unknown
&& (strcmp (t->strvalue, "key")==0)) {
*errMsg = tdomstrdup("The 'key' function is not allowed "
"in the use and match attribute "
"pattern of xsl:key.");
return 0;
}
if (t->type == GetVar || t->type == GetFQVar) {
*errMsg = tdomstrdup("Variable references are not allowed "
"in the use and match attribute of "
"xsl:key.");
return 0;
}
}
if (type == XPATH_TEMPMATCH_PATTERN) {
/* 5.3: "It is an error for the value of the match
attribute to contain a VariableReference." */
if (t->type == GetVar || t->type == GetFQVar) {
*errMsg = tdomstrdup("Variable references are not allowed "
"in the match attribute of "
"xsl:template."
);
return 0;
}
}
}
if (t->child) {
if (!xpathParsePostProcess (t->child, type, exprContext,
prefixMappings, errMsg)) return 0;
}
t = t->next;
}
return 1;
}
/*----------------------------------------------------------------------------
| xpathParse
|
\---------------------------------------------------------------------------*/
int xpathParse (
char *xpath,
domNode *exprContext,
xpathExprType type,
char **prefixMappings,
xpathParseVarCB *varParseCB,
ast *t,
char **errMsg
)
{
XPathTokens tokens;
int i, l, len, newlen, slen;
int useNamespaceAxis = 0;
char tmp[200];
DDBG(fprintf(stderr, "\nLex output following tokens for '%s':\n", xpath);)
*errMsg = NULL;
tokens = xpathLexer(xpath, exprContext, prefixMappings, &useNamespaceAxis,
varParseCB, errMsg);
if (*errMsg != NULL) {
if (tokens != NULL) xpathFreeTokens (tokens);
return XPATH_LEX_ERR;
}
DDBG(
for (i=0; tokens[i].token != EOS; i++) {
fprintf(stderr, "%3d %-12s %5ld %8.3g %5d %s\n",
i,
token2str[tokens[i].token-LPAR],
tokens[i].intvalue,
tokens[i].realvalue,
tokens[i].pos,
tokens[i].strvalue
);
}
)
l = 0;
*t = NULL;
if (type == XPATH_EXPR || type == XPATH_KEY_USE_EXPR) {
*t = OrExpr (&l, tokens, errMsg);
} else {
*t = Pattern (&l, tokens, errMsg);
}
if ((*errMsg == NULL) && (tokens[l].token != EOS)) {
*errMsg = tdomstrdup("Unexpected tokens (beyond end)!");
}
if (*errMsg == NULL && ((type != XPATH_EXPR) || useNamespaceAxis)) {
xpathParsePostProcess (*t, type, exprContext, prefixMappings, errMsg);
}
if (*errMsg) {
len = strlen(*errMsg);
newlen = strlen(xpath);
*errMsg = (char*)REALLOC(*errMsg, len+newlen+10);
memmove(*errMsg + len, " for '", 6);
memmove(*errMsg + len+6, xpath, newlen);
memmove(*errMsg + len+6+newlen, "' ", 3);
for (i=0; tokens[i].token != EOS; i++) {
sprintf(tmp, "%s\n%3s%3d %-12s %5ld %09.3g %5d ",
(i==0) ? "\n\nParsed symbols:" : "",
(i==l) ? "-->" : " ",
i,
token2str[tokens[i].token-LPAR],
tokens[i].intvalue,
tokens[i].realvalue,
tokens[i].pos
);
len = strlen(*errMsg);
newlen = strlen(tmp);
slen = 0;
if (tokens[i].strvalue) {
slen = strlen(tokens[i].strvalue);
}
*errMsg = (char*)REALLOC(*errMsg, len+newlen+slen+1);
memmove(*errMsg + len, tmp, newlen);
memmove(*errMsg + len + newlen, tokens[i].strvalue, slen);
(*errMsg)[len + newlen + slen] = '\0';
}
}
DBG(
if (type) {
fprintf(stderr, "\nPattern AST for '%s': \n", xpath);
printAst (0, *t);
} else {
fprintf(stderr, "AST (xpathParse):\n");
printAst (0, *t);
}
)
xpathFreeTokens (tokens);
if (*errMsg!=NULL) {
if (*t) freeAst (*t);
return XPATH_SYNTAX_ERR;
}
return 0;
} /* xpathParse */
/*----------------------------------------------------------------------------
| xpathNodeTest
|
\---------------------------------------------------------------------------*/
int xpathNodeTest (
domNode *node,
ast step
)
{
const char *localName, *nodeUri;
if (!(step->child)) return 1;
if (step->child->type == IsElement) {
if (node->nodeType == ELEMENT_NODE) {
if ((step->child->strvalue[0] == '*') &&
(step->child->strvalue[1] == '\0') &&
(node->ownerDocument->rootNode != node) &&
(step->child->intvalue == 0)) return 1;
if (node->namespace
&& (node->ownerDocument->namespaces[node->namespace-1]->prefix[0] != '\0'
|| node->ownerDocument->namespaces[node->namespace-1]->uri[0] != '\0')
) return 0;
return (strcmp(node->nodeName, step->child->strvalue)==0);
}
return 0;
} else
if (step->child->type == IsAttr) {
if (node->nodeType == ATTRIBUTE_NODE) {
if (node->nodeFlags & IS_NS_NODE) return 0;
if ( (step->child->strvalue[0] == '*')
&&(step->child->strvalue[1] == '\0')
) {
return 1;
}
return (strcmp( ((domAttrNode*)node)->nodeName,
step->child->strvalue)==0);
}
return 0;
} else
if (step->child->type == IsFQElement) {
if (node->nodeType != ELEMENT_NODE || node->namespace == 0) return 0;
nodeUri = domNamespaceURI (node);
if (!nodeUri) return 0;
if (strcmp (step->child->strvalue, nodeUri) != 0) return 0;
localName = domGetLocalName (node->nodeName);
if (strcmp (step->child->child->strvalue, localName)==0) return 1;
return 0;
} else
if (step->child->type == IsNSElement) {
nodeUri = domNamespaceURI (node);
if (!nodeUri) return 0;
if (nodeUri && (strcmp (step->child->strvalue, nodeUri)==0)) return 1;
return 0;
} else
if (step->child->type == IsNSAttr) {
if (node->nodeType != ATTRIBUTE_NODE
|| node->nodeFlags & IS_NS_NODE) return 0;
nodeUri = domNamespaceURI (node);
if (!nodeUri) return 0;
if (strcmp (step->child->strvalue, nodeUri) != 0) return 0;
if (strcmp (step->child->child->strvalue, "*")==0) return 1;
localName = domGetLocalName (((domAttrNode *) node)->nodeName);
if (strcmp (step->child->child->strvalue, localName)==0) return 1;
return 0;
} else
if (step->child->type == IsNode) {
DBG(fprintf(stderr, "nodeTest: nodeType=%d \n", node->nodeType);)
return 1;
} else
if (step->child->type == IsText) {
DBG(fprintf(stderr, "nodeTest: nodeType=%d == %d?? \n", node->nodeType, TEXT_NODE);)
return (node->nodeType == TEXT_NODE);
} else
if (step->child->type == IsPI) {
return (node->nodeType == PROCESSING_INSTRUCTION_NODE);
} else
if (step->child->type == IsSpecificPI) {
return (node->nodeType == PROCESSING_INSTRUCTION_NODE
&& strncmp (((domProcessingInstructionNode*)node)->targetValue,
step->child->strvalue,
((domProcessingInstructionNode*)node)->targetLength)
== 0);
} else
if (step->child->type == IsComment) {
return (node->nodeType == COMMENT_NODE);
}
return 1;
}
/*----------------------------------------------------------------------------
| xpathFuncBoolean
|
\---------------------------------------------------------------------------*/
int xpathFuncBoolean (
xpathResultSet *rs
)
{
switch (rs->type) {
case BoolResult: return ( rs->intvalue ? 1 : 0 );
case IntResult: return ( rs->intvalue ? 1 : 0 );
case RealResult: return ((rs->realvalue != 0.0 ) && !IS_NAN (rs->realvalue));
case StringResult: return ( rs->string_len > 0 );
case xNodeSetResult: return ( rs->nr_nodes > 0 );
case InfResult:
case NInfResult: return 1;
/* NaNResult and EmptyResult are 'false' */
default: return 0;
}
}
static double xpathStringToNumber (
char *str,
int *NaN
)
{
int dotseen = 0;
double d;
char *pc, *tailptr;
/*
Just to use strtod() isn't sufficient for a few reasons:
- strtod() accepts a leading - or +, but XPath allows only a
leading -
- strtod() accepts the string representation of a hexadecimal
number, but XPath does not
- strtod() accepts an optional exponent but XPath does not
- strtod() accepts leading whitespace including \f and \v, but
XPath doesn't allow this characters. Since this two
characters are not legal XML characters, they can not be part
of a DOM tree and therefor there isn't a problem with XPath
expressions on DOM trees or in XSLT. But on Tcl level it's
possible, to feed that characters literal into the XPath
engine.
*/
*NaN = 0;
pc = str;
while (*pc && IS_XML_WHITESPACE(*pc)) pc++;
if (!*pc) goto returnNaN;
if (*pc == '-') {
pc++;
if (!*pc) goto returnNaN;
} else if (*pc == '.') {
dotseen = 1;
pc++;
if (!*pc) goto returnNaN;
}
if (!isdigit((unsigned char)*pc)) goto returnNaN;
while (*pc) {
if (isdigit((unsigned char)*pc)) {
pc++;
continue;
}
if (*pc == '.' && !dotseen) {
dotseen = 1;
pc++;
continue;
}
break;
}
while (*pc && IS_XML_WHITESPACE(*pc)) pc++;
if (*pc) goto returnNaN;
/* If we are here str is a number string, as XPath expects it. Now
we just use strtod(), to get the number */
d = strtod (str, &tailptr);
if (d == 0.0 && tailptr == str) goto returnNaN;
else if (IS_NAN(d)) goto returnNaN;
return d;
returnNaN:
*NaN = 2;
return strtod ("nan", &tailptr);
}
/*----------------------------------------------------------------------------
| xpathFuncNumber
|
\---------------------------------------------------------------------------*/
double xpathFuncNumber (
xpathResultSet *rs,
int *NaN
)
{
double d;
char *pc, *tailptr;
*NaN = 0;
switch (rs->type) {
case BoolResult: return (rs->intvalue? 1.0 : 0.0);
case IntResult: return rs->intvalue;
case RealResult:
if (IS_NAN(rs->realvalue)) *NaN = 2;
else if (IS_INF(rs->realvalue)!=0) *NaN = IS_INF(rs->realvalue);
return rs->realvalue;
case NaNResult:
*NaN = 2;
return 0.0;
case InfResult:
*NaN = 1;
#ifdef INFINITY
return INFINITY;
#else
# ifdef DBL_MAX
return DBL_MAX;
# else
/* well, what? */
return 0.0
# endif
#endif
case NInfResult:
*NaN = -1;
#ifdef INFINITY
return -INFINITY;
#else
# ifdef DBL_MAX
return -DBL_MAX;
# else
/* well, what? */
return 0.0
# endif
#endif
case StringResult:
return xpathStringToNumber(rs->string, NaN);
case xNodeSetResult:
pc = xpathFuncString(rs);
d = xpathStringToNumber(pc, NaN);
FREE(pc);
return d;
default:
DBG(fprintf(stderr, "funcNumber: default: 0.0\n");)
d = strtod ("nan", &tailptr);
*NaN = 2;
return d;
}
}
/*----------------------------------------------------------------------------
| xpathGetStringValue
|
\---------------------------------------------------------------------------*/
char * xpathGetStringValueForElement (
domNode *node,
int *len
)
{
char *pc, *t;
int l;
domNode *child;
if (node->nodeType == ELEMENT_NODE) {
DBG(fprintf(stderr,"GetTextValue: tag='%s' \n", node->nodeName);)
pc = MALLOC(1); *pc = '\0'; *len = 0;
child = node->firstChild;
while (child) {
t = xpathGetStringValueForElement(child, &l);
pc = (char*)REALLOC(pc, 1 + *len + l);
memmove(pc + *len, t, l );
*len += l;
pc[*len] = '\0';
FREE((char*)t);
child = child->nextSibling;
}
} else
if (node->nodeType == TEXT_NODE) {
*len = ((domTextNode*)node)->valueLength;
pc = (char*)MALLOC(1+*len);
memmove(pc, ((domTextNode*)node)->nodeValue, *len);
pc[*len] = '\0';
DBG(fprintf(stderr,"GetTextValue: text='%s' \n", pc);)
} else {
pc = tdomstrdup ("");
*len = 0;
}
return pc;
}
char * xpathGetStringValue (
domNode *node,
int *len
)
{
char *pc, *t;
int l;
domNode *child;
domAttrNode *attr;
if (node->nodeType == ELEMENT_NODE) {
DBG(fprintf(stderr,"GetTextValue: tag='%s' \n", node->nodeName);)
pc = MALLOC(1); *pc = '\0'; *len = 0;
child = node->firstChild;
while (child) {
t = xpathGetStringValueForElement(child, &l);
pc = (char*)REALLOC(pc, 1 + *len + l);
memmove(pc + *len, t, l );
*len += l;
pc[*len] = '\0';
FREE((char*)t);
child = child->nextSibling;
}
} else
if ((node->nodeType == TEXT_NODE) ||
(node->nodeType == CDATA_SECTION_NODE) ||
(node->nodeType == COMMENT_NODE)
) {
*len = ((domTextNode*)node)->valueLength;
pc = (char*)MALLOC(1+*len);
memmove(pc, ((domTextNode*)node)->nodeValue, *len);
pc[*len] = '\0';
DBG(fprintf(stderr,"GetTextValue: text='%s' \n", pc);)
} else
if (node->nodeType == PROCESSING_INSTRUCTION_NODE) {
*len = ((domProcessingInstructionNode*)node)->dataLength;
pc = (char*)MALLOC(1+*len);
memmove(pc, ((domProcessingInstructionNode*)node)->dataValue, *len);
pc[*len] = '\0';
} else
if (node->nodeType == ATTRIBUTE_NODE) {
attr = (domAttrNode*)node;
DBG(fprintf(stderr,"GetTextValue: attr='%s' \n", attr->nodeName);)
pc = MALLOC(attr->valueLength +1);
memmove(pc, attr->nodeValue, attr->valueLength);
*(pc + attr->valueLength) = '\0';
*len = attr->valueLength;
}
else {
pc = tdomstrdup ("");
*len = 0;
}
return pc;
}
/*----------------------------------------------------------------------------
| xpathFuncString
|
\---------------------------------------------------------------------------*/
char * xpathFuncString (
xpathResultSet *rs
)
{
char tmp[80], *pc;
int len;
switch (rs->type) {
case BoolResult:
if (rs->intvalue) return (tdomstrdup("true"));
else return (tdomstrdup("false"));
case IntResult:
sprintf(tmp, "%ld", rs->intvalue);
return (tdomstrdup(tmp));
case RealResult:
if (IS_NAN (rs->realvalue)) return tdomstrdup ("NaN");
else if (IS_INF (rs->realvalue)) {
if (IS_INF (rs->realvalue) == 1) return tdomstrdup ("Infinity");
else return tdomstrdup ("-Infinity");
}
sprintf(tmp, "%g", rs->realvalue);
/* strip trailing 0 and . */
len = strlen(tmp);
for (; (len > 0) && (tmp[len-1] == '0'); len--) tmp[len-1] = '\0';
if ((len > 0) && (tmp[len-1] == '.')) tmp[len-1] = '\0';
return (tdomstrdup(tmp));
case NaNResult:
return tdomstrdup ("NaN");
case InfResult:
return tdomstrdup ("Infinity");
case NInfResult:
return tdomstrdup ("-Infinity");
case StringResult:
pc = MALLOC(rs->string_len +1);
memmove(pc, rs->string, rs->string_len);
*(pc + rs->string_len) = '\0';
return pc;
case xNodeSetResult:
if (rs->nr_nodes == 0) {
pc = tdomstrdup ("");
} else {
pc = xpathGetStringValue (rs->nodes[0], &len);
}
return pc;
default:
return (tdomstrdup (""));
}
}
/*----------------------------------------------------------------------------
| xpathFuncStringForNode
|
\---------------------------------------------------------------------------*/
char * xpathFuncStringForNode (
domNode *node
)
{
int len;
return xpathGetStringValue (node, &len);
}
/*----------------------------------------------------------------------------
| xpathArity
|
\---------------------------------------------------------------------------*/
static int xpathArity (
ast step
)
{
int parms = 0;
step = step->child;
while (step) {
parms++;
step = step->next;
}
return parms;
}
/*----------------------------------------------------------------------------
| xpathArityCheck
|
\---------------------------------------------------------------------------*/
static int xpathArityCheck (
ast step,
int arity,
char **errMsg
)
{
int parms = 0;
step = step->child;
while (step) {
parms++;
step = step->next;
}
if (arity!=parms) {
*errMsg = tdomstrdup("wrong number of parameters!");
return 1;
}
return 0;
}
#define XPATH_ARITYCHECK(s,a,m) if (xpathArityCheck(s,a,m)) return XPATH_EVAL_ERR
/*----------------------------------------------------------------------------
| xpathRound
|
\---------------------------------------------------------------------------*/
int xpathRound (double r) {
if (r < 0.0) {
return (int)floor (r + 0.5);
} else {
return (int)(r + 0.5);
}
}
/*----------------------------------------------------------------------------
| idSplitAndAdd
|
\---------------------------------------------------------------------------*/
static void
idSplitAndAdd (
char *idStr,
Tcl_HashTable *ids,
xpathResultSet *result
)
{
int pwhite;
char *pfrom, *pto;
Tcl_HashEntry *entryPtr;
domNode *node;
pwhite = 0;
pfrom = pto = idStr;
while (*pto) {
switch (*pto) {
case ' ' : case '\n': case '\r': case '\t':
if (pwhite) {
pto++;
continue;
}
*pto = '\0';
entryPtr = Tcl_FindHashEntry (ids, pfrom);
if (entryPtr) {
node = (domNode*) Tcl_GetHashValue (entryPtr);
/* Don't report nodes out of the fragment list */
if (node->parentNode != NULL ||
(node == node->ownerDocument->documentElement)) {
rsAddNode (result, node);
}
}
pwhite = 1;
pto++;
continue;
default:
if (pwhite) {
pfrom = pto;
pwhite = 0;
}
pto++;
}
}
if (!pwhite) {
entryPtr = Tcl_FindHashEntry (ids, pfrom);
if (entryPtr) {
node = (domNode*) Tcl_GetHashValue (entryPtr);
/* Don't report nodes out of the fragment list */
if (node->parentNode != NULL ||
(node == node->ownerDocument->documentElement)) {
rsAddNode (result, node);
}
}
}
}
/*----------------------------------------------------------------------------
| xpathEvalFunction
|
\---------------------------------------------------------------------------*/
static int
xpathEvalFunction (
ast step,
domNode *ctxNode,
domNode *exprContext,
int position,
xpathResultSet *nodeList,
xpathCBs *cbs,
xpathResultSet *result,
int *docOrder,
char **errMsg
)
{
xpathResultSet leftResult, rightResult, replaceResult;
int i, rc, pwhite, len, NaN;
char *replaceStr, *pfrom, *pto, tmp[80], tmp1[80];
domNode *node;
domAttrNode *attr;
double leftReal;
ast nextStep;
int argc, savedDocOrder, from;
xpathResultSets *args;
xpathResultSet *arg;
Tcl_HashTable *ids;
Tcl_HashEntry *entryPtr;
int left = 0;
double dRight = 0.0;
char *leftStr = NULL, *rightStr = NULL;
const char *str;
Tcl_DString dStr;
int found, j;
int lenstr, fromlen, utfCharLen;
char utfBuf[TCL_UTF_MAX];
Tcl_DString tstr, tfrom, tto, tresult;
Tcl_UniChar *ufStr, *upfrom, unichar;
switch (step->intvalue) {
case f_position:
XPATH_ARITYCHECK(step,0,errMsg);
if (*docOrder) {
rsSetInt (result, position+1);
} else {
rsSetInt (result, nodeList->nr_nodes - position);
}
break;
case f_last:
XPATH_ARITYCHECK(step,0,errMsg);
rsSetInt (result, nodeList->nr_nodes);
break;
case f_number:
xpathRSInit (&leftResult);
if (xpathArity(step) == 0) {
/* no parameter, the context node is the nodeset to
* operate with
*/
rsAddNodeFast( &leftResult, ctxNode);
} else {
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
}
leftReal = xpathFuncNumber(&leftResult, &NaN);
if (NaN) {
if (NaN == 2) rsSetNaN (result);
else if (NaN == 1) rsSetInf (result);
else rsSetNInf (result);
} else {
rsSetReal (result, leftReal);
}
xpathRSFree( &leftResult );
break;
case f_floor:
case f_ceiling:
case f_round:
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
leftReal = xpathFuncNumber(&leftResult, &NaN);
if (NaN) {
if (NaN == 2) rsSetNaN (result);
else if (NaN == 1) rsSetInf (result);
else rsSetNInf (result);
} else {
if (step->intvalue == f_floor) leftReal = floor(leftReal);
else if (step->intvalue == f_ceiling) leftReal = ceil(leftReal);
else leftReal =
xpathRound(leftReal);
rsSetReal (result, leftReal);
}
xpathRSFree( &leftResult );
break;
case f_boolean:
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
left = xpathFuncBoolean(&leftResult);
rsSetBool (result, left);
xpathRSFree( &leftResult );
break;
case f_string:
case f_normalizeSpace:
case f_stringLength:
xpathRSInit (&leftResult);
if (step->child == NULL) {
/* no parameter, the context node is the nodeset to
* operate with
*/
rsAddNodeFast( &leftResult, ctxNode);
} else {
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
DBG(fprintf(stderr, "normalize-space: \n");
rsPrint(&leftResult);
)
}
leftStr = xpathFuncString (&leftResult );
xpathRSFree( &leftResult );
DBG(fprintf(stderr, "leftStr='%s'\n", leftStr);)
if (step->intvalue == f_string)
rsSetString (result, leftStr);
else if (step->intvalue == f_stringLength) {
pto = leftStr;
len = 0;
while (*pto) {
len++;
i = UTF8_CHAR_LEN (*pto);
if (!i) {
FREE (leftStr);
*errMsg = tdomstrdup("Can only handle UTF-8 chars up "
"to 4 bytes length");
return XPATH_I18N_ERR;
}
pto += i;
}
rsSetInt (result, len);
}
else {
pwhite = 1;
pfrom = pto = leftStr;
while (*pfrom) {
switch (*pfrom) {
case ' ' : case '\n': case '\r': case '\t':
if (!pwhite) {
*pto++ = ' ';
pwhite = 1;
}
break;
default:
*pto++ = *pfrom;
pwhite = 0;
break;
}
pfrom++;
}
if ((pto > leftStr) && (*(pto-1) == ' ')) {
pto--; /* cut last empty space */
}
*pto = '\0';
rsSetString (result, leftStr);
}
FREE(leftStr);
break;
case f_not:
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree (&leftResult);
return rc;
}
left = xpathFuncBoolean(&leftResult);
xpathRSFree (&leftResult);
rsSetBool (result, !left);
break;
case f_true:
XPATH_ARITYCHECK(step,0,errMsg);
rsSetBool (result, 1);
break;
case f_false:
XPATH_ARITYCHECK(step,0,errMsg);
rsSetBool (result, 0);
break;
case f_id:
XPATH_ARITYCHECK(step,1,errMsg);
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
ids = ((domAttrNode*)ctxNode)->parentNode->ownerDocument->ids;
} else {
ids = ctxNode->ownerDocument->ids;
}
if (!ids) {
break;
}
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
DBG(fprintf(stderr, "id: \n");
rsPrint(&leftResult);
)
if (leftResult.type == EmptyResult) {
xpathRSFree (&leftResult);
return XPATH_OK;
}
if (leftResult.type == xNodeSetResult) {
for (i=0; i < leftResult.nr_nodes; i++) {
leftStr = xpathFuncStringForNode (leftResult.nodes[i]);
idSplitAndAdd (leftStr, ids, result);
FREE(leftStr);
}
} else {
leftStr = xpathFuncString (&leftResult);
idSplitAndAdd (leftStr, ids, result);
FREE(leftStr);
}
sortByDocOrder (result);
xpathRSFree (&leftResult);
break;
case f_sum:
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
if (leftResult.type != xNodeSetResult) {
if (leftResult.type == EmptyResult) {
rsSetInt (result, 0);
return XPATH_OK;
} else {
xpathRSFree( &leftResult );
*errMsg = tdomstrdup("sum() requires a node set!");
xpathRSFree( &leftResult );
return XPATH_EVAL_ERR;
}
}
xpathRSInit(&rightResult);
rightResult.nr_nodes = 1;
rightResult.type = leftResult.type;
leftReal = 0.0;
for (i=0; i<leftResult.nr_nodes; i++) {
rightResult.nodes = &(leftResult.nodes[i]);
DBG(fprintf(stderr, "leftReal = %f \n", leftReal);)
leftReal += xpathFuncNumber(&rightResult, &NaN);
if (NaN) {
rsSetNaN (result);
xpathRSFree( &leftResult );
return XPATH_OK;
}
DBG(fprintf(stderr, "leftReal = %f \n", leftReal);)
}
rsSetReal (result, leftReal);
xpathRSFree( &leftResult );
break;
case f_lang:
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree (&leftResult);
return rc;
}
leftStr = xpathFuncString (&leftResult);
if (ctxNode->nodeType != ELEMENT_NODE) {
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
node = ((domAttrNode*)ctxNode)->parentNode;
} else {
node = ctxNode->parentNode;
}
} else {
node = ctxNode;
}
while (node) {
attr = node->firstAttr;
while (attr) {
if (strcmp (attr->nodeName, "xml:lang")!=0) {
attr = attr->nextSibling;
continue;
}
tcldom_tolower (attr->nodeValue, tmp, 80);
tcldom_tolower (leftStr, tmp1, 80);
if (strcmp (tmp, tmp1)==0) {
rsSetBool (result, 1);
FREE(leftStr);
xpathRSFree (&leftResult);
return XPATH_OK;
} else {
pfrom = tmp;
i = 0;
while (*pfrom && i < 79) {
if (*pfrom == '-') {
*pfrom = '\0';
break;
}
pfrom++;
i++;
}
if (strcmp (tmp, tmp1)==0) {
rsSetBool (result, 1);
FREE(leftStr);
xpathRSFree (&leftResult);
return XPATH_OK;
} else {
rsSetBool (result, 0);
FREE(leftStr);
xpathRSFree (&leftResult);
return XPATH_OK;
}
}
}
node = node->parentNode;
}
rsSetBool (result, 0);
FREE(leftStr);
xpathRSFree (&leftResult);
break;
case f_startsWith:
case f_contains:
case f_substringBefore:
case f_substringAfter:
XPATH_ARITYCHECK(step,2,errMsg);
xpathRSInit (&leftResult);
xpathRSInit (&rightResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
rc = xpathEvalStep( step->child->next, ctxNode, exprContext, position,
nodeList, cbs, &rightResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG(fprintf(stderr, "\nsubstring-* left,right:\n");
rsPrint(&leftResult);
rsPrint(&rightResult);
)
leftStr = xpathFuncString( &leftResult );
rightStr = xpathFuncString( &rightResult );
DBG(fprintf(stderr, "substring-* '%s' '%s' \n", leftStr, rightStr);)
if (step->intvalue == f_contains) {
if (strstr(leftStr, rightStr) != NULL) {
rsSetBool (result, 1);
} else {
rsSetBool (result, 0);
}
} else
if (step->intvalue == f_substringBefore) {
pfrom = strstr(leftStr, rightStr);
if (pfrom != NULL) {
DBG(fprintf(stderr, "substring-before '%s' '%s' : ", leftStr, rightStr);)
*pfrom = '\0';
DBG(fprintf(stderr, "'%s' \n", leftStr);)
rsSetString (result, leftStr);
} else {
rsSetString (result, "");
}
} else
if (step->intvalue == f_substringAfter) {
pfrom = strstr(leftStr, rightStr);
if (pfrom != NULL) {
rsSetString (result, pfrom + strlen (rightStr));
} else {
rsSetString (result, "");
}
} else {
/* starts-with */
i = strlen(rightStr);
if(strncmp(leftStr, rightStr, i)==0) {
rsSetBool (result, 1);
} else {
rsSetBool (result, 0);
}
}
xpathRSFree (&leftResult);
xpathRSFree (&rightResult);
FREE(rightStr);
FREE(leftStr);
break;
case f_concat:
if (xpathArity(step) < 2) {
*errMsg = tdomstrdup("wrong number of parameters!");
return XPATH_EVAL_ERR;
}
nextStep = step->child;
pto = MALLOC(1);
*pto = '\0';
len = 0;
while (nextStep) {
xpathRSInit (&leftResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( nextStep, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
FREE (pto);
return rc;
}
*docOrder = savedDocOrder;
leftStr = xpathFuncString( &leftResult );
pto = (char*)REALLOC(pto, 1+len+strlen(leftStr));
memmove(pto + len, leftStr, strlen(leftStr));
len += strlen(leftStr);
*(pto + len) = '\0';
xpathRSFree( &leftResult );
FREE(leftStr);
nextStep = nextStep->next;
}
rsSetString (result, pto);
FREE(pto);
break;
case f_substring:
i = xpathArity(step);
if (i != 2 && i != 3) {
*errMsg = tdomstrdup("wrong number of parameters!");
return XPATH_EVAL_ERR;
}
xpathRSInit (&leftResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
xpathRSInit (&rightResult);
rc = xpathEvalStep( step->child->next, ctxNode, exprContext, position,
nodeList, cbs, &rightResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
leftStr = xpathFuncString( &leftResult );
xpathRSFree (&leftResult);
from = xpathRound(xpathFuncNumber(&rightResult, &NaN))-1;
xpathRSFree( &rightResult );
if (NaN) {
FREE(leftStr);
rsSetString (result, "");
return XPATH_OK;
}
if (step->child->next->next) {
xpathRSInit (&rightResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( step->child->next->next, ctxNode, exprContext,
position, nodeList, cbs, &rightResult,
docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
dRight = xpathFuncNumber (&rightResult, &NaN);
len = xpathRound(dRight);
xpathRSFree (&rightResult);
if (NaN) {
if (NaN == 1) {
len = INT_MAX;
} else {
FREE(leftStr);
rsSetString (result, "");
return XPATH_OK;
}
}
xpathRSFree (&rightResult);
if (from < 0) {
len = len + from;
if (len <= 0) {
FREE(leftStr);
rsSetString (result, "");
return XPATH_OK;
}
from = 0;
}
} else {
if (from < 0) from = 0;
len = INT_MAX;
}
pfrom = leftStr;
while (*pfrom && (from > 0)) {
i = UTF8_CHAR_LEN (*pfrom);
if (!i) {
FREE (leftStr);
*errMsg = tdomstrdup("Can only handle UTF-8 chars up "
"to 4 bytes length");
return XPATH_I18N_ERR;
}
pfrom += i;
from--;
}
if (len < INT_MAX) {
pto = pfrom;
while (*pto && (len > 0)) {
i = UTF8_CHAR_LEN (*pto);
if (!i) {
FREE (leftStr);
*errMsg = tdomstrdup("Can only handle UTF-8 chars up "
"to 4 bytes length");
return XPATH_I18N_ERR;
}
pto += i;
len--;
}
*pto = '\0';
}
rsSetString (result, pfrom);
FREE(leftStr);
break;
case f_translate:
XPATH_ARITYCHECK(step,3,errMsg);
xpathRSInit (&leftResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
xpathRSInit (&rightResult);
rc = xpathEvalStep( step->child->next, ctxNode, exprContext, position,
nodeList, cbs, &rightResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
xpathRSInit (&replaceResult);
rc = xpathEvalStep( step->child->next->next, ctxNode, exprContext,
position, nodeList, cbs, &replaceResult, docOrder,
errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
leftStr = xpathFuncString( &leftResult );
rightStr = xpathFuncString( &rightResult );
replaceStr = xpathFuncString( &replaceResult );
Tcl_DStringInit (&tstr);
Tcl_DStringInit (&tfrom);
Tcl_DStringInit (&tto);
Tcl_DStringInit (&tresult);
Tcl_UtfToUniCharDString (leftStr, -1, &tstr);
Tcl_UtfToUniCharDString (rightStr, -1, &tfrom);
Tcl_UtfToUniCharDString (replaceStr, -1, &tto);
lenstr = Tcl_DStringLength (&tstr) / sizeof (Tcl_UniChar);
fromlen = Tcl_DStringLength (&tfrom) / sizeof (Tcl_UniChar);
len = Tcl_DStringLength (&tto) / sizeof (Tcl_UniChar);
upfrom = (Tcl_UniChar *)Tcl_DStringValue (&tstr);
for (i = 0; i < lenstr; i++) {
found = 0;
ufStr = (Tcl_UniChar *)Tcl_DStringValue (&tfrom);
for (j = 0; j < fromlen; j++) {
if (*ufStr == *upfrom) {
found = 1;
break;
}
ufStr++;
}
if (found) {
if (j < len) {
unichar = Tcl_UniCharAtIndex (replaceStr, j);
utfCharLen = Tcl_UniCharToUtf (unichar, utfBuf);
Tcl_DStringAppend (&tresult, utfBuf, utfCharLen);
}
} else {
utfCharLen = Tcl_UniCharToUtf (*upfrom, utfBuf);
Tcl_DStringAppend (&tresult, utfBuf, utfCharLen);
}
upfrom++;
}
rsSetString (result, Tcl_DStringValue (&tresult));
Tcl_DStringFree (&tstr);
Tcl_DStringFree (&tfrom);
Tcl_DStringFree (&tto);
Tcl_DStringFree (&tresult);
xpathRSFree( &replaceResult );
xpathRSFree( &rightResult );
xpathRSFree( &leftResult );
FREE(leftStr); FREE(rightStr); FREE(replaceStr);
break;
case f_count:
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
if (leftResult.type == EmptyResult) {
rsSetInt (result, 0);
return XPATH_OK;
}
if (leftResult.type != xNodeSetResult) {
*errMsg = tdomstrdup("count() requires a node set!");
xpathRSFree( &leftResult );
return XPATH_EVAL_ERR;
}
rsSetInt (result, leftResult.nr_nodes);
xpathRSFree (&leftResult);
break;
case f_unparsedEntityUri:
XPATH_ARITYCHECK(step,1,errMsg);
if (!ctxNode->ownerDocument->unparsedEntities) {
break;
}
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
leftStr = xpathFuncString (&leftResult);
entryPtr = Tcl_FindHashEntry (ctxNode->ownerDocument->unparsedEntities,
leftStr);
if (entryPtr) {
rsSetString (result, (char *)Tcl_GetHashValue (entryPtr));
} else {
rsSetString (result, "");
}
FREE(leftStr);
xpathRSFree (&leftResult);
break;
case f_localName:
case f_name:
case f_namespaceUri:
case f_generateId:
xpathRSInit (&leftResult);
if (step->child == NULL) {
/* no parameter, the context node is the nodeset to
* operate with
*/
rsAddNodeFast( &leftResult, ctxNode);
} else {
XPATH_ARITYCHECK(step,1,errMsg);
xpathRSInit (&leftResult);
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
if (rc) {
xpathRSFree( &leftResult );
return rc;
}
}
if (leftResult.type == EmptyResult) {
rsSetString (result, "");
return XPATH_OK;
}
if (step->intvalue == f_generateId) {
if (leftResult.type != xNodeSetResult) {
*errMsg = tdomstrdup("generate-id() requires a nodeset or no argument!");
xpathRSFree (&leftResult);
return XPATH_EVAL_ERR;
}
if (leftResult.nodes[0]->nodeType == ATTRIBUTE_NODE) {
node = ((domAttrNode*)leftResult.nodes[0])->parentNode;
i = 0;
attr = node->firstAttr;
while (attr) {
if ((domNode*)attr == leftResult.nodes[0]) break;
attr = attr->nextSibling;
i++;
}
sprintf(tmp,"id%p-%d", (void *)node, i);
} else {
sprintf(tmp,"id%p", (void *)leftResult.nodes[0]);
}
rsSetString (result, tmp);
} else
if (step->intvalue == f_namespaceUri) {
if (leftResult.type != xNodeSetResult) {
*errMsg = tdomstrdup("namespace-uri() requires a node set!");
xpathRSFree( &leftResult );
return XPATH_EVAL_ERR;
}
if ( (leftResult.nr_nodes <= 0)
|| ( leftResult.nodes[0]->nodeType != ELEMENT_NODE
&& leftResult.nodes[0]->nodeType != ATTRIBUTE_NODE )
)
{
rsSetString (result, "");
} else {
rsSetString (result, domNamespaceURI(leftResult.nodes[0]));
}
} else
if (step->intvalue == f_localName) {
if (leftResult.type != xNodeSetResult) {
*errMsg = tdomstrdup("local-name() requires a node set!");
xpathRSFree( &leftResult );
return XPATH_EVAL_ERR;
}
if (leftResult.nodes[0]->nodeType == ELEMENT_NODE) {
if (leftResult.nodes[0] ==
leftResult.nodes[0]->ownerDocument->rootNode) {
rsSetString (result, "");
} else {
rsSetString (result, domGetLocalName(leftResult.nodes[0]->nodeName));
}
} else
if (leftResult.nodes[0]->nodeType == ATTRIBUTE_NODE) {
str = domGetLocalName(((domAttrNode*)leftResult.nodes[0])->nodeName);
if (str[0] == 'x' && strcmp(str, "xmlns")==0) {
rsSetString (result, "");
} else {
rsSetString (result, str);
}
} else
if (leftResult.nodes[0]->nodeType == PROCESSING_INSTRUCTION_NODE) {
if (((domProcessingInstructionNode*)leftResult.nodes[0])->targetLength > 79) {
memmove(tmp, ((domProcessingInstructionNode*)leftResult.nodes[0])->targetValue, 79);
tmp[79]= '\0';
} else {
memmove(tmp, ((domProcessingInstructionNode*)leftResult.nodes[0])->targetValue,
((domProcessingInstructionNode*)leftResult.nodes[0])->targetLength);
tmp[((domProcessingInstructionNode*)leftResult.nodes[0])->targetLength] = '\0';
}
rsSetString (result, tmp);
} else {
rsSetString (result, "");
}
} else
if (step->intvalue == f_name) {
if ( leftResult.type != xNodeSetResult ) {
*errMsg = tdomstrdup("name() requires a node set!");
xpathRSFree( &leftResult );
return XPATH_EVAL_ERR;
}
if (leftResult.nodes[0]->nodeType == ELEMENT_NODE) {
if (leftResult.nodes[0] ==
leftResult.nodes[0]->ownerDocument->rootNode) {
rsSetString (result, "");
} else {
rsSetString (result, leftResult.nodes[0]->nodeName);
}
} else
if (leftResult.nodes[0]->nodeType == ATTRIBUTE_NODE) {
if (leftResult.nodes[0]->nodeFlags & IS_NS_NODE) {
if (((domAttrNode *)leftResult.nodes[0])->nodeName[5] == '\0') {
rsSetString (result, "");
} else {
rsSetString (result, &((domAttrNode*)leftResult.nodes[0])->nodeName[6]);
}
} else {
rsSetString (result, ((domAttrNode*)leftResult.nodes[0])->nodeName );
}
} else
if (leftResult.nodes[0]->nodeType == PROCESSING_INSTRUCTION_NODE) {
if (((domProcessingInstructionNode*)leftResult.nodes[0])->targetLength > 79) {
memmove(tmp, ((domProcessingInstructionNode*)leftResult.nodes[0])->targetValue, 79);
tmp[79]= '\0';
} else {
memmove(tmp, ((domProcessingInstructionNode*)leftResult.nodes[0])->targetValue,
((domProcessingInstructionNode*)leftResult.nodes[0])->targetLength);
tmp[((domProcessingInstructionNode*)leftResult.nodes[0])->targetLength] = '\0';
}
rsSetString (result, tmp);
} else {
rsSetString (result, "");
}
}
xpathRSFree( &leftResult );
break;
case f_fqfunction:
Tcl_DStringInit (&dStr);
if (cbs->funcCB == NULL) {
Tcl_DStringAppend (&dStr, "Unknown function ", -1);
}
Tcl_DStringAppend (&dStr, step->strvalue, -1);
Tcl_DStringAppend (&dStr, "::", 2);
nextStep = step->child;
Tcl_DStringAppend (&dStr, nextStep->strvalue, -1);
if (cbs->funcCB == NULL) {
*errMsg = tdomstrdup (Tcl_DStringValue (&dStr));
Tcl_DStringFree (&dStr);
return XPATH_EVAL_ERR;
}
/* fall throu */
default:
if (cbs->funcCB == NULL) {
if (strlen(step->strvalue)>50) *(step->strvalue + 50) = '\0';
sprintf(tmp, "Unknown function '%s'!", step->strvalue);
*errMsg = tdomstrdup(tmp);
return XPATH_EVAL_ERR;
}
/* count number of arguments (to be able to allocate later) */
argc = 0;
if (step->intvalue == f_fqfunction) {
nextStep = step->child->next;
} else {
nextStep = step->child;
}
while (nextStep) {
argc++;
nextStep = nextStep->next;
}
args = (xpathResultSets*)MALLOC((argc+1) * sizeof(xpathResultSets));
args[0] = NULL;
argc = 0;
if (step->intvalue == f_fqfunction) {
nextStep = step->child->next;
} else {
nextStep = step->child;
}
savedDocOrder = *docOrder;
while (nextStep) {
arg = (xpathResultSet*)MALLOC(sizeof(xpathResultSet));
args[argc++] = arg;
args[argc] = NULL;
xpathRSInit (arg);
rc = xpathEvalStep( nextStep, ctxNode, exprContext, position,
nodeList, cbs, arg, docOrder, errMsg);
if (rc) goto unknownfunccleanup;
*docOrder = savedDocOrder;
nextStep = nextStep->next;
}
if (step->intvalue == f_fqfunction) {
rc = (cbs->funcCB) (cbs->funcClientData, Tcl_DStringValue (&dStr),
ctxNode, position, nodeList, exprContext,
argc, args, result, errMsg);
} else {
rc = (cbs->funcCB) (cbs->funcClientData, step->strvalue,
ctxNode, position, nodeList, exprContext,
argc, args, result, errMsg);
}
unknownfunccleanup:
argc = 0;
while ( args[argc] != NULL) {
xpathRSFree( args[argc] );
FREE((char*)args[argc++]);
}
FREE((char*)args);
if (step->intvalue == f_fqfunction) {
Tcl_DStringFree (&dStr);
}
return rc;
}
return XPATH_OK;
}
/*----------------------------------------------------------------------------
| xpathEvalStep
|
\---------------------------------------------------------------------------*/
static int xpathEvalStep (
ast step,
domNode *ctxNode,
domNode *exprContext,
int position,
xpathResultSet *nodeList,
xpathCBs *cbs,
xpathResultSet *result,
int *docOrder,
char **errMsg
)
{
xpathResultSet leftResult, rightResult;
xpathResultSet *pleftResult, *prightResult, tResult;
int i, j, k, rc, res, NaN, NaN1, switchResult, count = 0;
domNode *node, *child, *startingNode, *ancestor;
domAttrNode *attr;
int savedDocOrder, predLimit;
int left = 0, right = 0, useFastAdd;
double dLeft = 0.0, dRight = 0.0, dTmp;
char *leftStr = NULL, *rightStr = NULL;
astType savedAstType;
if (result->type == EmptyResult) useFastAdd = 1;
else useFastAdd = 0;
switch (step->type) {
case AxisChild:
DBG(fprintf(stderr, "AxisChild ctxNode->nodeType = %d \n",
ctxNode->nodeType);)
*docOrder = 1;
if (ctxNode->nodeType != ELEMENT_NODE) return XPATH_OK;
DBG(fprintf(stderr, "AxisChild: scanning \n");)
child = ctxNode->firstChild;
if (step->intvalue) {
while (child && (count < step->intvalue)) {
DBG(fprintf(stderr, "AxisChild: child '%s' domNode%p \n",
child->nodeName, child);)
if (xpathNodeTest(child, step)) {
DBG(fprintf(stderr,
"AxisChild: after node taking child '%s' domNode%p \n",
child->nodeName, child);)
checkRsAddNode( result, child);
count++;
}
child = child->nextSibling;
}
} else {
while (child) {
DBG(fprintf(stderr, "AxisChild: child '%s' domNode%p \n",
child->nodeName, child);)
if (xpathNodeTest(child, step)) {
DBG(fprintf(stderr,
"AxisChild: after node taking child '%s' domNode%p \n",
child->nodeName, child);)
checkRsAddNode( result, child);
}
child = child->nextSibling;
}
}
DBG( fprintf(stderr,"AxisChild result:\n");
rsPrint(result);
)
break;
case SlashSlash:
xpathRSInit (&tResult);
node = ctxNode->firstChild;
while (node) {
if (node->nodeType == ELEMENT_NODE) {
rc = xpathEvalStep (step, node, exprContext, position,
nodeList, cbs, result, docOrder, errMsg);
if (rc) {
xpathRSFree (&tResult);
return rc;
}
}
if (xpathNodeTest(node, step)) {
rsAddNodeFast( &tResult, node);
}
node = node->nextSibling;
}
rc = xpathEvalPredicate (step->next, exprContext, result, &tResult,
cbs, docOrder, errMsg);
xpathRSFree (&tResult);
CHECK_RC;
break;
case AxisDescendant:
case AxisDescendantOrSelf:
if (step->next && step->next->type == Pred) {
*docOrder = 1;
if (ctxNode->nodeType == ATTRIBUTE_NODE
&& step->type == AxisDescendantOrSelf) {
if (xpathNodeTest(ctxNode, step)) {
checkRsAddNode( result, ctxNode);
}
break;
}
if (ctxNode->nodeType != ELEMENT_NODE) return XPATH_OK;
if (step->type == AxisDescendantOrSelf) {
if (xpathNodeTest(ctxNode, step)) {
xpathRSInit (&tResult);
rsAddNodeFast( &tResult, ctxNode);
rc = xpathEvalPredicate (step->next, exprContext, result,
&tResult, cbs, docOrder, errMsg);
xpathRSFree (&tResult);
CHECK_RC;
}
}
savedAstType = step->type;
step->type = SlashSlash;
rc = xpathEvalStep (step, ctxNode, exprContext, position,
nodeList, cbs, result, docOrder, errMsg);
step->type = savedAstType;
CHECK_RC;
break;
}
/* without following Pred step, // is the same as
AxisDescendantOrSelf, fall throu */
case AxisDescendantLit:
case AxisDescendantOrSelfLit:
*docOrder = 1;
if (step->intvalue
&& (step->type == AxisDescendantLit
|| step->type == AxisDescendantOrSelfLit)) predLimit = 1;
else predLimit = 0;
if (ctxNode->nodeType == ATTRIBUTE_NODE
&& (step->type == AxisDescendantOrSelf
|| step->type == AxisDescendantOrSelfLit)) {
if (xpathNodeTest(ctxNode, step)) {
checkRsAddNode( result, ctxNode);
}
break;
}
if (ctxNode->nodeType != ELEMENT_NODE) return XPATH_OK;
if (step->type == AxisDescendantOrSelf
|| step->type == AxisDescendantOrSelfLit) {
if (xpathNodeTest(ctxNode, step)) {
checkRsAddNode( result, ctxNode);
if (predLimit) {
count++;
}
}
}
startingNode = ctxNode;
node = ctxNode->firstChild;
while (node && node != startingNode) {
if (xpathNodeTest(node, step)) {
checkRsAddNode( result, node);
if (predLimit) {
count++;
if (count >= step->intvalue) break;
}
}
if ((node->nodeType == ELEMENT_NODE) && (node->firstChild)) {
node = node->firstChild;
continue;
}
if (node->nextSibling) {
node = node->nextSibling;
continue;
}
while ( node->parentNode &&
(node->parentNode != startingNode) &&
(node->parentNode->nextSibling == NULL) ) {
node = node->parentNode;
}
if ((node != startingNode) &&
(node->parentNode) &&
(node->parentNode != startingNode)
) {
node = node->parentNode->nextSibling;
} else {
break;
}
}
break;
case AxisSelf:
*docOrder = 1;
DBG(fprintf(stderr, "AxisSelf :: \n");)
if (xpathNodeTest(ctxNode, step)) {
rsAddNode( result, ctxNode);
}
break;
case GetContextNode:
rsAddNode( result, ctxNode);
break;
case AxisAttribute:
*docOrder = 1;
DBG(fprintf(stderr, "AxisAttribute %s \n", step->child->strvalue);)
if (ctxNode->nodeType != ELEMENT_NODE) return XPATH_OK;
if (step->child->type == IsElement) {
step->child->type = IsAttr;
}
if (step->child->type == IsAttr) {
if (strcmp(step->child->strvalue, "*")==0) {
attr = ctxNode->firstAttr;
while (attr) {
if (!(attr->nodeFlags & IS_NS_NODE)) {
checkRsAddNode (result, (domNode *)attr);
}
attr = attr->nextSibling;
}
} else {
attr = ctxNode->firstAttr;
while (attr && (attr->nodeFlags & IS_NS_NODE))
attr = attr->nextSibling;
while (attr) {
if (xpathNodeTest( (domNode*)attr, step)) {
checkRsAddNode (result, (domNode *)attr);
}
attr = attr->nextSibling;
}
}
} else
if (step->child->type == IsNSAttr) {
attr = ctxNode->firstAttr;
while (attr && (attr->nodeFlags & IS_NS_NODE))
attr = attr->nextSibling;
while (attr) {
if (xpathNodeTest ( (domNode*)attr, step)) {
checkRsAddNode (result, (domNode *)attr);
}
attr = attr->nextSibling;
}
}
break;
case AxisParent:
*docOrder = 1;
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
if (xpathNodeTest(((domAttrNode *)ctxNode)->parentNode, step)) {
rsAddNode(result,((domAttrNode *)ctxNode)->parentNode);
}
} else {
if (ctxNode->parentNode) {
if (xpathNodeTest(ctxNode->parentNode, step)) {
rsAddNode(result,ctxNode->parentNode);
}
} else {
if (ctxNode->ownerDocument->rootNode != ctxNode
&& xpathNodeTest (ctxNode->ownerDocument->rootNode,
step)) {
rsAddNode (result, ctxNode->ownerDocument->rootNode);
}
}
}
break;
case GetParentNode:
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
rsAddNode(result,((domAttrNode*)ctxNode)->parentNode);
} else {
if (ctxNode->parentNode) {
rsAddNode(result,ctxNode->parentNode);
} else {
if (ctxNode != ctxNode->ownerDocument->rootNode) {
rsAddNode (result, ctxNode->ownerDocument->rootNode);
}
}
}
break;
case AxisAncestor:
case AxisAncestorOrSelf:
*docOrder = 0;
xpathRSInit (&tResult);
if (step->type == AxisAncestorOrSelf) {
if (xpathNodeTest(ctxNode, step)) {
rsAddNodeFast(&tResult, ctxNode);
}
}
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
ctxNode = ((domAttrNode *)ctxNode)->parentNode;
if (xpathNodeTest(ctxNode, step)) {
rsAddNodeFast(&tResult, ctxNode);
}
}
startingNode = ctxNode;
while (ctxNode->parentNode) {
ctxNode = ctxNode->parentNode;
if (xpathNodeTest(ctxNode, step)) {
rsAddNodeFast(&tResult, ctxNode);
}
}
if (startingNode != ctxNode->ownerDocument->rootNode) {
if (xpathNodeTest (ctxNode->ownerDocument->rootNode, step)) {
rsAddNodeFast (&tResult, ctxNode->ownerDocument->rootNode);
}
}
for (i = tResult.nr_nodes - 1; i >= 0; i--) {
checkRsAddNode (result, tResult.nodes[i]);
}
xpathRSFree (&tResult);
break;
case AxisFollowingSibling:
*docOrder = 1;
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
return XPATH_OK;
}
if (step->intvalue) {
while (ctxNode->nextSibling && (count < step->intvalue)) {
ctxNode = ctxNode->nextSibling;
if (xpathNodeTest(ctxNode, step)) {
checkRsAddNode(result, ctxNode);
count++;
}
}
} else {
while (ctxNode->nextSibling) {
ctxNode = ctxNode->nextSibling;
if (xpathNodeTest(ctxNode, step)) {
checkRsAddNode(result, ctxNode);
}
}
}
break;
case AxisPrecedingSibling:
*docOrder = 0;
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
return XPATH_OK;
}
if (step->intvalue) {
node = ctxNode->previousSibling;
xpathRSInit (&tResult);
while (node && (count < step->intvalue)) {
if (xpathNodeTest (node, step)) {
rsAddNodeFast (&tResult, node);
count++;
}
node = node->previousSibling;
}
for (i = tResult.nr_nodes -1; i >= 0; i--) {
checkRsAddNode (result, tResult.nodes[i]);
}
xpathRSFree (&tResult);
} else {
startingNode = ctxNode;
if (startingNode->parentNode) {
node = (startingNode->parentNode)->firstChild;
} else {
if (ctxNode == ctxNode->ownerDocument->rootNode)
return XPATH_OK;
node = startingNode->ownerDocument->rootNode->firstChild;
}
while (node && (node != startingNode)) {
if (xpathNodeTest(node, step)) {
checkRsAddNode(result, node);
}
node = node->nextSibling;
}
}
break;
case AxisFollowing:
*docOrder = 1;
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
node = ((domAttrNode *)ctxNode)->parentNode->firstChild;
} else {
node = ctxNode;
if (node->nextSibling) {
node = node->nextSibling;
} else {
while (node->parentNode) {
node = node->parentNode;
if (node->nextSibling) break;
}
if (!node->nextSibling) return XPATH_OK;
else node = node->nextSibling;
}
}
while (1) {
if (xpathNodeTest (node, step)) {
checkRsAddNode (result, node);
if (step->intvalue) {
count++;
if (count >= step->intvalue) break;
}
}
if (node->nodeType == ELEMENT_NODE &&
node->firstChild) {
node = node->firstChild;
} else
if (node->nextSibling) {
node = node->nextSibling;
} else {
while (node->parentNode) {
node = node->parentNode;
if (node->nextSibling) break;
}
if (!node->nextSibling) break;
node = node->nextSibling;
}
}
break;
case AxisPreceding:
*docOrder = 0;
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
ancestor = node = ((domAttrNode *)ctxNode)->parentNode;
} else {
ancestor = node = ctxNode;
}
i = 0;
while (node->parentNode) {
ancestor = node;
node = node->parentNode;
i++;
}
startingNode = node->firstChild;
for (; i > 0; i--) {
if (!startingNode)
continue;
if (startingNode->nodeType == ELEMENT_NODE) {
node = startingNode->firstChild;
} else {
node = NULL;
}
while (startingNode != ancestor) {
if (xpathNodeTest(startingNode, step)) {
checkRsAddNode(result, startingNode);
}
while ((node) && (node != startingNode)) {
if (xpathNodeTest(node, step)) {
checkRsAddNode(result, node);
}
if ((node->nodeType == ELEMENT_NODE) &&
(node->firstChild)) {
node = node->firstChild;
continue;
}
if (node->nextSibling) {
node = node->nextSibling;
continue;
}
while ((node->parentNode != startingNode) &&
(node->parentNode->nextSibling == NULL)) {
node = node->parentNode;
}
if (node->parentNode != startingNode) {
node = node->parentNode->nextSibling;
} else {
break;
}
}
startingNode = startingNode->nextSibling;
if (startingNode->nodeType == ELEMENT_NODE) {
node = startingNode->firstChild;
} else {
node = NULL;
}
}
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
node = ((domAttrNode *)ctxNode)->parentNode;
} else {
node = ctxNode;
}
for (j = 0; j < i - 1 ; j++) {
ancestor = node;
node = node->parentNode;
}
if (node->nodeType == ELEMENT_NODE) {
startingNode = node->firstChild;
} else {
startingNode = NULL;
}
}
break;
case AxisNamespace:
*docOrder = 1;
/* XPath rec 2.2: "the namespace axis contains the namespace
* nodes of the context node; the axis will be empty unless
* the context node is an element */
if (ctxNode->nodeType != ELEMENT_NODE) {
return XPATH_OK;
}
node = ctxNode;
while (node) {
attr = node->firstAttr;
while (attr && (attr->nodeFlags & IS_NS_NODE)) {
if (step->child->type == IsElement) {
if ((step->child->strvalue[0] != '*')) {
if (strcmp(attr->nodeValue,
step->child->strvalue)!=0) {
attr = attr->nextSibling;
continue;
}
}
}
rc = 0;
for (i = 0; i < result->nr_nodes; i++) {
if (strcmp (attr->nodeName, ((domAttrNode*)result->nodes[i])->nodeName)==0) {
rc = 1; break;
}
}
if (rc) {attr = attr->nextSibling; continue;}
checkRsAddNode (result, (domNode *)attr);
attr = attr->nextSibling;
}
if (node == node->ownerDocument->documentElement) {
if (ctxNode != ctxNode->ownerDocument->rootNode) {
node = ctxNode->ownerDocument->rootNode;
} else {
node = NULL;
}
} else {
node = node->parentNode;
}
}
break;
case GetFQVar:
case GetVar:
if (cbs->varCB) {
if (step->type == GetFQVar) {
leftStr = step->child->strvalue;
rightStr = step->strvalue;
} else {
leftStr = step->strvalue;
rightStr = NULL;
}
rc = (cbs->varCB)(cbs->varClientData, leftStr, rightStr, result,
errMsg);
CHECK_RC;
}
break;
case Literal:
rsSetString (result, step->strvalue);
break;
case Int:
rsSetInt (result, step->intvalue);
break;
case Real:
rsSetReal (result, step->realvalue);
break;
case Add:
case Subtract:
case Mult:
case Div:
case Mod:
xpathRSInit (&leftResult);
xpathRSInit (&rightResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
CHECK_RC;
DBG( fprintf(stderr,"left:\n");
rsPrint(&leftResult);
)
*docOrder = savedDocOrder;
rc = xpathEvalStep( step->child->next, ctxNode, exprContext, position,
nodeList, cbs, &rightResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG( fprintf(stderr,"right:\n");
rsPrint(&rightResult);
)
if ((&leftResult)->type == RealResult) {
dLeft = (&leftResult)->realvalue;
NaN = 0;
if (IS_NAN(dLeft)) NaN = 2;
else if (IS_INF(dLeft)!=0) NaN = IS_INF(dLeft);
} else {
dLeft = xpathFuncNumber(&leftResult, &NaN);
}
if ((&rightResult)->type == RealResult) {
dRight = (&rightResult)->realvalue;
NaN1 = 0;
if (IS_NAN(dRight)) NaN1= 2;
else if (IS_INF(dRight) !=0) NaN1 = IS_INF(dRight);
} else {
dRight = xpathFuncNumber(&rightResult, &NaN1);
}
if (NaN || NaN1) {
if ((NaN == 2) || (NaN1 == 2)) {
rsSetNaN (result);
} else {
switch (step->type) {
case Subtract:
NaN1 = -1 * NaN1;
/* fall throu */
case Add:
if (NaN == NaN1) {
if (NaN == 1) rsSetInf (result);
else rsSetNInf (result);
} else if ((rc = NaN + NaN1) != 0) {
if (rc == 1) rsSetInf (result);
else rsSetNInf (result);
} else {
rsSetNaN (result);
}
break;
case Mult:
if ((dLeft == 0.0) || (dRight == 0.0)) rsSetNaN (result);
else if (NaN && NaN1) {
rc = NaN * NaN1;
if (rc == 1) rsSetInf (result);
else rsSetNInf (result);
} else {
if (NaN) dTmp = NaN * dRight;
else dTmp = NaN1 * dLeft;
if (dTmp > 0.0) rsSetInf (result);
else rsSetNInf (result);
}
break;
case Div:
if (NaN && NaN1) rsSetNaN (result);
else if (NaN1) rsSetInt (result, 0);
else {
if (dRight == 0.0) dTmp = NaN;
else dTmp = NaN * dRight;
if (dTmp > 0.0) rsSetInf (result);
else rsSetNInf (result);
}
break;
case Mod:
rsSetNaN (result);
break;
default: break;
}
}
xpathRSFree (&rightResult);
xpathRSFree (&leftResult);
return XPATH_OK;
}
switch (step->type) {
case Add: rsSetReal (result, dLeft + dRight); break;
case Subtract: rsSetReal (result, dLeft - dRight); break;
case Mult: rsSetReal (result, dLeft * dRight); break;
case Div:
if (dRight == 0.0) {
if (dLeft == 0.0) {
rsSetNaN (result);
} else {
if (dLeft > 0) {
rsSetInf (result);
} else {
rsSetNInf (result);
}
}
} else {
rsSetReal (result, dLeft / dRight);
}
break;
case Mod:
if ((int)dRight == 0) {
rsSetNaN (result);
} else {
if (dRight < LONG_MIN - 0.1
|| dRight > LONG_MAX + 0.1
|| dLeft < LONG_MIN - 0.1
|| dLeft > LONG_MAX + 0.1) {
rsSetNaN (result);
} else {
rsSetInt (result, ((long)dLeft) % ((long)dRight));
}
}
break;
default: break;
}
xpathRSFree (&rightResult);
xpathRSFree (&leftResult);
return XPATH_OK;
case CombineSets:
xpathRSInit (&leftResult);
xpathRSInit (&rightResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep (step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
CHECK_RC;
DBG( fprintf(stderr,"left:\n");
rsPrint(&leftResult);
)
*docOrder = savedDocOrder;
rc = xpathEvalStep( step->child->next, ctxNode, exprContext, position,
nodeList, cbs, &rightResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG( fprintf(stderr,"right:\n");
rsPrint(&rightResult);
)
if (((leftResult.type != xNodeSetResult)
&& (leftResult.type != EmptyResult))
||
((rightResult.type != xNodeSetResult)
&& (rightResult.type != EmptyResult)))
{
*errMsg = tdomstrdup("| requires node sets!");
xpathRSFree (&rightResult);
xpathRSFree (&leftResult);
return XPATH_EVAL_ERR;
}
if (leftResult.type == EmptyResult) {
rsCopy (result, &rightResult);
goto combineSetCleanup;
}
if (rightResult.type == EmptyResult) {
rsCopy (result, &leftResult);
goto combineSetCleanup;
}
*docOrder = 1;
j = k = 0;
for (i=0; i<(leftResult.nr_nodes+rightResult.nr_nodes); i++) {
if (leftResult.nodes[j] == rightResult.nodes[k]) {
rsAddNodeFast (result, leftResult.nodes[j]);
j++; k++;
if (j == leftResult.nr_nodes) break;
if (k == rightResult.nr_nodes) break;
i++;
continue;
}
if (domPrecedes (leftResult.nodes[j], rightResult.nodes[k])) {
rsAddNodeFast (result, leftResult.nodes[j]);
j++;
if (j == leftResult.nr_nodes) break;
} else {
rsAddNodeFast (result, rightResult.nodes[k]);
k++;
if (k == rightResult.nr_nodes) break;
}
}
if (j < leftResult.nr_nodes) {
for (i=j; i< leftResult.nr_nodes; i++) {
rsAddNodeFast ( result, leftResult.nodes[i]);
}
} else {
for (i=k; i< rightResult.nr_nodes; i++) {
rsAddNodeFast ( result, rightResult.nodes[i]);
}
}
combineSetCleanup:
xpathRSFree (&rightResult);
xpathRSFree (&leftResult);
return XPATH_OK;
case And:
case Or:
case Equal:
case NotEqual:
xpathRSInit (&leftResult);
xpathRSInit (&rightResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG( fprintf(stderr,"left:\n");
rsPrint(&leftResult);
)
/*----------------------------------------------
| short circuit evalution for AND/OR
\---------------------------------------------*/
if (step->type == And) {
left = xpathFuncBoolean(&leftResult);
if (!left) {
/* left result is false, so AND result is also false */
rsSetBool (result, left);
xpathRSFree (&leftResult);
return XPATH_OK;
}
}
if (step->type == Or) {
left = xpathFuncBoolean(&leftResult);
if (left) {
/* left result is true, so OR result is also true */
rsSetBool (result, left);
xpathRSFree (&leftResult);
return XPATH_OK;
}
}
rc = xpathEvalStep( step->child->next, ctxNode, exprContext, position,
nodeList, cbs, &rightResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG( fprintf(stderr,"right:\n");
rsPrint(&rightResult);
)
res = 0;
if ((step->type == And) || (step->type == Or)) {
right = xpathFuncBoolean(&rightResult);
if (step->type == And) res = (left && right);
if (step->type == Or) res = (left || right);
rsSetBool (result, res);
xpathRSFree (&rightResult);
xpathRSFree (&leftResult);
return XPATH_OK;
}
if ( leftResult.type == xNodeSetResult
|| rightResult.type == xNodeSetResult) {
if (leftResult.type == xNodeSetResult) {
pleftResult = &leftResult;
prightResult = &rightResult;
} else {
pleftResult = &rightResult;
prightResult = &leftResult;
}
switch (prightResult->type) {
case EmptyResult:
res = 0;
break;
case xNodeSetResult:
JDBG( fprintf(stderr,"\nleft+right result:\n");
rsPrint(pleftResult);
rsPrint(prightResult);
)
for (i=0; i < pleftResult->nr_nodes; i++) {
leftStr = xpathFuncStringForNode (pleftResult->nodes[i]);
for (j=0; j < prightResult->nr_nodes; j++) {
rightStr = xpathFuncStringForNode (prightResult->nodes[j]);
JDBG(fprintf(stderr, "leftStr='%s' rightStr='%s'\n", leftStr, rightStr);)
res = strcmp (leftStr, rightStr);
if (step->type == Equal) res = (res==0);
else if (step->type == NotEqual) res = (res!=0);
FREE(rightStr);
if (res) break;
}
FREE(leftStr);
if (res) break;
}
break;
case BoolResult:
if (step->type == Equal) res = (prightResult->intvalue != 0);
else res = (prightResult->intvalue == 0);
break;
case IntResult:
case RealResult:
dRight = xpathFuncNumber (prightResult, &NaN);
if (NaN) {
/* The real value of a node could never be inf/-inf */
/* And NaN is always != NaN (and != everything else) */
if (step->type == Equal) res = 0;
else res = 1;
break;
}
for (i=0; i < pleftResult->nr_nodes; i++) {
leftStr = xpathFuncStringForNode (pleftResult->nodes[i]);
dLeft = xpathStringToNumber (leftStr, &NaN);
FREE(leftStr);
if (NaN) continue;
if (step->type == Equal) res = (dLeft == dRight);
else res = (dLeft != dRight);
if (res) break;
}
break;
case NaNResult:
case InfResult:
case NInfResult:
res = 0;
break;
case StringResult:
rightStr = xpathFuncString (prightResult);
for (i=0; i < pleftResult->nr_nodes; i++) {
leftStr = xpathFuncStringForNode (pleftResult->nodes[i]);
res = strcmp (leftStr, rightStr);
if (step->type == Equal) res = (res == 0);
else res = (res != 0);
FREE(leftStr);
if (res) break;
}
FREE(rightStr);
break;
}
} else
if (leftResult.type == BoolResult || rightResult.type == BoolResult) {
left = xpathFuncBoolean (&leftResult);
right = xpathFuncBoolean (&rightResult);
if (step->type == Equal) res = (left == right);
else res = (left != right);
} else
if (
leftResult.type == IntResult
|| leftResult.type == RealResult
|| rightResult.type == IntResult
|| rightResult.type == RealResult
|| rightResult.type == NaNResult
|| rightResult.type == InfResult
|| rightResult.type == NInfResult
|| leftResult.type == NaNResult
|| leftResult.type == InfResult
|| leftResult.type == NInfResult
) {
if ( leftResult.type == EmptyResult
|| rightResult.type == EmptyResult) {
res = 0;
} else {
dLeft = xpathFuncNumber (&leftResult, &NaN);
dRight = xpathFuncNumber (&rightResult, &NaN1);
if (NaN || NaN1) {
if (NaN == NaN1) {
if (NaN != 2) {
if (step->type == Equal) res = 1;
} else {
if (step->type == NotEqual) res = 1;
}
} else {
if (step->type == NotEqual) res = 1;
}
} else {
if (step->type == Equal) res = (dLeft == dRight);
else res = (dLeft != dRight);
}
}
} else {
if ( leftResult.type == EmptyResult
|| rightResult.type == EmptyResult) {
res = 0;
} else {
leftStr = xpathFuncString (&leftResult);
rightStr = xpathFuncString (&rightResult);
res = strcmp (leftStr, rightStr);
if (step->type == Equal) res = (res == 0);
else res = (res != 0);
FREE(leftStr);
FREE(rightStr);
}
}
rsSetBool (result, res);
xpathRSFree (&rightResult);
xpathRSFree (&leftResult);
return XPATH_OK;
case Less:
case LessOrEq:
case Greater:
case GreaterOrEq:
xpathRSInit (&leftResult);
xpathRSInit (&rightResult);
savedDocOrder = *docOrder;
rc = xpathEvalStep( step->child, ctxNode, exprContext, position,
nodeList, cbs, &leftResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG( fprintf(stderr,"left:\n");
rsPrint(&leftResult);
)
rc = xpathEvalStep( step->child->next, ctxNode, exprContext, position,
nodeList, cbs, &rightResult, docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG( fprintf(stderr,"right:\n");
rsPrint(&rightResult);
)
res = 0;
if ( leftResult.type == xNodeSetResult
|| rightResult.type == xNodeSetResult) {
if (leftResult.type == xNodeSetResult) {
pleftResult = &leftResult;
prightResult = &rightResult;
switchResult = 0;
} else {
pleftResult = &rightResult;
prightResult = &leftResult;
switchResult = 1;
}
switch (prightResult->type) {
case EmptyResult:
res = 0;
break;
case xNodeSetResult:
JDBG( fprintf(stderr,"\nleft+right result:\n");
rsPrint(pleftResult);
rsPrint(prightResult);
)
for (i=0; i < pleftResult->nr_nodes; i++) {
leftStr = xpathFuncStringForNode (pleftResult->nodes[i]);
dLeft = xpathStringToNumber (leftStr, &NaN);
FREE(leftStr);
if (NaN) continue;
for (j=0; j < prightResult->nr_nodes; j++) {
rightStr = xpathFuncStringForNode (prightResult->nodes[j]);
dRight = xpathStringToNumber (rightStr, &NaN);
FREE(rightStr);
if (NaN) continue;
if (switchResult) {
dTmp = dLeft;
dLeft = dRight;
dRight = dTmp;
}
if (step->type == Less) res = (dLeft < dRight);
else if (step->type == LessOrEq) res = (dLeft <= dRight);
else if (step->type == Greater) res = (dLeft > dRight);
else res = (dLeft >= dRight);
if (res) break;
}
if (res) break;
}
break;
case BoolResult:
/* pleftResult is a non-empty nodeset, therefor: */
dLeft = 1.0;
dRight = xpathFuncNumber (prightResult, &NaN);
if (NaN) break;
if (step->type == Less) res = (dLeft < dRight);
else if (step->type == LessOrEq) res = (dLeft <= dRight);
else if (step->type == Greater) res = (dLeft > dRight);
else res = (dLeft >= dRight);
if (switchResult) {
res = !res;
}
break;
case NaNResult:
break;
case InfResult:
case NInfResult:
case IntResult:
case RealResult:
case StringResult:
dRight = xpathFuncNumber (prightResult, &NaN);
if (NaN) {
if (NaN == 2) break;
#ifdef DBL_MAX
if (NaN == 1) dRight = DBL_MAX;
else dRight = -DBL_MAX;
#endif
}
for (i=0; i < pleftResult->nr_nodes; i++) {
leftStr = xpathFuncStringForNode (pleftResult->nodes[i]);
dLeft = xpathStringToNumber (leftStr, &NaN);
FREE (leftStr);
if (NaN) continue;
if (switchResult) {
dTmp = dLeft;
dLeft = dRight;
dRight = dTmp;
}
if (step->type == Less) res = (dLeft < dRight);
else if (step->type == LessOrEq) res = (dLeft <= dRight);
else if (step->type == Greater) res = (dLeft > dRight);
else res = (dLeft >= dRight);
if (res) break;
}
break;
}
} else {
if ( leftResult.type == EmptyResult
|| rightResult.type == EmptyResult) {
res = 0;
} else {
dLeft = xpathFuncNumber (&leftResult, &NaN);
if (NaN) {
if (NaN == 2) goto compareFinish;
#ifdef DBL_MAX
if (NaN == 1) dLeft = DBL_MAX;
else dLeft = -DBL_MAX;
#endif
}
dRight = xpathFuncNumber (&rightResult, &NaN);
if (NaN) {
if (NaN == 2) goto compareFinish;
#ifdef DBL_MAX
if (NaN == 1) dRight = DBL_MAX;
else dRight = -DBL_MAX;
#endif
}
if (step->type == Less) res = (dLeft < dRight);
else if (step->type == LessOrEq) res = (dLeft <= dRight);
else if (step->type == Greater) res = (dLeft > dRight);
else res = (dLeft >= dRight);
}
}
compareFinish:
rsSetBool (result, res);
xpathRSFree (&rightResult);
xpathRSFree (&leftResult);
return XPATH_OK;
case SelectRoot:
if (ctxNode->nodeType == ATTRIBUTE_NODE) {
node = ((domAttrNode *)ctxNode)->parentNode;
checkRsAddNode(result, node->ownerDocument->rootNode);
} else {
checkRsAddNode(result, ctxNode->ownerDocument->rootNode);
}
break;
case UnaryMinus:
xpathRSInit (&leftResult);
rc = xpathEvalSteps (step->child, nodeList, ctxNode, exprContext,
position, docOrder,cbs, &leftResult, errMsg);
CHECK_RC;
dLeft = xpathFuncNumber (&leftResult, &NaN);
if (NaN) {
if (NaN == 2) rsSetNaN (result);
else if (NaN == 1) rsSetNInf (result);
else rsSetInf (result);
} else rsSetReal (result , -1 * dLeft);
xpathRSFree (&leftResult);
break;
case EvalSteps:
rc = xpathEvalSteps (step->child, nodeList, ctxNode, exprContext,
position, docOrder,cbs, &leftResult, errMsg);
CHECK_RC;
if ((result->type != EmptyResult) && (leftResult.type != result->type)) {
DBG( fprintf (stderr, "EvalSteps:\nresult:\n");
rsPrint (result);
fprintf (stderr, "leftResult:\n");
rsPrint (&leftResult); )
*errMsg = tdomstrdup("can not merge different result types!");
return XPATH_EVAL_ERR;
}
switch (leftResult.type) {
case xNodeSetResult:
for (i=0; i<leftResult.nr_nodes; i++) {
DBG(fprintf(stderr, "EvalSteps: adding %d \n", i);)
checkRsAddNode (result, leftResult.nodes[i]);
}
break;
case BoolResult: rsSetBool(result, leftResult.intvalue);
break;
case IntResult: rsSetInt(result, leftResult.intvalue);
break;
case RealResult: rsSetReal(result, leftResult.realvalue);
break;
case StringResult: rsSetString (result, leftResult.string);
break;
case NaNResult: rsSetNaN (result); break;
case InfResult: rsSetInf (result); break;
case NInfResult: rsSetNInf (result); break;
default: break;
}
xpathRSFree( &leftResult );
break;
case ExecFunction:
case ExecIdKey:
rc = xpathEvalFunction (step, ctxNode, exprContext, position, nodeList,
cbs, result, docOrder, errMsg);
CHECK_RC;
break;
default:
fprintf (stderr, "!! xpathEvalStep: not handled switch %s !!\n", astType2str[step->type]);
return XPATH_EVAL_ERR;
}
return XPATH_OK;
} /* xpathEvalStep */
/*----------------------------------------------------------------------------
| xpathEvalPredicate
|
\---------------------------------------------------------------------------*/
static int xpathEvalPredicate (
ast step,
domNode *exprContext,
xpathResultSet *result,
xpathResultSet *stepResult,
xpathCBs *cbs,
int *docOrder,
char **errMsg
)
{
xpathResultSet predResult, tmpResult;
int i, rc, savedDocOrder, useFastAdd;
if (result->nr_nodes == 0) useFastAdd = 1;
else useFastAdd = 0;
savedDocOrder = *docOrder;
while (step && step->type == Pred) {
xpathRSInit (&tmpResult);
if (step->child->type == Int) {
if (stepResult->nr_nodes >= step->child->intvalue
&& step->child->intvalue > 0) {
if (*docOrder) {
rsAddNode (&tmpResult,
stepResult->nodes[step->child->intvalue - 1]);
} else {
rsAddNode (&tmpResult,
stepResult->nodes[stepResult->nr_nodes -
step->child->intvalue]);
}
}
goto nextPred;
}
for (i=0; i<stepResult->nr_nodes; i++) {
xpathRSInit (&predResult);
rc = xpathEvalStep( step->child, stepResult->nodes[i],
exprContext, i, stepResult, cbs, &predResult,
docOrder, errMsg);
CHECK_RC;
*docOrder = savedDocOrder;
DBG( fprintf(stderr, "after eval for Predicate: \n"); )
DBG( rsPrint( &predResult); )
if (predResult.type == RealResult) {
predResult.type = IntResult;
predResult.intvalue = xpathRound(predResult.realvalue);
}
if (predResult.type == IntResult) {
if (predResult.intvalue < 0) {
predResult.intvalue =
stepResult->nr_nodes + predResult.intvalue;
}
if (savedDocOrder) {
if (predResult.intvalue == (i+1)) {
rsAddNodeFast (&tmpResult, stepResult->nodes[i]);
}
} else {
if (predResult.intvalue == (stepResult->nr_nodes - i)){
rsAddNodeFast (&tmpResult, stepResult->nodes[i]);
}
}
} else if (xpathFuncBoolean(&predResult)) {
rsAddNodeFast (&tmpResult, stepResult->nodes[i]);
}
xpathRSFree (&predResult);
}
nextPred:
DBG( fprintf(stderr, "result after Predicate: \n"); )
DBG( rsPrint( &tmpResult); )
xpathRSFree( stepResult );
*stepResult = tmpResult;
step = step->next;
}
/* add remaining result set to overall result set */
for (i=0; i<stepResult->nr_nodes; i++) {
checkRsAddNode (result, stepResult->nodes[i]);
}
return 0;
}
/*----------------------------------------------------------------------------
| xpathEvalStepAndPredicates
|
\---------------------------------------------------------------------------*/
static int xpathEvalStepAndPredicates (
ast steps,
xpathResultSet *nodeList,
domNode *currentNode,
domNode *exprContext,
int currentPos,
int *docOrder,
xpathCBs *cbs,
xpathResultSet *result,
char **errMsg
)
{
xpathResultSet stepResult;
int rc;
/* For AxisDescendantOrSelf/AxisDescendant, the predicate filtering
is already done 'inlined' during xpathEvalStep, to do the filtering
"with respect to the child axis" (XPath rec. 3.3) in an efficienter
way, then up to now. */
if ( steps->next
&& steps->next->type == Pred
&& steps->type != AxisDescendantOrSelf
&& steps->type != AxisDescendant) {
xpathRSInit (&stepResult);
rc = xpathEvalStep( steps, currentNode, exprContext, currentPos,
nodeList, cbs, &stepResult, docOrder, errMsg);
if (rc) {
xpathRSFree (&stepResult);
return rc;
}
rc = xpathEvalPredicate (steps->next, exprContext, result, &stepResult,
cbs, docOrder, errMsg);
xpathRSFree (&stepResult);
CHECK_RC;
} else {
/* for steps not followed by a predicate immediately add to
the final result set */
rc = xpathEvalStep( steps, currentNode, exprContext, currentPos, nodeList,
cbs, result, docOrder, errMsg);
CHECK_RC;
DBG( rsPrint( result); )
}
return 0;
}
/*----------------------------------------------------------------------------
| xpathEvalSteps
|
\---------------------------------------------------------------------------*/
int xpathEvalSteps (
ast steps,
xpathResultSet *nodeList,
domNode *currentNode,
domNode *exprContext,
int currentPos,
int *docOrder,
xpathCBs *cbs,
xpathResultSet *result,
char **errMsg
)
{
int i, rc, first = 1;
xpathResultSet savedContext;
DBG (fprintf (stderr, "xpathEvalSteps start\n");)
savedContext = *nodeList;
xpathRSInit (result);
while (steps) {
DBG (fprintf (stderr, "xpathEvalSteps: eval step '%s'\n",
astType2str[steps->type]);)
if (steps->type == Pred) {
*errMsg = "Pred step not expected now!";
return XPATH_EVAL_ERR;
}
if (first) {
rc = xpathEvalStepAndPredicates (steps, nodeList, currentNode,
exprContext, currentPos, docOrder,
cbs, result, errMsg);
CHECK_RC;
first = 0;
} else {
DBG( fprintf(stderr, "doing location step nodeList->nr_nodes=%d \n",
nodeList->nr_nodes);
)
if (result->type != xNodeSetResult) {
xpathRSFree (result);
xpathRSInit (result);
*nodeList = savedContext;
return 0;
}
*nodeList = *result;
xpathRSInit (result);
for (i=0; i<nodeList->nr_nodes; i++) {
rc = xpathEvalStepAndPredicates (steps, nodeList,
nodeList->nodes[i],
exprContext, i, docOrder, cbs,
result, errMsg);
if (rc) {
xpathRSFree (result);
xpathRSFree (nodeList);
return rc;
}
}
xpathRSFree (nodeList);
}
DBG( fprintf(stderr, "result after location step: \n"); )
DBG( rsPrint( result); )
steps = steps->next;
/* skip the already processed Predicate parts */
while (steps && steps->type == Pred) steps = steps->next;
*docOrder = 1;
}
*nodeList = savedContext;
return 0;
}
/*----------------------------------------------------------------------------
| xpathEval
|
\---------------------------------------------------------------------------*/
int xpathEval (
domNode * node,
domNode * exprContext,
char * xpath,
char ** prefixMappings,
xpathCBs * cbs,
xpathParseVarCB * parseVarCB,
Tcl_HashTable * cache,
char ** errMsg,
xpathResultSet * result
)
{
xpathResultSet nodeList;
int rc, hnew = 1, docOrder = 1;
ast t;
Tcl_HashEntry *h = NULL;
*errMsg = NULL;
if (cache) {
h = Tcl_CreateHashEntry (cache, xpath, &hnew);
}
if (hnew) {
rc = xpathParse(xpath, exprContext, XPATH_EXPR, prefixMappings,
parseVarCB, &t, errMsg);
if (rc) {
if (h != NULL) {
Tcl_DeleteHashEntry(h);
}
return rc;
}
if (cache) {
Tcl_SetHashValue(h, t);
}
} else {
t = (ast)Tcl_GetHashValue(h);
}
xpathRSInit( &nodeList);
rsAddNodeFast( &nodeList, node);
rc = xpathEvalSteps( t, &nodeList, node, exprContext, 0, &docOrder, cbs,
result, errMsg);
if (!cache) {
freeAst(t);
}
xpathRSFree( &nodeList );
CHECK_RC;
DBG(rsPrint( result );)
return 0;
} /* xpathEval */
/*----------------------------------------------------------------------------
| xpathMatches
|
\---------------------------------------------------------------------------*/
int xpathMatches (
ast step,
domNode * exprContext,
domNode * nodeToMatch,
xpathCBs * cbs,
char ** errMsg
)
{
xpathResultSet stepResult, nodeList, newNodeList;
ast childSteps;
int rc, i, j, currentPos = 0, nodeMatches, docOrder = 1;
int useFastAdd;
const char *localName = NULL, *nodeUri;
domAttrNode *attr;
domNode *child;
DBG(printAst(3,step));
xpathRSInit (&nodeList);
while (step) {
TRACE1("xpathMatches type=%d \n", step->type);
if (nodeList.nr_nodes == 0) useFastAdd = 1;
else useFastAdd = 0;
switch (step->type) {
case AxisAttribute:
if (step->child->type != IsAttr && step->child->type != IsNSAttr) {
if (step->child->type == IsElement) {
step->child->type = IsAttr;
} else {
DBG(fprintf(stderr, "strange: AxisAttribute with no IsAttr below!\n");)
xpathRSFree (&nodeList); return 0;
}
}
attr = NULL;
if (nodeToMatch->nodeType == ATTRIBUTE_NODE) {
rc = xpathMatches (step->child, exprContext, nodeToMatch, cbs,
errMsg);
DBG(fprintf(stderr, "rc=%d attribute match\n", rc); )
if (rc != 1) { xpathRSFree (&nodeList); return 0; }
attr = (domAttrNode*) nodeToMatch;
} else { xpathRSFree (&nodeList); return 0; }
if (attr == NULL) { xpathRSFree (&nodeList); return 0; }
break;
case AxisChild:
if (step->child->type == IsElement) {
if (nodeToMatch->nodeType != ELEMENT_NODE) {
xpathRSFree (&nodeList); return 0;
}
if (nodeToMatch == nodeToMatch->ownerDocument->rootNode) {
xpathRSFree (&nodeList); return 0;
}
if ((step->child->strvalue[0] != '*') ||
(step->child->strvalue[1] != '\0') ||
(step->child->intvalue != 0))
{
if (nodeToMatch->namespace) return 0;
if (strcmp(nodeToMatch->nodeName, step->child->strvalue)!=0) {
xpathRSFree (&nodeList); return 0;
}
}
break;
}
if (step->child->type == IsFQElement) {
if (nodeToMatch->nodeType != ELEMENT_NODE) {
xpathRSFree (&nodeList); return 0;
}
nodeUri = domNamespaceURI (nodeToMatch);
if (!nodeUri) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->child->strvalue, nodeUri) != 0) {
xpathRSFree (&nodeList); return 0;
}
localName = domGetLocalName (nodeToMatch->nodeName);
if (!localName) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->child->child->strvalue, localName)!=0) {
xpathRSFree (&nodeList); return 0;
}
break;
}
if (step->child->type == IsNSElement) {
nodeUri = domNamespaceURI (nodeToMatch);
if (!nodeUri) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->child->strvalue, nodeUri)!=0) {
xpathRSFree (&nodeList); return 0;
}
break;
}
DBG(fprintf(stderr, "strange: AxisChild with no IsElement, IsFQElement or IsNSElement below!\n");)
return 0;
case IsElement:
if (nodeToMatch->nodeType != ELEMENT_NODE) {
xpathRSFree (&nodeList); return 0;
}
if (nodeToMatch == nodeToMatch->ownerDocument->rootNode) {
xpathRSFree (&nodeList); return 0;
}
if ((step->strvalue[0] != '*') ||
(step->strvalue[1] != '\0') ||
(step->intvalue != 0))
{
if (nodeToMatch->namespace) return 0;
if (strcmp(nodeToMatch->nodeName, step->strvalue)!=0) {
xpathRSFree (&nodeList); return 0;
}
}
break;
case IsFQElement:
if (nodeToMatch->nodeType != ELEMENT_NODE) {
xpathRSFree (&nodeList); return 0;
}
nodeUri = domNamespaceURI (nodeToMatch);
if (!nodeUri) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->strvalue, nodeUri) != 0) {
xpathRSFree (&nodeList); return 0;
}
localName = domGetLocalName (nodeToMatch->nodeName);
if (!localName) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->child->strvalue, localName)!=0) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsNSElement:
nodeUri = domNamespaceURI (nodeToMatch);
if (!nodeUri) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->strvalue, nodeUri)!=0) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsAttr:
if ((nodeToMatch->nodeType != ATTRIBUTE_NODE)
|| (nodeToMatch->nodeFlags & IS_NS_NODE)) {
xpathRSFree (&nodeList); return 0;
}
if (!((step->strvalue[0] == '*') && (step->strvalue[1] == '\0'))) {
if (strcmp( ((domAttrNode*)nodeToMatch)->nodeName, step->strvalue)!=0) {
xpathRSFree (&nodeList); return 0;
}
}
break;
case IsNSAttr:
if ((nodeToMatch->nodeType != ATTRIBUTE_NODE)
|| (nodeToMatch->nodeFlags & IS_NS_NODE)) {
xpathRSFree (&nodeList); return 0;
}
nodeUri = domNamespaceURI (nodeToMatch);
if (!nodeUri) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->strvalue, nodeUri) != 0) {
xpathRSFree (&nodeList); return 0;
}
if (strcmp (step->child->strvalue, "*")==0) break;
localName = domGetLocalName (((domAttrNode *)nodeToMatch)->nodeName);
if (!localName) { xpathRSFree (&nodeList); return 0; }
if (strcmp (step->child->strvalue, localName)!=0) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsNode:
DBG(fprintf(stderr, "IsNode: nodeTpye=%d \n", nodeToMatch->nodeType);)
if (nodeToMatch->nodeType == ATTRIBUTE_NODE) {
xpathRSFree (&nodeList); return 0;
}
if ((nodeToMatch->nodeType == ELEMENT_NODE) &&
(nodeToMatch->ownerDocument->rootNode == nodeToMatch)) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsText:
if (nodeToMatch->nodeType != TEXT_NODE) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsPI:
if (nodeToMatch->nodeType != PROCESSING_INSTRUCTION_NODE) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsSpecificPI:
if (nodeToMatch->nodeType != PROCESSING_INSTRUCTION_NODE) {
xpathRSFree (&nodeList); return 0;
}
if (strncmp(((domProcessingInstructionNode*)nodeToMatch)->targetValue, step->strvalue, ((domProcessingInstructionNode*)nodeToMatch)->targetLength) != 0) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsComment:
if (nodeToMatch->nodeType != COMMENT_NODE) {
xpathRSFree (&nodeList); return 0;
}
break;
case IsRoot:
if (nodeToMatch->nodeType == ATTRIBUTE_NODE) {
xpathRSFree (&nodeList); return 0;
}
if (nodeToMatch != nodeToMatch->ownerDocument->rootNode) {
xpathRSFree (&nodeList); return 0;
}
break;
case ToParent:
if (nodeToMatch->nodeType == ATTRIBUTE_NODE) {
nodeToMatch = ((domAttrNode *)nodeToMatch)->parentNode;
break;
}
if (nodeToMatch == nodeToMatch->ownerDocument->rootNode) {
xpathRSFree (&nodeList); return 0;
}
if (nodeToMatch->parentNode) {
nodeToMatch = nodeToMatch->parentNode;
} else {
nodeToMatch = nodeToMatch->ownerDocument->rootNode;
}
break;
case ToAncestors:
if (step->next == NULL) { xpathRSFree (&nodeList); return 1;}
while (1) {
if (nodeToMatch->nodeType == ATTRIBUTE_NODE) {
nodeToMatch = ((domAttrNode *)nodeToMatch)->parentNode;
} else {
if (nodeToMatch == nodeToMatch->ownerDocument->rootNode) {
xpathRSFree (&nodeList); return 0;
}
if (nodeToMatch->parentNode) {
nodeToMatch = nodeToMatch->parentNode;
} else {
nodeToMatch = nodeToMatch->ownerDocument->rootNode;
}
}
if (step->next == NULL) {
xpathRSFree (&nodeList); return 0;
}
rc = xpathMatches (step->next, exprContext, nodeToMatch,
cbs, errMsg);
if (rc == 1) break;
}
break;
case FillWithCurrentNode:
checkRsAddNode( &nodeList, nodeToMatch);
currentPos = 0;
DBG(
fprintf(stderr, "FillWithCurrentNode:\n");
rsPrint(&nodeList);
)
break;
case FillNodeList:
if (nodeToMatch->nodeType == ATTRIBUTE_NODE) {
child = (domNode*) ((domAttrNode*)nodeToMatch)->parentNode->firstAttr;
DBG(fprintf(stderr, "FillNodeList all attribute\n");)
} else {
if (nodeToMatch->ownerDocument->rootNode == nodeToMatch) {
xpathRSFree (&nodeList); return 0;
}
DBG(if (nodeToMatch->parentNode) {
fprintf(stderr, "FillNodeList on %s/%s...\n", nodeToMatch->parentNode->nodeName,nodeToMatch->nodeName);
} else {
fprintf(stderr, "FillNodeList on (null)/%s...\n", nodeToMatch->nodeName);
}
fprintf (stderr, "Current nodeList is:\n");
rsPrint (&nodeList);
)
if (nodeToMatch->parentNode == NULL) {
child = nodeToMatch->ownerDocument->rootNode->firstChild;
} else {
child = nodeToMatch->parentNode->firstChild;
}
}
currentPos = -1;
i = 0;
while (child) {
rc = xpathMatches (step->child, exprContext, child, cbs, errMsg);
if (rc == 1) {
checkRsAddNode( &nodeList, child);
if (child == nodeToMatch) currentPos = i;
i++;
if (step->intvalue && i >= step->intvalue) break;
}
if (child->nodeType == ATTRIBUTE_NODE) {
child = (domNode*) ((domAttrNode*)child)->nextSibling;
} else {
child = child->nextSibling;
}
}
DBG(
rsPrint(&nodeList);
fprintf(stderr, "currentPos = %d \n", currentPos);
)
break;
case Pred:
xpathRSInit (&stepResult);
DBG(fprintf(stderr, "Befor Pred inner EvalStep. currentPos = %d\n", currentPos);)
rc = xpathEvalStep (step->child, nodeToMatch, exprContext,
currentPos, &nodeList, cbs, &stepResult,
&docOrder, errMsg);
CHECK_RC;
DBG(
fprintf(stderr, "Pred inner EvalStep returned\n");
rsPrint(&stepResult);
)
nodeMatches = 0;
if (stepResult.type == RealResult) {
stepResult.type = IntResult;
stepResult.intvalue = xpathRound(stepResult.realvalue);
}
if (stepResult.type == IntResult) {
if (stepResult.intvalue < 0) {
stepResult.intvalue = nodeList.nr_nodes + stepResult.intvalue;
}
if ((stepResult.intvalue > 0 ) &&
(stepResult.intvalue <= nodeList.nr_nodes) &&
(stepResult.intvalue == (currentPos+1)) ) {
nodeMatches = 1;
}
} else if (xpathFuncBoolean(&stepResult)) {
nodeMatches = 1;
}
xpathRSFree (&stepResult);
/* if nodeMatches == false we don't have to continue to filter */
if (!nodeMatches) {
xpathRSFree (&nodeList); return 0;
}
/* if the nr_nodes of nodeList is > 1 (ie. we filter a
FillNodeList step, not only a FillWithCurrentNode
step), build the resulting nodeList context after
this predicate */
if (nodeList.nr_nodes > 1) {
xpathRSInit (&newNodeList);
currentPos = -1;
j = 0;
for (i = 0; i < nodeList.nr_nodes; i++) {
xpathRSInit (&stepResult);
docOrder = 1;
rc = xpathEvalStep (step->child, nodeList.nodes[i],
exprContext, i, &nodeList, cbs,
&stepResult, &docOrder, errMsg);
if (rc) {
xpathRSFree (&stepResult);
xpathRSFree (&nodeList);
return rc;
}
nodeMatches = 0;
if (stepResult.type == RealResult) {
stepResult.type = IntResult;
stepResult.intvalue =
xpathRound(stepResult.realvalue);
}
if (stepResult.type == IntResult) {
if (stepResult.intvalue < 0) {
stepResult.intvalue =
nodeList.nr_nodes + stepResult.intvalue;
}
if ((stepResult.intvalue > 0 ) &&
(stepResult.intvalue <= nodeList.nr_nodes) &&
(stepResult.intvalue == (currentPos+1)) ) {
nodeMatches = 1;
}
} else if (xpathFuncBoolean(&stepResult)) {
nodeMatches = 1;
}
if (nodeMatches) {
checkRsAddNode (&newNodeList, nodeList.nodes[i]);
if (nodeList.nodes[i] == nodeToMatch) {
currentPos = j;
}
j++;
}
xpathRSFree (&stepResult);
}
xpathRSFree (&nodeList);
nodeList = newNodeList;
}
break;
case CombinePath:
childSteps = step->child;
while (childSteps) {
rc = xpathMatches (childSteps->child, exprContext,
nodeToMatch, cbs, errMsg);
if (rc == 1) break;
childSteps = childSteps->next;
}
if (childSteps == NULL) {
xpathRSFree (&nodeList); return 0;
}
break;
case ExecIdKey:
xpathRSInit (&stepResult);
rc = xpathEvalStep (step, nodeToMatch, exprContext,
currentPos, &nodeList, cbs, &stepResult,
&docOrder, errMsg);
CHECK_RC;
if (stepResult.type != xNodeSetResult) {
xpathRSFree (&stepResult);
xpathRSFree (&nodeList); return 0;
}
nodeMatches = 0;
for (i = 0; i < stepResult.nr_nodes; i++) {
if (nodeToMatch == stepResult.nodes[i]) {
nodeMatches = 1;
break;
}
}
xpathRSFree (&stepResult);
if (!nodeMatches) { xpathRSFree (&nodeList); return 0; }
break;
default:
DBG(
fprintf(stderr, "wrong type %d for xpathMatches \n", step->type);
fprintf(stderr, "AST:\n");
)
printAst (0, step);
xpathRSFree (&nodeList); return 0;
break;
}
step = step->next;
}
xpathRSFree (&nodeList);
return 1;
}
/*----------------------------------------------------------------------------
| xpathGetPrio
|
\---------------------------------------------------------------------------*/
double xpathGetPrio (
ast steps
)
{
if (!steps) return 0.0;
DBG(printAst(0, steps);)
if (steps->next == NULL) {
if (steps->type == IsElement) {
if (strcmp(steps->strvalue, "*")==0 && steps->intvalue==0) {
return -0.5;
} else {
return 0.0;
}
} else
if (steps->type == IsFQElement) {
return 0.0;
} else
if (steps->type == IsNSElement) {
return -0.25;
} else
if (steps->type == IsAttr) {
if (strcmp(steps->strvalue, "*")==0) {
return -0.5;
} else {
return 0.0;
}
} else
if (steps->type == IsNSAttr) {
if (strcmp(steps->child->strvalue, "*")==0) {
return -0.25;
} else {
return 0.0;
}
} else
if (steps->type == IsSpecificPI) {
return 0.0;
} else
if ( steps->type == IsNode
|| steps->type == IsText
|| steps->type == IsPI
|| steps->type == IsComment
) {
return -0.5;
} else
if ( steps->type == AxisChild
|| steps->type == AxisAttribute
|| steps->type == EvalSteps
) {
return (xpathGetPrio (steps->child));
}
}
return 0.5;
} /* xpathGetPrio */
/*----------------------------------------------------------------------------
| nodeToXPath - returns a XPath addressing exactly the given node
|
\---------------------------------------------------------------------------*/
static void nodeToXPath (
domNode * node,
char ** xpath,
int * xpathLen,
int * xpathAllocated,
int legacy
)
{
domNode *parent, *child;
char step[200], *nTest;
int sameNodes, nodeIndex, len;
parent = node->parentNode;
if (parent == NULL) {
parent = node->ownerDocument->rootNode;
} else {
nodeToXPath (parent, xpath, xpathLen, xpathAllocated, legacy);
}
step[0] = '\0';
switch (node->nodeType) {
case ELEMENT_NODE:
nodeIndex = 0;
sameNodes = 0;
child = parent->firstChild;
if (node->namespace && !legacy) {
while (child) {
if (child->nodeType == ELEMENT_NODE) {
sameNodes++;
if (node == child) {
nodeIndex = sameNodes;
if (sameNodes > 1) break;
}
}
child = child->nextSibling;
}
if (sameNodes == 1) {
sprintf(step, "/*");
} else {
sprintf(step, "/*[%d]", nodeIndex);
}
} else {
while (child) {
if (strcmp(child->nodeName, node->nodeName)==0) {
sameNodes++;
if (node == child) nodeIndex = sameNodes;
if ((nodeIndex != 0) && (sameNodes > 2)) break;
}
child = child->nextSibling;
}
if (sameNodes == 1) {
sprintf(step, "/%s", node->nodeName);
} else {
sprintf(step, "/%s[%d]", node->nodeName, nodeIndex);
}
}
break;
case TEXT_NODE:
case COMMENT_NODE:
case PROCESSING_INSTRUCTION_NODE:
nodeIndex = 0;
sameNodes = 0;
child = parent->firstChild;
while (child) {
if (child->nodeType == node->nodeType) {
sameNodes++;
if (node == child) nodeIndex = sameNodes;
if ((nodeIndex != 0) && (sameNodes > 2)) break;
}
child = child->nextSibling;
}
switch (node->nodeType) {
case TEXT_NODE: nTest = "text()"; break;
case COMMENT_NODE: nTest = "comment()"; break;
case PROCESSING_INSTRUCTION_NODE: nTest = "processing-instruction()"; break;
default: nTest = "unknownNodeType()";
}
if (sameNodes == 1) {
sprintf(step, "/%s", nTest);
} else {
sprintf(step, "/%s[%d]", nTest, nodeIndex);
}
break;
default:
break;
}
len = strlen(step);
if ( (len + *xpathLen) > *xpathAllocated ) {
*xpathAllocated = *xpathAllocated * 2;
*xpath = REALLOC(*xpath, *xpathAllocated + 1);
}
strcpy( *xpath + *xpathLen, step);
*xpathLen += len;
} /* nodeToXPath */
/*----------------------------------------------------------------------------
| xpathNodeToXPath
|
\---------------------------------------------------------------------------*/
char * xpathNodeToXPath (
domNode *node,
int legacy
)
{
char * xpath;
int xpathLen, xpathAllocated;
xpathAllocated = 100;
xpathLen = 0;
xpath = MALLOC(xpathAllocated + 1);
nodeToXPath (node, &xpath, &xpathLen, &xpathAllocated, legacy);
return xpath;
} /* xpathNodeToXPath */