Commit f09e7e35 authored by Daniel Veillard's avatar Daniel Veillard
Browse files

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
parent d2ade932
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"
......
......@@ -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)
......
......@@ -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 */
};
/*
......
......@@ -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);
}
......
......@@ -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++];
......
......@@ -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));
}
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;
}
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);
}
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
......
......@@ -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 */
};
/*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment