XPath fixes and cleanup, 2 general bug fixes:
- 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
Daniel
diff --git a/ChangeLog b/ChangeLog
index 23a079a..8ce8e95 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Sun Oct 1 16:28:22 CEST 2000 Daniel Veillard <Daniel.Veillard@w3.org>
+
+ * 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 <Daniel.Veillard@w3.org>
* configure.in xml-config.in: patch from "Ben Taylor"
diff --git a/Makefile.am b/Makefile.am
index 513e127..cecc284 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -99,9 +99,9 @@
$(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 fc567fd..0efe466 100644
--- a/include/libxml/xpath.h
+++ b/include/libxml/xpath.h
@@ -45,17 +45,19 @@
* @@ 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 @@
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 33a3dce..83e2350 100644
--- a/parser.c
+++ b/parser.c
@@ -6014,6 +6014,7 @@
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 @@
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 48b1506..7166c5d 100644
--- a/uri.c
+++ b/uri.c
@@ -1372,16 +1372,11 @@
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 @@
* 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 @@
/*
* 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 7826a72..753576d 100644
--- a/xpath.c
+++ b/xpath.c
@@ -262,6 +262,8 @@
#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 @@
"Invalid operand",
"Invalid type",
"Invalid number of arguments",
+ "Invalid context size",
+ "Invalid context position",
};
/**
@@ -835,6 +839,9 @@
ret->namespaces = NULL;
ret->user = NULL;
ret->nsNr = 0;
+
+ ret->contextSize = -1;
+ ret->proximityPosition = -1;
return(ret);
}
@@ -2191,13 +2198,13 @@
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 @@
*/
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 @@
*/
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 @@
* [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 @@
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 @@
}
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 fc567fd..0efe466 100644
--- a/xpath.h
+++ b/xpath.h
@@ -45,17 +45,19 @@
* @@ 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 @@
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 */
};
/*