diff --git a/ChangeLog b/ChangeLog index 23a079ab..8ce8e95e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Sun Oct 1 16:28:22 CEST 2000 Daniel Veillard + + * xpath.[ch] : fixed some serious XPath Predicate evaluation + problems + * Makefile.am : added XPath regression tests to normal tests + * uri.c: fixed a problem with local paths, cleanup + * parser.c: fixed a problem with large CData sections + Sat Sep 30 16:35:54 CEST 2000 Daniel Veillard * configure.in xml-config.in: patch from "Ben Taylor" diff --git a/Makefile.am b/Makefile.am index 513e1274..cecc284a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -99,9 +99,9 @@ install-data: $(srcdir)/libxml $(libxml_la_SOURCES): $(srcdir)/libxml -testall : tests SVGtests SAXtests XPathtests +testall : tests SVGtests SAXtests -tests: XMLtests XMLenttests HTMLtests Validtests URItests +tests: XMLtests XMLenttests HTMLtests Validtests URItests XPathtests HTMLtests : testHTML @(rm -f .memdump ; touch .memdump) diff --git a/include/libxml/xpath.h b/include/libxml/xpath.h index fc567fd8..0efe4668 100644 --- a/include/libxml/xpath.h +++ b/include/libxml/xpath.h @@ -45,17 +45,19 @@ struct _xmlNodeSet { * @@ XPointer will add more types ! */ -#define XPATH_UNDEFINED 0 -#define XPATH_NODESET 1 -#define XPATH_BOOLEAN 2 -#define XPATH_NUMBER 3 -#define XPATH_STRING 4 -#define XPATH_USERS 5 +typedef enum { + XPATH_UNDEFINED = 0, + XPATH_NODESET = 1, + XPATH_BOOLEAN = 2, + XPATH_NUMBER = 3, + XPATH_STRING = 4, + XPATH_USERS = 5 +} xmlXPathObjectType; typedef struct _xmlXPathObject xmlXPathObject; typedef xmlXPathObject *xmlXPathObjectPtr; struct _xmlXPathObject { - int type; + xmlXPathObjectType type; xmlNodeSetPtr nodesetval; int boolval; double floatval; @@ -163,6 +165,10 @@ struct _xmlXPathContext { xmlNsPtr *namespaces; /* The namespaces lookup */ int nsNr; /* the current Namespace index */ void *user; /* user defined extra info */ + + /* extra variables */ + int contextSize; /* the context size */ + int proximityPosition; /* the proximity position */ }; /* diff --git a/parser.c b/parser.c index 33a3dce1..83e23501 100644 --- a/parser.c +++ b/parser.c @@ -6014,6 +6014,7 @@ xmlParseCDSect(xmlParserCtxtPtr ctxt) { int r, rl; int s, sl; int cur, l; + int count = 0; if ((NXT(0) == '<') && (NXT(1) == '!') && (NXT(2) == '[') && (NXT(3) == 'C') && @@ -6070,6 +6071,11 @@ xmlParseCDSect(xmlParserCtxtPtr ctxt) { rl = sl; s = cur; sl = l; + count++; + if (count > 50) { + GROW; + count = 0; + } NEXTL(l); cur = CUR_CHAR(l); } diff --git a/uri.c b/uri.c index 48b15063..7166c5d9 100644 --- a/uri.c +++ b/uri.c @@ -1372,16 +1372,11 @@ xmlNormalizeURIPath(char *path) { xmlChar * xmlBuildURI(const xmlChar *URI, const xmlChar *base) { xmlChar *val = NULL; - int ret, len, index, cur, out; + int ret, ret2, len, index, cur, out; xmlURIPtr ref = NULL; xmlURIPtr bas = NULL; xmlURIPtr res = NULL; - if ((URI == NULL) && (base == NULL)) - return(NULL); - if (URI == NULL) - return((xmlChar *) xmlMemStrdup((const char *) base)); - /* * 1) The URI reference is parsed into the potential four components and * fragment identifier, as described in Section 4.3. @@ -1390,20 +1385,43 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { * as a reference to "." rather than as a synonym for the current * URI. Should we do that here? */ - ref = xmlCreateURI(); - if (ref == NULL) - goto done; - if (*URI) { - ret = xmlParseURIReference(ref, (const char *) URI); - if (ret != 0) + if (URI == NULL) + ret = -1; + else { + ref = xmlCreateURI(); + if (ref == NULL) goto done; + if (*URI) + ret = xmlParseURIReference(ref, (const char *) URI); + else + ret = -1; } - bas = xmlCreateURI(); - if (bas == NULL) + if (base == NULL) + ret2 = -1; + else { + bas = xmlCreateURI(); + if (bas == NULL) + goto done; + ret2 = xmlParseURIReference(bas, (const char *) base); + } + if ((ret != 0) && (ret2 != 0)) goto done; - ret = xmlParseURIReference(bas, (const char *) base); - if (ret != 0) + if (ret != 0) { + /* + * the base fragment must be ignored + */ + if (bas->fragment != NULL) { + xmlFree(bas->fragment); + bas->fragment = NULL; + } + val = xmlSaveUri(bas); goto done; + } + if (ret2 != 0) { + val = xmlSaveUri(ref); + goto done; + } + /* * 2) If the path component is empty and the scheme, authority, and @@ -1552,7 +1570,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { /* * Ensure the path includes a '/' */ - if (out == 0) + if ((out == 0) && (bas->server != NULL)) res->path[out++] = '/'; while (ref->path[index] != 0) { res->path[out++] = ref->path[index++]; diff --git a/xpath.c b/xpath.c index 7826a72b..753576db 100644 --- a/xpath.c +++ b/xpath.c @@ -262,6 +262,8 @@ PUSH_AND_POP(xmlXPathObjectPtr, value) #define XPATH_INVALID_OPERAND 10 #define XPATH_INVALID_TYPE 11 #define XPATH_INVALID_ARITY 12 +#define XPATH_INVALID_CTXT_SIZE 13 +#define XPATH_INVALID_CTXT_POSITION 14 const char *xmlXPathErrorMessages[] = { "Ok", @@ -277,6 +279,8 @@ const char *xmlXPathErrorMessages[] = { "Invalid operand", "Invalid type", "Invalid number of arguments", + "Invalid context size", + "Invalid context position", }; /** @@ -835,6 +839,9 @@ xmlXPathNewContext(xmlDocPtr doc) { ret->namespaces = NULL; ret->user = NULL; ret->nsNr = 0; + + ret->contextSize = -1; + ret->proximityPosition = -1; return(ret); } @@ -2191,13 +2198,13 @@ xmlXPathRoot(xmlXPathParserContextPtr ctxt) { void xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { CHECK_ARITY(0); - if ((ctxt->context->nodelist == NULL) || - (ctxt->context->node == NULL) || - (ctxt->context->nodelist->nodeNr == 0)) { - valuePush(ctxt, xmlXPathNewFloat((double) 0)); + if (ctxt->context->contextSize > 0) { + valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize)); +#ifdef DEBUG_EXPR + fprintf(xmlXPathDebug, "last() : %d\n", ctxt->context->contextSize); +#endif } else { - valuePush(ctxt, - xmlXPathNewFloat((double) ctxt->context->nodelist->nodeNr)); + XP_ERROR(XPATH_INVALID_CTXT_SIZE); } } @@ -2212,21 +2219,17 @@ xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) { */ void xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) { - int i; - CHECK_ARITY(0); - if ((ctxt->context->nodelist == NULL) || - (ctxt->context->node == NULL) || - (ctxt->context->nodelist->nodeNr == 0)) { - valuePush(ctxt, xmlXPathNewFloat((double) 0)); + if (ctxt->context->proximityPosition > 0) { + valuePush(ctxt, + xmlXPathNewFloat((double) ctxt->context->proximityPosition)); +#ifdef DEBUG_EXPR + fprintf(xmlXPathDebug, "position() : %d\n", + ctxt->context->proximityPosition); +#endif + } else { + XP_ERROR(XPATH_INVALID_CTXT_POSITION); } - for (i = 0; i < ctxt->context->nodelist->nodeNr;i++) { - if (ctxt->context->node == ctxt->context->nodelist->nodeTab[i]) { - valuePush(ctxt, xmlXPathNewFloat((double) i + 1)); - return; - } - } - valuePush(ctxt, xmlXPathNewFloat((double) 0)); } /** @@ -4137,13 +4140,13 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) { */ int xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, - xmlXPathObjectPtr res, int index) { + xmlXPathObjectPtr res) { if (res == NULL) return(0); switch (res->type) { case XPATH_BOOLEAN: return(res->boolval); case XPATH_NUMBER: - return(res->floatval == index); + return(res->floatval == ctxt->context->proximityPosition); case XPATH_NODESET: return(res->nodesetval->nodeNr != 0); case XPATH_STRING: @@ -4162,6 +4165,15 @@ xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, * [8] Predicate ::= '[' PredicateExpr ']' * [9] PredicateExpr ::= Expr * + * --------------------- + * For each node in the node-set to be filtered, the PredicateExpr is + * evaluated with that node as the context node, with the number of nodes + * in the node-set as the context size, and with the proximity position + * of the node in the node-set with respect to the axis as the context + * position; if PredicateExpr evaluates to true for that node, the node + * is included in the new node-set; otherwise, it is not included. + * --------------------- + * * Parse and evaluate a predicate for all the elements of the * current node list. Then refine the list by removing all * nodes where the predicate is false. @@ -4169,8 +4181,9 @@ xmlXPathEvaluatePredicateResult(xmlXPathParserContextPtr ctxt, void xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) { const xmlChar *cur; - xmlXPathObjectPtr res, listHolder = NULL; + xmlXPathObjectPtr res; xmlNodeSetPtr newset = NULL; + xmlNodeSetPtr oldset; int i; SKIP_BLANKS; @@ -4179,59 +4192,75 @@ xmlXPathEvalPredicate(xmlXPathParserContextPtr ctxt) { } NEXT; SKIP_BLANKS; - if ((ctxt->context->nodelist == NULL) || - (ctxt->context->nodelist->nodeNr == 0)) { - ctxt->context->node = NULL; + + /* + * Extract the old set, and then evaluate the result of the + * expression for all the element in the set. use it to grow + * up a new set. + */ + oldset = ctxt->context->nodelist; + ctxt->context->nodelist = NULL; + ctxt->context->node = NULL; + + if ((oldset == NULL) || (oldset->nodeNr == 0)) { xmlXPathEvalExpr(ctxt); CHECK_ERROR; + ctxt->context->contextSize = 0; + ctxt->context->proximityPosition = 0; res = valuePop(ctxt); if (res != NULL) xmlXPathFreeObject(res); } else { + /* + * Save the expression pointer since we will have to evaluate + * it multiple times. Initialize the new set. + */ cur = ctxt->cur; newset = xmlXPathNodeSetCreate(NULL); - /* Create a copy of the current node set because it is important: */ - listHolder = xmlXPathNewNodeSetList(ctxt->context->nodelist); - - for (i = 0; i < ctxt->context->nodelist->nodeNr; i++) { + for (i = 0; i < oldset->nodeNr; i++) { ctxt->cur = cur; - ctxt->context->node = ctxt->context->nodelist->nodeTab[i]; - /* This nodeset is useful for the loop but no, longer necessary this iteration: */ - if (ctxt->context->nodelist != NULL) - xmlXPathFreeNodeSet(ctxt->context->nodelist); - - /* This line was missed out: */ - ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->node); + /* + * Run the evaluation with a node list made of a single item + * in the nodeset. + */ + ctxt->context->node = oldset->nodeTab[i]; + ctxt->context->nodelist = NULL; + ctxt->context->contextSize = oldset->nodeNr; + ctxt->context->proximityPosition = i + 1; xmlXPathEvalExpr(ctxt); CHECK_ERROR; + + /* + * The result of the evaluation need to be tested to + * decided whether the filter succeeded or not + */ res = valuePop(ctxt); - if (xmlXPathEvaluatePredicateResult(ctxt, res, i + 1)) - { - /* Add the current node as the result has proven correct: */ - xmlXPathNodeSetAdd(newset, listHolder->nodesetval->nodeTab[i]); - } + if (xmlXPathEvaluatePredicateResult(ctxt, res)) { + xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]); + } if (res != NULL) - xmlXPathFreeObject(res); + xmlXPathFreeObject(res); - /* Copy the contents of the temporary list back to the node list for the next round: */ - ctxt->context->nodelist = xmlXPathNewNodeSetList(listHolder->nodesetval)->nodesetval; + ctxt->context->node = NULL; } - if (ctxt->context->nodelist != NULL) - xmlXPathFreeNodeSet(ctxt->context->nodelist); - - /* Clean up after temporary variable holder: */ - if (listHolder != NULL) - xmlXPathFreeObject(listHolder); - + + /* + * The result is used as the new evaluation set. + */ ctxt->context->nodelist = newset; ctxt->context->node = NULL; + ctxt->context->contextSize = -1; + ctxt->context->proximityPosition = -1; } if (CUR != ']') { XP_ERROR(XPATH_INVALID_PREDICATE_ERROR); } + if (oldset != NULL) + xmlXPathFreeNodeSet(oldset); + NEXT; SKIP_BLANKS; #ifdef DEBUG_STEP diff --git a/xpath.h b/xpath.h index fc567fd8..0efe4668 100644 --- a/xpath.h +++ b/xpath.h @@ -45,17 +45,19 @@ struct _xmlNodeSet { * @@ XPointer will add more types ! */ -#define XPATH_UNDEFINED 0 -#define XPATH_NODESET 1 -#define XPATH_BOOLEAN 2 -#define XPATH_NUMBER 3 -#define XPATH_STRING 4 -#define XPATH_USERS 5 +typedef enum { + XPATH_UNDEFINED = 0, + XPATH_NODESET = 1, + XPATH_BOOLEAN = 2, + XPATH_NUMBER = 3, + XPATH_STRING = 4, + XPATH_USERS = 5 +} xmlXPathObjectType; typedef struct _xmlXPathObject xmlXPathObject; typedef xmlXPathObject *xmlXPathObjectPtr; struct _xmlXPathObject { - int type; + xmlXPathObjectType type; xmlNodeSetPtr nodesetval; int boolval; double floatval; @@ -163,6 +165,10 @@ struct _xmlXPathContext { xmlNsPtr *namespaces; /* The namespaces lookup */ int nsNr; /* the current Namespace index */ void *user; /* user defined extra info */ + + /* extra variables */ + int contextSize; /* the context size */ + int proximityPosition; /* the proximity position */ }; /*