/*---------------------------------------------------------------------------- | 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 #include #include #include #include #include #include #include #include #include #include /*---------------------------------------------------------------------------- | 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; inr_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, "", 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; inr_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; itype]); 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; ichild, 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; itype]); 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; inr_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; inr_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; inr_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 */