From d8df6c0e8c883e1d618388d266015dda36731440 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Thu, 5 Apr 2001 16:54:14 +0000 Subject: [PATCH] Fixes in the plane: - xpath.c: fixed a [] evaluation problem reported - Fixes in the plane: - xpath.c: fixed a [] evaluation problem reported - test/XPath/tests/simpleaddr: extended test - result/XPath/simpleaddr: updated result Daniel --- ChangeLog | 6 + aclocal.m4 | 40 +- result/XPath/tests/simpleabbr | 20 +- test/XPath/tests/simpleabbr | 2 + testXPath.c | 1 + xpath.c | 770 ++++++++++++++++++---------------- 6 files changed, 464 insertions(+), 375 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3aa1a2e7..bb66ff3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Fri Apr 6 12:53:05 CEST 2001 Daniel Veillard + + * xpath.c: fixed a [] evaluation problem reported + * test/XPath/tests/simpleaddr: extended test + * result/XPath/simpleaddr: updated result + Wed Apr 4 02:07:53 CEST 2001 Daniel Veillard * xmllint.c: Dan Timis reported a portability problem diff --git a/aclocal.m4 b/aclocal.m4 index 869e5fcc..e3726759 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -620,31 +620,35 @@ esac ]) # AC_LIBLTDL_CONVENIENCE[(dir)] - sets LIBLTDL to the link flags for -# the libltdl convenience library, adds --enable-ltdl-convenience to -# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor -# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed -# to be `${top_builddir}/libltdl'. Make sure you start DIR with -# '${top_builddir}/' (note the single quotes!) if your package is not -# flat, and, if you're not using automake, define top_builddir as -# appropriate in the Makefiles. +# the libltdl convenience library and INCLTDL to the include flags for +# the libltdl header and adds --enable-ltdl-convenience to the +# configure arguments. Note that LIBLTDL and INCLTDL are not +# AC_SUBSTed, nor is AC_CONFIG_SUBDIRS called. If DIR is not +# provided, it is assumed to be `libltdl'. LIBLTDL will be prefixed +# with '${top_builddir}/' and INCLTDL will be prefixed with +# '${top_srcdir}/' (note the single quotes!). If your package is not +# flat and you're not using automake, define top_builddir and +# top_srcdir appropriately in the Makefiles. AC_DEFUN(AC_LIBLTDL_CONVENIENCE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl case "$enable_ltdl_convenience" in no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; "") enable_ltdl_convenience=yes ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; esac - LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdlc.la - INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la + INCLTDL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) ]) # AC_LIBLTDL_INSTALLABLE[(dir)] - sets LIBLTDL to the link flags for -# the libltdl installable library, and adds --enable-ltdl-install to -# the configure arguments. Note that LIBLTDL is not AC_SUBSTed, nor -# is AC_CONFIG_SUBDIRS called. If DIR is not provided, it is assumed -# to be `${top_builddir}/libltdl'. Make sure you start DIR with -# '${top_builddir}/' (note the single quotes!) if your package is not -# flat, and, if you're not using automake, define top_builddir as -# appropriate in the Makefiles. +# the libltdl installable library and INCLTDL to the include flags for +# the libltdl header and adds --enable-ltdl-install to the configure +# arguments. Note that LIBLTDL and INCLTDL are not AC_SUBSTed, nor is +# AC_CONFIG_SUBDIRS called. If DIR is not provided and an installed +# libltdl is not found, it is assumed to be `libltdl'. LIBLTDL will +# be prefixed with '${top_builddir}/' and INCLTDL will be prefixed +# with '${top_srcdir}/' (note the single quotes!). If your package is +# not flat and you're not using automake, define top_builddir and +# top_srcdir appropriately in the Makefiles. # In the future, this macro may have to be called after AC_PROG_LIBTOOL. AC_DEFUN(AC_LIBLTDL_INSTALLABLE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl AC_CHECK_LIB(ltdl, main, @@ -657,8 +661,8 @@ AC_DEFUN(AC_LIBLTDL_INSTALLABLE, [AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl ]) if test x"$enable_ltdl_install" = x"yes"; then ac_configure_args="$ac_configure_args --enable-ltdl-install" - LIBLTDL=ifelse($#,1,$1,['${top_builddir}/libltdl'])/libltdl.la - INCLTDL=ifelse($#,1,-I$1,['-I${top_builddir}/libltdl']) + LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la + INCLTDL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) else ac_configure_args="$ac_configure_args --enable-ltdl-install=no" LIBLTDL="-lltdl" diff --git a/result/XPath/tests/simpleabbr b/result/XPath/tests/simpleabbr index e8d5d97c..85da41a2 100644 --- a/result/XPath/tests/simpleabbr +++ b/result/XPath/tests/simpleabbr @@ -51,12 +51,30 @@ Set contains 2 nodes: ======================== Expression: //p/text()[position()=1] Object is a Node Set : +Set contains 2 nodes: +1 TEXT + content=bla bla bla ... +2 TEXT + content=... + +======================== +Expression: //p/text()[position()=last()] +Object is a Node Set : +Set contains 2 nodes: +1 TEXT + content=bla bla bla ... +2 TEXT + content=... + +======================== +Expression: (//p/text())[position()=1] +Object is a Node Set : Set contains 1 nodes: 1 TEXT content=bla bla bla ... ======================== -Expression: //p/text()[position()=last()] +Expression: (//p/text())[position()=last()] Object is a Node Set : Set contains 1 nodes: 1 TEXT diff --git a/test/XPath/tests/simpleabbr b/test/XPath/tests/simpleabbr index a7e3f542..8de476fe 100644 --- a/test/XPath/tests/simpleabbr +++ b/test/XPath/tests/simpleabbr @@ -6,3 +6,5 @@ //p/text() //p/text()[position()=1] //p/text()[position()=last()] +(//p/text())[position()=1] +(//p/text())[position()=last()] diff --git a/testXPath.c b/testXPath.c index 424380b0..fde0adc4 100644 --- a/testXPath.c +++ b/testXPath.c @@ -113,6 +113,7 @@ testXPath(const char *str) { xmlXPathDebugDumpCompExpr(stdout, comp, 0); res = xmlXPathCompiledEval(comp, ctxt); + xmlXPathFreeCompExpr(comp); } else res = NULL; } diff --git a/xpath.c b/xpath.c index 98f37c0e..89b139f8 100644 --- a/xpath.c +++ b/xpath.c @@ -204,6 +204,7 @@ typedef enum { XPATH_OP_FUNCTION, XPATH_OP_ARG, XPATH_OP_PREDICATE, + XPATH_OP_FILTER, XPATH_OP_SORT #ifdef LIBXML_XPTR_ENABLED ,XPATH_OP_RANGETO @@ -378,6 +379,9 @@ xmlXPathCompExprAdd(xmlXPathCompExprPtr comp, int ch1, int ch2, return(comp->nbStep++); } +#define PUSH_FULL_EXPR(op, op1, op2, val, val2, val3, val4, val5) \ + xmlXPathCompExprAdd(ctxt->comp, (op1), (op2), \ + (op), (val), (val2), (val3), (val4), (val5)) #define PUSH_LONG_EXPR(op, val, val2, val3, val4, val5) \ xmlXPathCompExprAdd(ctxt->comp, ctxt->comp->last, -1, \ (op), (val), (val2), (val3), (val4), (val5)) @@ -782,6 +786,7 @@ xmlXPathDebugDumpStepOp(FILE *output, xmlXPathCompExprPtr comp, } case XPATH_OP_ARG: fprintf(output, "ARG"); break; case XPATH_OP_PREDICATE: fprintf(output, "PREDICATE"); break; + case XPATH_OP_FILTER: fprintf(output, "FILTER"); break; default: fprintf(output, "UNKNOWN %d\n", op->op); return; } @@ -1096,6 +1101,9 @@ xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file, cur = ctxt->cur; base = ctxt->base; + if ((cur == NULL) || (base == NULL)) + return; + while ((cur > base) && ((*cur == '\n') || (*cur == '\r'))) { cur--; } @@ -1376,7 +1384,7 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) { */ xmlNodeSetPtr xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { - int i, j, initNr; + int i, j, initNr, skip; if (val2 == NULL) return(val1); if (val1 == NULL) { @@ -1389,8 +1397,15 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) { /* * check against doublons */ - for (j = 0; j < initNr; j++) - if (val1->nodeTab[j] == val2->nodeTab[i]) continue; + skip = 0; + for (j = 0; j < initNr; j++) { + if (val1->nodeTab[j] == val2->nodeTab[i]) { + skip = 1; + break; + } + } + if (skip) + continue; /* * grow the nodeTab if needed @@ -2555,12 +2570,12 @@ xmlXPathCompareNodeSets(int inf, int strict, ns1 = arg1->nodesetval; ns2 = arg2->nodesetval; - if (ns1->nodeNr <= 0) { + if ((ns1 == NULL) || (ns1->nodeNr <= 0)) { xmlXPathFreeObject(arg1); xmlXPathFreeObject(arg2); return(0); } - if (ns2->nodeNr <= 0) { + if ((ns2 == NULL) || (ns2->nodeNr <= 0)) { xmlXPathFreeObject(arg1); xmlXPathFreeObject(arg2); return(0); @@ -2685,6 +2700,8 @@ xmlXPathEqualNodeSetString(xmlXPathObjectPtr arg, const xmlChar *str) { ((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) return(0); ns = arg->nodesetval; + if (ns == NULL) + return(0); if (ns->nodeNr <= 0) return(0); for (i = 0;i < ns->nodeNr;i++) { @@ -3752,339 +3769,6 @@ xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) { #define IS_FUNCTION 200 -/** - * xmlXPathNodeCollectAndTest: - * @ctxt: the XPath Parser context - * @axis: the XPath axis - * @test: the XPath test - * @type: the XPath type - * @prefix: the namesapce prefix if any - * @name: the name used in the search if any - * - * This is the function implementing a step: based on the current list - * of nodes, it builds up a new list, looking at all nodes under that - * axis and selecting them. - * - * Returns the new NodeSet resulting from the search. - */ -static void -xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, xmlXPathAxisVal axis, - xmlXPathTestVal test, xmlXPathTypeVal type, - const xmlChar *prefix, const xmlChar *name) { -#ifdef DEBUG_STEP - int n = 0, t = 0; -#endif - int i; - xmlNodeSetPtr ret; - xmlXPathTraversalFunction next = NULL; - void (*addNode)(xmlNodeSetPtr, xmlNodePtr); - xmlNodePtr cur = NULL; - xmlXPathObjectPtr obj; - xmlNodeSetPtr nodelist; - xmlNodePtr tmp; - - CHECK_TYPE(XPATH_NODESET); - obj = valuePop(ctxt); - addNode = xmlXPathNodeSetAdd; - -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "new step : "); -#endif - switch (axis) { - case AXIS_ANCESTOR: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'ancestors' "); -#endif - next = xmlXPathNextAncestor; break; - case AXIS_ANCESTOR_OR_SELF: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'ancestors-or-self' "); -#endif - next = xmlXPathNextAncestorOrSelf; break; - case AXIS_ATTRIBUTE: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'attributes' "); -#endif - next = xmlXPathNextAttribute; break; - break; - case AXIS_CHILD: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'child' "); -#endif - next = xmlXPathNextChild; break; - case AXIS_DESCENDANT: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'descendant' "); -#endif - next = xmlXPathNextDescendant; break; - case AXIS_DESCENDANT_OR_SELF: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'descendant-or-self' "); -#endif - next = xmlXPathNextDescendantOrSelf; break; - case AXIS_FOLLOWING: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'following' "); -#endif - next = xmlXPathNextFollowing; break; - case AXIS_FOLLOWING_SIBLING: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'following-siblings' "); -#endif - next = xmlXPathNextFollowingSibling; break; - case AXIS_NAMESPACE: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'namespace' "); -#endif - next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break; - break; - case AXIS_PARENT: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'parent' "); -#endif - next = xmlXPathNextParent; break; - case AXIS_PRECEDING: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'preceding' "); -#endif - next = xmlXPathNextPreceding; break; - case AXIS_PRECEDING_SIBLING: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'preceding-sibling' "); -#endif - next = xmlXPathNextPrecedingSibling; break; - case AXIS_SELF: -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "axis 'self' "); -#endif - next = xmlXPathNextSelf; break; - } - if (next == NULL) - return; - - nodelist = obj->nodesetval; - if ((nodelist != NULL) && - (nodelist->nodeNr <= 1)) - addNode = xmlXPathNodeSetAddUnique; - else - addNode = xmlXPathNodeSetAdd; - ret = xmlXPathNodeSetCreate(NULL); -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - " context contains %d nodes\n", - nodelist->nodeNr); - switch (test) { - case NODE_TEST_NODE: - xmlGenericError(xmlGenericErrorContext, - " searching all nodes\n"); - break; - case NODE_TEST_NONE: - xmlGenericError(xmlGenericErrorContext, - " searching for none !!!\n"); - break; - case NODE_TEST_TYPE: - xmlGenericError(xmlGenericErrorContext, - " searching for type %d\n", type); - break; - case NODE_TEST_PI: - xmlGenericError(xmlGenericErrorContext, - " searching for PI !!!\n"); - break; - case NODE_TEST_ALL: - xmlGenericError(xmlGenericErrorContext, - " searching for *\n"); - break; - case NODE_TEST_NS: - xmlGenericError(xmlGenericErrorContext, - " searching for namespace %s\n", - prefix); - break; - case NODE_TEST_NAME: - xmlGenericError(xmlGenericErrorContext, - " searching for name %s\n", name); - if (prefix != NULL) - xmlGenericError(xmlGenericErrorContext, - " with namespace %s\n", - prefix); - break; - } - xmlGenericError(xmlGenericErrorContext, "Testing : "); -#endif - /* - * 2.3 Node Tests - * - For the attribute axis, the principal node type is attribute. - * - For the namespace axis, the principal node type is namespace. - * - For other axes, the principal node type is element. - * - * A node test * is true for any node of the - * principal node type. For example, child::* willi - * select all element children of the context node - */ - tmp = ctxt->context->node; - for (i = 0;i < nodelist->nodeNr; i++) { - ctxt->context->node = nodelist->nodeTab[i]; - - cur = NULL; - do { - cur = next(ctxt, cur); - if (cur == NULL) break; -#ifdef DEBUG_STEP - t++; - xmlGenericError(xmlGenericErrorContext, " %s", cur->name); -#endif - switch (test) { - case NODE_TEST_NONE: - ctxt->context->node = tmp; - STRANGE - return; - case NODE_TEST_TYPE: - if ((cur->type == type) || - ((type == NODE_TYPE_NODE) && - ((cur->type == XML_DOCUMENT_NODE) || - (cur->type == XML_HTML_DOCUMENT_NODE) || - (cur->type == XML_ELEMENT_NODE) || - (cur->type == XML_PI_NODE) || - (cur->type == XML_COMMENT_NODE) || - (cur->type == XML_CDATA_SECTION_NODE) || - (cur->type == XML_TEXT_NODE)))) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } - break; - case NODE_TEST_PI: - if (cur->type == XML_PI_NODE) { - if ((name != NULL) && - (!xmlStrEqual(name, cur->name))) - break; -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } - break; - case NODE_TEST_ALL: - if (axis == AXIS_ATTRIBUTE) { - if (cur->type == XML_ATTRIBUTE_NODE) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } - } else if (axis == AXIS_NAMESPACE) { - if (cur->type == XML_NAMESPACE_DECL) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } - } else { - if ((cur->type == XML_ELEMENT_NODE) || - (cur->type == XML_DOCUMENT_NODE) || - (cur->type == XML_HTML_DOCUMENT_NODE)) { - if (prefix == NULL) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } else if ((cur->ns != NULL) && - (xmlStrEqual(prefix, - cur->ns->href))) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } - } - } - break; - case NODE_TEST_NS: { - TODO; - break; - } - case NODE_TEST_NAME: - switch (cur->type) { - case XML_ELEMENT_NODE: - if (xmlStrEqual(name, cur->name)) { - if (prefix == NULL) { - if ((cur->ns == NULL) || - (cur->ns->prefix == NULL)) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } - } else { - if ((cur->ns != NULL) && - (xmlStrEqual(prefix, - cur->ns->href))) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, cur); - } - } - } - break; - case XML_ATTRIBUTE_NODE: { - xmlAttrPtr attr = (xmlAttrPtr) cur; - if (xmlStrEqual(name, attr->name)) { - if (prefix == NULL) { - if ((attr->ns == NULL) || - (attr->ns->prefix == NULL)) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, (xmlNodePtr) attr); - } - } else { - if ((attr->ns != NULL) && - (xmlStrEqual(prefix, - attr->ns->href))) { -#ifdef DEBUG_STEP - n++; -#endif - addNode(ret, (xmlNodePtr) attr); - } - } - } - break; - } - case XML_NAMESPACE_DECL: { - TODO; - break; - } - default: - break; - } - break; - } - } while (cur != NULL); - } - ctxt->context->node = tmp; -#ifdef DEBUG_STEP - xmlGenericError(xmlGenericErrorContext, - "\nExamined %d nodes, found %d nodes at that step\n", t, n); -#endif - xmlXPathFreeObject(obj); - valuePush(ctxt, xmlXPathWrapNodeSet(ret)); -} - /************************************************************************ * * @@ -5280,7 +4964,7 @@ xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) { XP_ERROR(XPATH_INVALID_TYPE); cur = valuePop(ctxt); - if (cur->nodesetval->nodeNr == 0) { + if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) { valuePush(ctxt, xmlXPathNewFloat(0.0)); } else { valuePush(ctxt, @@ -5392,7 +5076,7 @@ xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { * implementation. */ static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt); -static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt); +static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter); static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt); #ifdef VMS static void xmlXPathCompRelLocationPath(xmlXPathParserContextPtr ctxt); @@ -5865,7 +5549,7 @@ xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) { SKIP_BLANKS; while (CUR == '[') { - xmlXPathCompPredicate(ctxt); + xmlXPathCompPredicate(ctxt, 1); SKIP_BLANKS; } @@ -6358,6 +6042,7 @@ xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) { /** * xmlXPathCompPredicate: * @ctxt: the XPath Parser context + * @filter: act as a filter * * [8] Predicate ::= '[' PredicateExpr ']' * [9] PredicateExpr ::= Expr @@ -6365,7 +6050,7 @@ xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) { * Compile a predicate expression */ static void -xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt) { +xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) { int op1 = ctxt->comp->last; SKIP_BLANKS; @@ -6383,7 +6068,10 @@ xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt) { XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); } - PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0); + if (filter) + PUSH_BINARY_EXPR(XPATH_OP_FILTER, op1, ctxt->comp->last, 0, 0); + else + PUSH_BINARY_EXPR(XPATH_OP_PREDICATE, op1, ctxt->comp->last, 0, 0); NEXT; SKIP_BLANKS; @@ -6637,8 +6325,9 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { xmlChar *name = NULL; const xmlChar *prefix = NULL; xmlXPathTestVal test; - xmlXPathAxisVal axis; + xmlXPathAxisVal axis = 0; xmlXPathTypeVal type; + int op1; /* * The modification needed for XPointer change to the production @@ -6647,7 +6336,7 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { if (ctxt->xptr) { name = xmlXPathParseNCName(ctxt); if ((name != NULL) && (xmlStrEqual(name, BAD_CAST "range-to"))) { - int op1 = ctxt->comp->last; + int op2 = ctxt->comp->last; xmlFree(name); SKIP_BLANKS; if (CUR != '(') { @@ -6657,7 +6346,7 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { SKIP_BLANKS; xmlXPathCompileExpr(ctxt); - PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op1, ctxt->comp->last, 0, 0); + PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); CHECK_ERROR; SKIP_BLANKS; @@ -6704,19 +6393,23 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) { "Basis : computing new set\n"); #endif - PUSH_LONG_EXPR(XPATH_OP_COLLECT, axis, test, type, - (void *)prefix, (void *)name); - #ifdef DEBUG_STEP xmlGenericError(xmlGenericErrorContext, "Basis : "); xmlGenericErrorContextNodeSet(stdout, ctxt->value->nodesetval); #endif eval_predicates: + op1 = ctxt->comp->last; + ctxt->comp->last = -1; + SKIP_BLANKS; while (CUR == '[') { - xmlXPathCompPredicate(ctxt); + xmlXPathCompPredicate(ctxt, 0); } + + PUSH_FULL_EXPR(XPATH_OP_COLLECT, op1, ctxt->comp->last, axis, + test, type, (void *)prefix, (void *)name); + } #ifdef DEBUG_STEP xmlGenericError(xmlGenericErrorContext, "Step : "); @@ -6821,6 +6514,366 @@ xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt) { * * ************************************************************************/ +static void +xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op); + +/** + * xmlXPathNodeCollectAndTest: + * @ctxt: the XPath Parser context + * @op: the XPath precompiled step operation + * + * This is the function implementing a step: based on the current list + * of nodes, it builds up a new list, looking at all nodes under that + * axis and selecting them it also do the predicate filtering + * + * Pushes the new NodeSet resulting from the search. + */ +static void +xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt, + xmlXPathStepOpPtr op) { + xmlXPathAxisVal axis = op->value; + xmlXPathTestVal test = op->value2; + xmlXPathTypeVal type = op->value3; + const xmlChar *prefix = op->value4; + const xmlChar *name = op->value5; + +#ifdef DEBUG_STEP + int n = 0, t = 0; +#endif + int i; + xmlNodeSetPtr ret, list; + xmlXPathTraversalFunction next = NULL; + void (*addNode)(xmlNodeSetPtr, xmlNodePtr); + xmlNodePtr cur = NULL; + xmlXPathObjectPtr obj; + xmlNodeSetPtr nodelist; + xmlNodePtr tmp; + + CHECK_TYPE(XPATH_NODESET); + obj = valuePop(ctxt); + addNode = xmlXPathNodeSetAdd; + +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "new step : "); +#endif + switch (axis) { + case AXIS_ANCESTOR: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'ancestors' "); +#endif + next = xmlXPathNextAncestor; break; + case AXIS_ANCESTOR_OR_SELF: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'ancestors-or-self' "); +#endif + next = xmlXPathNextAncestorOrSelf; break; + case AXIS_ATTRIBUTE: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'attributes' "); +#endif + next = xmlXPathNextAttribute; break; + break; + case AXIS_CHILD: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'child' "); +#endif + next = xmlXPathNextChild; break; + case AXIS_DESCENDANT: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'descendant' "); +#endif + next = xmlXPathNextDescendant; break; + case AXIS_DESCENDANT_OR_SELF: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'descendant-or-self' "); +#endif + next = xmlXPathNextDescendantOrSelf; break; + case AXIS_FOLLOWING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'following' "); +#endif + next = xmlXPathNextFollowing; break; + case AXIS_FOLLOWING_SIBLING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'following-siblings' "); +#endif + next = xmlXPathNextFollowingSibling; break; + case AXIS_NAMESPACE: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'namespace' "); +#endif + next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break; + break; + case AXIS_PARENT: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'parent' "); +#endif + next = xmlXPathNextParent; break; + case AXIS_PRECEDING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'preceding' "); +#endif + next = xmlXPathNextPreceding; break; + case AXIS_PRECEDING_SIBLING: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'preceding-sibling' "); +#endif + next = xmlXPathNextPrecedingSibling; break; + case AXIS_SELF: +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "axis 'self' "); +#endif + next = xmlXPathNextSelf; break; + } + if (next == NULL) + return; + + nodelist = obj->nodesetval; + if (nodelist == NULL) { + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathWrapNodeSet(NULL)); + return; + } + addNode = xmlXPathNodeSetAddUnique; + ret = NULL; +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + " context contains %d nodes\n", + nodelist->nodeNr); + switch (test) { + case NODE_TEST_NODE: + xmlGenericError(xmlGenericErrorContext, + " searching all nodes\n"); + break; + case NODE_TEST_NONE: + xmlGenericError(xmlGenericErrorContext, + " searching for none !!!\n"); + break; + case NODE_TEST_TYPE: + xmlGenericError(xmlGenericErrorContext, + " searching for type %d\n", type); + break; + case NODE_TEST_PI: + xmlGenericError(xmlGenericErrorContext, + " searching for PI !!!\n"); + break; + case NODE_TEST_ALL: + xmlGenericError(xmlGenericErrorContext, + " searching for *\n"); + break; + case NODE_TEST_NS: + xmlGenericError(xmlGenericErrorContext, + " searching for namespace %s\n", + prefix); + break; + case NODE_TEST_NAME: + xmlGenericError(xmlGenericErrorContext, + " searching for name %s\n", name); + if (prefix != NULL) + xmlGenericError(xmlGenericErrorContext, + " with namespace %s\n", + prefix); + break; + } + xmlGenericError(xmlGenericErrorContext, "Testing : "); +#endif + /* + * 2.3 Node Tests + * - For the attribute axis, the principal node type is attribute. + * - For the namespace axis, the principal node type is namespace. + * - For other axes, the principal node type is element. + * + * A node test * is true for any node of the + * principal node type. For example, child::* willi + * select all element children of the context node + */ + tmp = ctxt->context->node; + for (i = 0;i < nodelist->nodeNr; i++) { + ctxt->context->node = nodelist->nodeTab[i]; + + cur = NULL; + list = xmlXPathNodeSetCreate(NULL); + do { + cur = next(ctxt, cur); + if (cur == NULL) break; +#ifdef DEBUG_STEP + t++; + xmlGenericError(xmlGenericErrorContext, " %s", cur->name); +#endif + switch (test) { + case NODE_TEST_NONE: + ctxt->context->node = tmp; + STRANGE + return; + case NODE_TEST_TYPE: + if ((cur->type == type) || + ((type == NODE_TYPE_NODE) && + ((cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE) || + (cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_PI_NODE) || + (cur->type == XML_COMMENT_NODE) || + (cur->type == XML_CDATA_SECTION_NODE) || + (cur->type == XML_TEXT_NODE)))) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } + break; + case NODE_TEST_PI: + if (cur->type == XML_PI_NODE) { + if ((name != NULL) && + (!xmlStrEqual(name, cur->name))) + break; +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } + break; + case NODE_TEST_ALL: + if (axis == AXIS_ATTRIBUTE) { + if (cur->type == XML_ATTRIBUTE_NODE) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } + } else if (axis == AXIS_NAMESPACE) { + if (cur->type == XML_NAMESPACE_DECL) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } + } else { + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_DOCUMENT_NODE) || + (cur->type == XML_HTML_DOCUMENT_NODE)) { + if (prefix == NULL) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } else if ((cur->ns != NULL) && + (xmlStrEqual(prefix, + cur->ns->href))) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } + } + } + break; + case NODE_TEST_NS: { + TODO; + break; + } + case NODE_TEST_NAME: + switch (cur->type) { + case XML_ELEMENT_NODE: + if (xmlStrEqual(name, cur->name)) { + if (prefix == NULL) { + if ((cur->ns == NULL) || + (cur->ns->prefix == NULL)) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } + } else { + if ((cur->ns != NULL) && + (xmlStrEqual(prefix, + cur->ns->href))) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, cur); + } + } + } + break; + case XML_ATTRIBUTE_NODE: { + xmlAttrPtr attr = (xmlAttrPtr) cur; + if (xmlStrEqual(name, attr->name)) { + if (prefix == NULL) { + if ((attr->ns == NULL) || + (attr->ns->prefix == NULL)) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, (xmlNodePtr) attr); + } + } else { + if ((attr->ns != NULL) && + (xmlStrEqual(prefix, + attr->ns->href))) { +#ifdef DEBUG_STEP + n++; +#endif + addNode(list, (xmlNodePtr) attr); + } + } + } + break; + } + case XML_NAMESPACE_DECL: { + TODO; + break; + } + default: + break; + } + break; + } + } while (cur != NULL); + + /* + * If there is some predicate filtering do it now + */ + if (op->ch2 != -1) { + xmlXPathObjectPtr obj2; + + valuePush(ctxt, xmlXPathWrapNodeSet(list)); + xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]); + CHECK_TYPE(XPATH_NODESET); + obj2 = valuePop(ctxt); + list = obj2->nodesetval; + obj2->nodesetval = NULL; + xmlXPathFreeObject(obj2); + } + if (ret == NULL) { + ret = list; + } else { + ret = xmlXPathNodeSetMerge(ret, list); + xmlXPathFreeNodeSet(list); + } + } + ctxt->context->node = tmp; +#ifdef DEBUG_STEP + xmlGenericError(xmlGenericErrorContext, + "\nExamined %d nodes, found %d nodes at that step\n", t, n); +#endif + xmlXPathFreeObject(obj); + valuePush(ctxt, xmlXPathWrapNodeSet(ret)); +} + /** * xmlXPathCompOpEval: * @ctxt: the XPath parser context with the compiled expression @@ -6929,12 +6982,14 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) { xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); ctxt->context->node = NULL; return; - case XPATH_OP_COLLECT: - if (op->ch1 != -1) - xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); - xmlXPathNodeCollectAndTest(ctxt, op->value, op->value2, - op->value3, op->value4, op->value5); + case XPATH_OP_COLLECT: { + if (op->ch1 == -1) + return; + + xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]); + xmlXPathNodeCollectAndTest(ctxt, op); return; + } case XPATH_OP_VALUE: valuePush(ctxt, xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4)); @@ -6995,7 +7050,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op) { if (op->ch2 != -1) xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]); return; - case XPATH_OP_PREDICATE: { + case XPATH_OP_PREDICATE: + case XPATH_OP_FILTER: { xmlXPathObjectPtr res; xmlXPathObjectPtr obj, tmp; xmlNodeSetPtr newset = NULL; @@ -7315,6 +7371,8 @@ xmlXPathEvalPredicate(xmlXPathContextPtr ctxt, xmlXPathObjectPtr res) { return(res->floatval == ctxt->proximityPosition); case XPATH_NODESET: case XPATH_XSLT_TREE: + if (res->nodesetval == NULL) + return(0); return(res->nodesetval->nodeNr != 0); case XPATH_STRING: return((res->stringval != NULL) &&