Release 1.6, lot of fixes, more validation, code cleanup, added namespace
on attributes, Daniel.
diff --git a/xpath.c b/xpath.c
index cff0e82..9fc3b16 100644
--- a/xpath.c
+++ b/xpath.c
@@ -28,7 +28,9 @@
#include <nan.h>
#endif
#include <stdio.h>
+#include <ctype.h>
#include "tree.h"
+#include "valid.h"
#include "xpath.h"
#include "parserInternals.h"
@@ -151,6 +153,7 @@
__FILE__, __LINE__);
double xmlXPathStringEvalNumber(const CHAR *str);
+void xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs);
/************************************************************************
* *
@@ -280,8 +283,8 @@
xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file,
int line, int no) {
int n;
- const char *cur;
- const char *base;
+ const CHAR *cur;
+ const CHAR *base;
fprintf(xmlXPathDebug, "Error %s:%d: %s\n", file, line,
xmlXPathErrorMessages[no]);
@@ -411,6 +414,7 @@
fprintf(xmlXPathDebug, "xmlXPathNodeSetAdd: out of memory\n");
return;
}
+ cur->nodeTab = temp;
}
cur->nodeTab[cur->nodeNr++] = val;
}
@@ -514,7 +518,7 @@
free(obj);
}
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_STEP)
/**
* xmlXPathDebugNodeSet:
* @output: a FILE * for the output
@@ -636,7 +640,7 @@
}
/**
- * xmlXPathNewBoolean:
+ * xmlXPathNewString:
* @val: the CHAR * value
*
* Create a new xmlXPathObjectPtr of type string and of value @val
@@ -659,6 +663,29 @@
}
/**
+ * xmlXPathNewCString:
+ * @val: the char * value
+ *
+ * Create a new xmlXPathObjectPtr of type string and of value @val
+ *
+ * Returns the newly created object.
+ */
+xmlXPathObjectPtr
+xmlXPathNewCString(const char *val) {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr) malloc(sizeof(xmlXPathObject));
+ if (ret == NULL) {
+ fprintf(xmlXPathDebug, "xmlXPathNewFloat: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
+ ret->type = XPATH_STRING;
+ ret->stringval = xmlStrdup(BAD_CAST val);
+ return(ret);
+}
+
+/**
* xmlXPathNewNodeSet:
* @val: the NodePtr value
*
@@ -736,15 +763,13 @@
* @doc: the XML document
* @variables: the variable list
* @functions: the function list
- * @namespaces: the namespace list
*
* Create a new xmlXPathContext
*
* Returns the xmlXPathContext just allocated.
*/
xmlXPathContextPtr
-xmlXPathNewContext(xmlDocPtr doc, void *variables, void *functions,
- void *namespaces) {
+xmlXPathNewContext(xmlDocPtr doc, void *variables, void *functions) {
xmlXPathContextPtr ret;
ret = (xmlXPathContextPtr) malloc(sizeof(xmlXPathContext));
@@ -756,7 +781,8 @@
ret->doc = doc;
ret->variables = variables;
ret->functions = functions;
- ret->namespaces = namespaces;
+ ret->namespaces = NULL;
+ ret->nsNr = 0;
return(ret);
}
@@ -768,6 +794,9 @@
*/
void
xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
+ if (ctxt->namespaces != NULL)
+ free(ctxt->namespaces);
+
#ifdef DEBUG
memset(ctxt, 0xB , (size_t) sizeof(xmlXPathContext));
#endif
@@ -861,12 +890,6 @@
************************************************************************/
/*
- * TODO: check the semantic for all these operations in case of operands
- * with different types, Cast function probably need to be provided
- * to simplify the coding.
- */
-
-/*
* Auto-pop and cast to a number
*/
void xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs);
@@ -937,7 +960,7 @@
*/
int
xmlXPathEqualNodeSetFloat(xmlXPathObjectPtr arg, float f) {
- CHAR buf[100] = "";
+ char buf[100] = "";
if ((arg == NULL) || (arg->type != XPATH_NODESET))
return(0);
@@ -951,7 +974,7 @@
else
sprintf(buf, "%0g", f);
- return(xmlXPathEqualNodeSetString(arg, buf));
+ return(xmlXPathEqualNodeSetString(arg, BAD_CAST buf));
}
@@ -1393,6 +1416,8 @@
*
* Traversal function for the "self" direction
* he self axis contains just the context node itself
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1408,6 +1433,8 @@
*
* Traversal function for the "child" direction
* The child axis contains the children of the context node in document order.
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1429,6 +1456,8 @@
* Traversal function for the "descendant" direction
* the descendant axis contains the descendants of the context node in document
* order; a descendant is a child or a child of a child and so on.
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextDescendant(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1463,6 +1492,8 @@
* of the context node in document order; thus the context node is the first
* node on the axis, and the first child of the context node is the second node
* on the axis
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextDescendantOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1493,6 +1524,8 @@
*
* Traversal function for the "parent" direction
* The parent axis contains the parent of the context node, if there is one.
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextParent(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1520,6 +1553,8 @@
* parent and so on; the nodes are ordered in reverse document order; thus the
* parent is the first node on the axis, and the parent's parent is the second
* node on the axis
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextAncestor(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1546,10 +1581,12 @@
* @cur: the current node in the traversal
*
* Traversal function for the "ancestor-or-self" direction
- * he ancestor-or-self axis contains the context node and ancestors of the context
- * node in reverse document order; thus the context node is the first node on the
- * axis, and the context node's parent the second; parent here is defined the same
- * as with the parent axis.
+ * he ancestor-or-self axis contains the context node and ancestors of
+ * the context node in reverse document order; thus the context node is
+ * the first node on the axis, and the context node's parent the second;
+ * parent here is defined the same as with the parent axis.
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextAncestorOrSelf(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1575,6 +1612,8 @@
* Traversal function for the "following-sibling" direction
* The following-sibling axis contains the following siblings of the context
* node in document order.
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextFollowingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1594,6 +1633,8 @@
* The preceding-sibling axis contains the preceding siblings of the context
* node in reverse document order; the first preceding sibling is first on the
* axis; the sibling preceding that node is the second on the axis and so on.
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextPrecedingSibling(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1614,6 +1655,8 @@
* node that are after the context node in document order, excluding any
* descendants and excluding attribute nodes and namespace nodes; the nodes
* are ordered in document order
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextFollowing(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1646,6 +1689,8 @@
* node that are before the context node in document order, excluding any
* ancestors and excluding attribute nodes and namespace nodes; the nodes are
* ordered in reverse document order
+ *
+ * Returns the next element following that axis
*/
xmlNodePtr
xmlXPathNextPreceding(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
@@ -1677,11 +1722,20 @@
* the namespace axis contains the namespace nodes of the context node;
* the order of nodes on this axis is implementation-defined; the axis will
* be empty unless the context node is an element
+ *
+ * Returns the next element following that axis
*/
-xmlAttrPtr
+xmlNsPtr
xmlXPathNextNamespace(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
- TODO /* namespace traversal */
- return(NULL);
+ if ((cur == NULL) || (ctxt->context->namespaces == NULL)) {
+ if (ctxt->context->namespaces != NULL)
+ free(ctxt->context->namespaces);
+ ctxt->context->namespaces =
+ xmlGetNsList(ctxt->context->doc, ctxt->context->node);
+ if (ctxt->context->namespaces == NULL) return(NULL);
+ ctxt->context->nsNr = 0;
+ }
+ return(ctxt->context->namespaces[ctxt->context->nsNr++]);
}
/**
@@ -1690,6 +1744,8 @@
* @cur: the current attribute in the traversal
*
* Traversal function for the "attribute" direction
+ *
+ * Returns the next element following that axis
*/
xmlAttrPtr
xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlAttrPtr cur) {
@@ -1771,7 +1827,7 @@
#ifdef DEBUG_STEP
fprintf(xmlXPathDebug, "axis 'attributes' ");
#endif
- TODO /* attribute axis */
+ next = (xmlXPathTraversalFunction) xmlXPathNextAttribute; break;
break;
case AXIS_CHILD:
#ifdef DEBUG_STEP
@@ -1802,7 +1858,7 @@
#ifdef DEBUG_STEP
fprintf(xmlXPathDebug, "axis 'namespace' ");
#endif
- TODO /* namespace axis */
+ next = (xmlXPathTraversalFunction) xmlXPathNextNamespace; break;
break;
case AXIS_PARENT:
#ifdef DEBUG_STEP
@@ -1839,7 +1895,6 @@
break;
case NODE_TEST_PI:
fprintf(xmlXPathDebug, " seaching for PI !!!\n");
- TODO /* PI search */
break;
case NODE_TEST_ALL:
fprintf(xmlXPathDebug, " seaching for *\n");
@@ -1881,19 +1936,30 @@
}
break;
case NODE_TEST_PI:
- TODO /* PI search */
+ if (cur->type == XML_PI_NODE) {
+ if ((name != NULL) &&
+ (xmlStrcmp(name, cur->name)))
+ break;
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ xmlXPathNodeSetAdd(ret, cur);
+ }
break;
case NODE_TEST_ALL:
- if (cur->type == XML_ELEMENT_NODE) {
+ if ((cur->type == XML_ELEMENT_NODE) ||
+ (cur->type == XML_ATTRIBUTE_NODE)) {
+ /* !!! || (cur->type == XML_TEXT_NODE)) { */
#ifdef DEBUG_STEP
n++;
#endif
xmlXPathNodeSetAdd(ret, cur);
}
break;
- case NODE_TEST_NS:
- TODO /* NS search */
+ case NODE_TEST_NS: {
+ TODO /* namespace search */
break;
+ }
case NODE_TEST_NAME:
switch (cur->type) {
case XML_ELEMENT_NODE:
@@ -1906,6 +1972,17 @@
#endif
xmlXPathNodeSetAdd(ret, cur);
}
+ break;
+ case XML_ATTRIBUTE_NODE: {
+ xmlAttrPtr attr = (xmlAttrPtr) cur;
+ if (!xmlStrcmp(name, attr->name)) {
+#ifdef DEBUG_STEP
+ n++;
+#endif
+ xmlXPathNodeSetAdd(ret, cur);
+ }
+ break;
+ }
default:
break;
}
@@ -1942,20 +2019,6 @@
ctxt->context->nodelist = xmlXPathNodeSetCreate(ctxt->context->node);
}
-/**
- * xmlXPathSearchPI:
- * @ctxt: the XPath Parser context
- *
- * Search a processing instruction by name. The name is
- * in the object on the stack.
- */
-void
-xmlXPathSearchPI(xmlXPathParserContextPtr ctxt, int nargs) {
- TODO /* Search PI */
- CHECK_ARITY(0);
- CHECK_TYPE(XPATH_NUMBER)
-}
-
/************************************************************************
* *
* The explicit core function library *
@@ -2048,8 +2111,64 @@
*/
void
xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
+ const CHAR *tokens;
+ const CHAR *cur;
+ CHAR *ID;
+ xmlAttrPtr attr;
+ xmlNodePtr elem = NULL;
+ xmlXPathObjectPtr ret, obj;
+
CHECK_ARITY(1);
- TODO /* Write ID/IDREF support in libxml first */
+ obj = valuePop(ctxt);
+ if (obj == NULL) ERROR(XPATH_INVALID_OPERAND);
+ if (obj->type == XPATH_NODESET) {
+ TODO /* ID function in case of NodeSet */
+ }
+ if (obj->type != XPATH_STRING) {
+ valuePush(ctxt, obj);
+ xmlXPathStringFunction(ctxt, 1);
+ obj = valuePop(ctxt);
+ if (obj->type != XPATH_STRING) {
+ xmlXPathFreeObject(obj);
+ return;
+ }
+ }
+ tokens = obj->stringval;
+
+ ret = xmlXPathNewNodeSet(NULL);
+ valuePush(ctxt, ret);
+ if (tokens == NULL) {
+ xmlXPathFreeObject(obj);
+ return;
+ }
+
+ cur = tokens;
+
+ while (IS_BLANK(*cur)) cur++;
+ while (*cur != 0) {
+ while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
+ (*cur == '.') || (*cur == '-') ||
+ (*cur == '_') || (*cur == ':') ||
+ (IS_COMBINING(*cur)) ||
+ (IS_EXTENDER(*cur)))
+ cur++;
+
+ if ((!IS_BLANK(*cur)) && (*cur != 0)) break;
+
+ ID = xmlStrndup(tokens, cur - tokens);
+ attr = xmlGetID(ctxt->context->doc, ID);
+ if (attr != NULL) {
+ elem = attr->node;
+ xmlXPathNodeSetAdd(ret->nodesetval, elem);
+ }
+ if (ID != NULL)
+ free(ID);
+
+ while (IS_BLANK(*cur)) cur++;
+ tokens = cur;
+ }
+ xmlXPathFreeObject(obj);
+ return;
}
/**
@@ -2072,7 +2191,7 @@
cur = valuePop(ctxt);
if (cur->nodesetval->nodeNr == 0) {
- valuePush(ctxt, xmlXPathNewString(""));
+ valuePush(ctxt, xmlXPathNewCString(""));
} else {
int i = 0; /* Should be first in document order !!!!! */
valuePush(ctxt, xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
@@ -2095,17 +2214,21 @@
xmlXPathNamespaceFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathObjectPtr cur;
+ if (nargs == 0) {
+ valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ nargs = 1;
+ }
CHECK_ARITY(1);
CHECK_TYPE(XPATH_NODESET);
cur = valuePop(ctxt);
if (cur->nodesetval->nodeNr == 0) {
- valuePush(ctxt, xmlXPathNewString(""));
+ valuePush(ctxt, xmlXPathNewCString(""));
} else {
int i = 0; /* Should be first in document order !!!!! */
if (cur->nodesetval->nodeTab[i]->ns == NULL)
- valuePush(ctxt, xmlXPathNewString(""));
+ valuePush(ctxt, xmlXPathNewCString(""));
else
valuePush(ctxt, xmlXPathNewString(
cur->nodesetval->nodeTab[i]->ns->href));
@@ -2142,7 +2265,7 @@
cur = valuePop(ctxt);
if (cur->nodesetval->nodeNr == 0) {
- valuePush(ctxt, xmlXPathNewString(""));
+ valuePush(ctxt, xmlXPathNewCString(""));
} else {
int i = 0; /* Should be first in document order !!!!! */
@@ -2151,10 +2274,11 @@
cur->nodesetval->nodeTab[i]->name));
else {
- CHAR name[2000];
- sprintf(name, "%s:%s", cur->nodesetval->nodeTab[i]->ns->prefix,
- cur->nodesetval->nodeTab[i]->name);
- valuePush(ctxt, xmlXPathNewString(name));
+ char name[2000];
+ sprintf(name, "%s:%s",
+ (char *) cur->nodesetval->nodeTab[i]->ns->prefix,
+ (char *) cur->nodesetval->nodeTab[i]->name);
+ valuePush(ctxt, xmlXPathNewCString(name));
}
}
xmlXPathFreeObject(cur);
@@ -2201,7 +2325,7 @@
switch (cur->type) {
case XPATH_NODESET:
if (cur->nodesetval->nodeNr == 0) {
- valuePush(ctxt, xmlXPathNewString(""));
+ valuePush(ctxt, xmlXPathNewCString(""));
} else {
CHAR *res;
int i = 0; /* Should be first in document order !!!!! */
@@ -2215,12 +2339,12 @@
valuePush(ctxt, cur);
return;
case XPATH_BOOLEAN:
- if (cur->boolval) valuePush(ctxt, xmlXPathNewString("true"));
- else valuePush(ctxt, xmlXPathNewString("false"));
+ if (cur->boolval) valuePush(ctxt, xmlXPathNewCString("true"));
+ else valuePush(ctxt, xmlXPathNewCString("false"));
xmlXPathFreeObject(cur);
return;
case XPATH_NUMBER: {
- CHAR buf[100];
+ char buf[100];
if (isnan(cur->floatval))
sprintf(buf, "NaN");
@@ -2230,7 +2354,7 @@
sprintf(buf, "-Infinity");
else
sprintf(buf, "%0g", cur->floatval);
- valuePush(ctxt, xmlXPathNewString(buf));
+ valuePush(ctxt, xmlXPathNewCString(buf));
xmlXPathFreeObject(cur);
return;
}
@@ -2454,7 +2578,7 @@
ret = xmlStrsub(str->stringval, i, l);
if (ret == NULL)
- valuePush(ctxt, xmlXPathNewString(""));
+ valuePush(ctxt, xmlXPathNewCString(""));
else {
valuePush(ctxt, xmlXPathNewString(ret));
free(ret);
@@ -2645,8 +2769,26 @@
*/
void
xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
- CHECK_ARITY(0);
- TODO /* Write down xml:lang support in libxml first */
+ xmlXPathObjectPtr val;
+ const CHAR *theLang;
+ const CHAR *lang;
+ int ret = 0;
+ int i;
+
+ CHECK_ARITY(1);
+ CHECK_TYPE(XPATH_STRING);
+ val = valuePop(ctxt);
+ lang = val->stringval;
+ theLang = xmlNodeGetLang(ctxt->context->node);
+ if ((theLang != NULL) && (lang != NULL)) {
+ for (i = 0;lang[i] != 0;i++)
+ if (toupper(lang[i]) != toupper(theLang[i]))
+ goto not_equal;
+ ret = 1;
+ }
+not_equal:
+ xmlXPathFreeObject(val);
+ valuePush(ctxt, xmlXPathNewBoolean(ret));
}
/**
@@ -2936,7 +3078,7 @@
* [29] Literal ::= '"' [^"]* '"'
* | "'" [^']* "'"
*
- * TODO: memory allocation could be improved.
+ * TODO: xmlXPathEvalLiteral memory allocation could be improved.
*/
void
xmlXPathEvalLiteral(xmlXPathParserContextPtr ctxt) {
@@ -3022,85 +3164,85 @@
*
* [35] FunctionName ::= QName - NodeType
*
- * TODO: for the moment the list is hardcoded from the spec !!!!
+ * TODO: for the moment the function list is hardcoded from the spec !!!!
*
* Returns the xmlXPathFunction if found, or NULL otherwise
*/
xmlXPathFunction
-xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, CHAR *name) {
+xmlXPathIsFunction(xmlXPathParserContextPtr ctxt, const CHAR *name) {
switch (name[0]) {
case 'b':
- if (!xmlStrcmp(name, "boolean"))
+ if (!xmlStrcmp(name, BAD_CAST "boolean"))
return(xmlXPathBooleanFunction);
break;
case 'c':
- if (!xmlStrcmp(name, "ceiling"))
+ if (!xmlStrcmp(name, BAD_CAST "ceiling"))
return(xmlXPathCeilingFunction);
- if (!xmlStrcmp(name, "count"))
+ if (!xmlStrcmp(name, BAD_CAST "count"))
return(xmlXPathCountFunction);
- if (!xmlStrcmp(name, "concat"))
+ if (!xmlStrcmp(name, BAD_CAST "concat"))
return(xmlXPathConcatFunction);
- if (!xmlStrcmp(name, "contains"))
+ if (!xmlStrcmp(name, BAD_CAST "contains"))
return(xmlXPathContainsFunction);
break;
case 'i':
- if (!xmlStrcmp(name, "id"))
+ if (!xmlStrcmp(name, BAD_CAST "id"))
return(xmlXPathIdFunction);
break;
case 'f':
- if (!xmlStrcmp(name, "false"))
+ if (!xmlStrcmp(name, BAD_CAST "false"))
return(xmlXPathFalseFunction);
- if (!xmlStrcmp(name, "floor"))
+ if (!xmlStrcmp(name, BAD_CAST "floor"))
return(xmlXPathFloorFunction);
break;
case 'l':
- if (!xmlStrcmp(name, "last"))
+ if (!xmlStrcmp(name, BAD_CAST "last"))
return(xmlXPathLastFunction);
- if (!xmlStrcmp(name, "lang"))
+ if (!xmlStrcmp(name, BAD_CAST "lang"))
return(xmlXPathLangFunction);
- if (!xmlStrcmp(name, "local-part"))
+ if (!xmlStrcmp(name, BAD_CAST "local-part"))
return(xmlXPathLocalPartFunction);
break;
case 'n':
- if (!xmlStrcmp(name, "not"))
+ if (!xmlStrcmp(name, BAD_CAST "not"))
return(xmlXPathNotFunction);
- if (!xmlStrcmp(name, "name"))
+ if (!xmlStrcmp(name, BAD_CAST "name"))
return(xmlXPathNameFunction);
- if (!xmlStrcmp(name, "namespace"))
+ if (!xmlStrcmp(name, BAD_CAST "namespace"))
return(xmlXPathNamespaceFunction);
- if (!xmlStrcmp(name, "normalize"))
+ if (!xmlStrcmp(name, BAD_CAST "normalize"))
return(xmlXPathNormalizeFunction);
- if (!xmlStrcmp(name, "number"))
+ if (!xmlStrcmp(name, BAD_CAST "number"))
return(xmlXPathNumberFunction);
break;
case 'p':
- if (!xmlStrcmp(name, "position"))
+ if (!xmlStrcmp(name, BAD_CAST "position"))
return(xmlXPathPositionFunction);
break;
case 'r':
- if (!xmlStrcmp(name, "round"))
+ if (!xmlStrcmp(name, BAD_CAST "round"))
return(xmlXPathRoundFunction);
break;
case 's':
- if (!xmlStrcmp(name, "string"))
+ if (!xmlStrcmp(name, BAD_CAST "string"))
return(xmlXPathStringFunction);
- if (!xmlStrcmp(name, "string-length"))
+ if (!xmlStrcmp(name, BAD_CAST "string-length"))
return(xmlXPathStringLengthFunction);
- if (!xmlStrcmp(name, "starts-with"))
+ if (!xmlStrcmp(name, BAD_CAST "starts-with"))
return(xmlXPathStartsWithFunction);
- if (!xmlStrcmp(name, "substring"))
+ if (!xmlStrcmp(name, BAD_CAST "substring"))
return(xmlXPathSubstringFunction);
- if (!xmlStrcmp(name, "substring-before"))
+ if (!xmlStrcmp(name, BAD_CAST "substring-before"))
return(xmlXPathSubstringBeforeFunction);
- if (!xmlStrcmp(name, "substring-after"))
+ if (!xmlStrcmp(name, BAD_CAST "substring-after"))
return(xmlXPathSubstringAfterFunction);
- if (!xmlStrcmp(name, "sum"))
+ if (!xmlStrcmp(name, BAD_CAST "sum"))
return(xmlXPathSumFunction);
break;
case 't':
- if (!xmlStrcmp(name, "true"))
+ if (!xmlStrcmp(name, BAD_CAST "true"))
return(xmlXPathTrueFunction);
- if (!xmlStrcmp(name, "translate"))
+ if (!xmlStrcmp(name, BAD_CAST "translate"))
return(xmlXPathTranslateFunction);
break;
}
@@ -3134,40 +3276,46 @@
* | 'node'
*/
int
-xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, CHAR *name) {
+xmlXPathGetNameType(xmlXPathParserContextPtr ctxt, const CHAR *name) {
switch (name[0]) {
case 'a':
- if (!xmlStrcmp(name, "ancestor")) return(AXIS_ANCESTOR);
- if (!xmlStrcmp(name, "ancestor-or-self")) return(AXIS_ANCESTOR_OR_SELF);
- if (!xmlStrcmp(name, "attribute")) return(AXIS_ATTRIBUTE);
+ if (!xmlStrcmp(name, BAD_CAST "ancestor")) return(AXIS_ANCESTOR);
+ if (!xmlStrcmp(name, BAD_CAST "ancestor-or-self"))
+ return(AXIS_ANCESTOR_OR_SELF);
+ if (!xmlStrcmp(name, BAD_CAST "attribute")) return(AXIS_ATTRIBUTE);
break;
case 'c':
- if (!xmlStrcmp(name, "child")) return(AXIS_CHILD);
- if (!xmlStrcmp(name, "comment")) return(NODE_TYPE_COMMENT);
+ if (!xmlStrcmp(name, BAD_CAST "child")) return(AXIS_CHILD);
+ if (!xmlStrcmp(name, BAD_CAST "comment")) return(NODE_TYPE_COMMENT);
break;
case 'd':
- if (!xmlStrcmp(name, "descendant")) return(AXIS_DESCENDANT);
- if (!xmlStrcmp(name, "descendant-or-self")) return(AXIS_DESCENDANT_OR_SELF);
+ if (!xmlStrcmp(name, BAD_CAST "descendant"))
+ return(AXIS_DESCENDANT);
+ if (!xmlStrcmp(name, BAD_CAST "descendant-or-self"))
+ return(AXIS_DESCENDANT_OR_SELF);
break;
case 'f':
- if (!xmlStrcmp(name, "following")) return(AXIS_FOLLOWING);
- if (!xmlStrcmp(name, "following-sibling")) return(AXIS_FOLLOWING_SIBLING);
+ if (!xmlStrcmp(name, BAD_CAST "following")) return(AXIS_FOLLOWING);
+ if (!xmlStrcmp(name, BAD_CAST "following-sibling"))
+ return(AXIS_FOLLOWING_SIBLING);
break;
case 'n':
- if (!xmlStrcmp(name, "namespace")) return(AXIS_NAMESPACE);
- if (!xmlStrcmp(name, "node")) return(NODE_TYPE_NODE);
+ if (!xmlStrcmp(name, BAD_CAST "namespace")) return(AXIS_NAMESPACE);
+ if (!xmlStrcmp(name, BAD_CAST "node")) return(NODE_TYPE_NODE);
break;
case 'p':
- if (!xmlStrcmp(name, "parent")) return(AXIS_PARENT);
- if (!xmlStrcmp(name, "preceding")) return(AXIS_PRECEDING);
- if (!xmlStrcmp(name, "preceding-sibling")) return(AXIS_PRECEDING_SIBLING);
- if (!xmlStrcmp(name, "processing-instruction")) return(NODE_TYPE_PI);
+ if (!xmlStrcmp(name, BAD_CAST "parent")) return(AXIS_PARENT);
+ if (!xmlStrcmp(name, BAD_CAST "preceding")) return(AXIS_PRECEDING);
+ if (!xmlStrcmp(name, BAD_CAST "preceding-sibling"))
+ return(AXIS_PRECEDING_SIBLING);
+ if (!xmlStrcmp(name, BAD_CAST "processing-instruction"))
+ return(NODE_TYPE_PI);
break;
case 's':
- if (!xmlStrcmp(name, "self")) return(AXIS_SELF);
+ if (!xmlStrcmp(name, BAD_CAST "self")) return(AXIS_SELF);
break;
case 't':
- if (!xmlStrcmp(name, "text")) return(NODE_TYPE_TEXT);
+ if (!xmlStrcmp(name, BAD_CAST "text")) return(NODE_TYPE_TEXT);
break;
}
if (xmlXPathIsFunction(ctxt, name)) return(IS_FUNCTION);
@@ -3293,6 +3441,55 @@
}
/**
+ * xmlXPathScanName:
+ * @ctxt: the XPath Parser context
+ *
+ * Trickery: parse an XML name but without consuming the input flow
+ * Needed for rollback cases.
+ *
+ * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
+ * CombiningChar | Extender
+ *
+ * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+ *
+ * [6] Names ::= Name (S Name)*
+ *
+ * Returns the Name parsed or NULL
+ */
+
+CHAR *
+xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
+ CHAR buf[XML_MAX_NAMELEN];
+ int len = 0;
+
+ if (!IS_LETTER(CUR) && (CUR != '_') &&
+ (CUR != ':')) {
+ return(NULL);
+ }
+
+ while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
+ (NXT(len) == '.') || (NXT(len) == '-') ||
+ (NXT(len) == '_') || (NXT(len) == ':') ||
+ (IS_COMBINING(NXT(len))) ||
+ (IS_EXTENDER(NXT(len)))) {
+ buf[len] = NXT(len);
+ len++;
+ if (len >= XML_MAX_NAMELEN) {
+ fprintf(stderr,
+ "xmlScanName: reached XML_MAX_NAMELEN limit\n");
+ while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
+ (NXT(len) == '.') || (NXT(len) == '-') ||
+ (NXT(len) == '_') || (NXT(len) == ':') ||
+ (IS_COMBINING(NXT(len))) ||
+ (IS_EXTENDER(NXT(len))))
+ len++;
+ break;
+ }
+ }
+ return(xmlStrndup(buf, len));
+}
+
+/**
* xmlXPathEvalPathExpr:
* @ctxt: the XPath Parser context
*
@@ -3314,13 +3511,6 @@
xmlXPathEvalPathExpr(xmlXPathParserContextPtr ctxt) {
xmlNodeSetPtr newset = NULL;
- /*
- * TODO: FilterExpr => PrimaryExpr => FunctionName is possible too
- * so we may have to parse a name here, and depending on whether
- * it's a function name or not parse a FilterExpr or a LocationPath
- * !!!!!!!!!!!! See NOTE1
- */
-
if ((CUR == '$') || (CUR == '(') || (IS_DIGIT(CUR)) ||
(CUR == '\'') || (CUR == '"')) {
xmlXPathEvalFilterExpr(ctxt);
@@ -3342,7 +3532,15 @@
xmlXPathEvalRelativeLocationPath(ctxt);
}
} else {
- xmlXPathEvalLocationPath(ctxt);
+ CHAR *name;
+
+ name = xmlXPathScanName(ctxt);
+ if ((name == NULL) || (!xmlXPathIsFunction(ctxt, name)))
+ xmlXPathEvalLocationPath(ctxt);
+ else
+ xmlXPathEvalFilterExpr(ctxt);
+ if (name != NULL)
+ free(name);
}
}
@@ -3393,8 +3591,7 @@
minus = 1;
NEXT;
}
- /* xmlXPathEvalUnionExpr(ctxt); NOTE1 !!! */
- xmlXPathEvalPrimaryExpr(ctxt);
+ xmlXPathEvalUnionExpr(ctxt);
CHECK_ERROR;
if (minus) {
xmlXPathValueFlipSign(ctxt);
@@ -3733,9 +3930,13 @@
int nodetest = NODE_TEST_NONE;
int nodetype = 0;
xmlNodeSetPtr newset = NULL;
+ int attribute = 0;
if (CUR == '@') {
- TODO /* attributes */
+ NEXT;
+ attribute = 1;
+ axis = AXIS_ATTRIBUTE;
+ goto parse_NodeTest;
} else if (CUR == '*') {
NEXT;
nodetest = NODE_TEST_ALL;
@@ -3746,6 +3947,53 @@
}
type = xmlXPathGetNameType(ctxt, name);
switch (type) {
+ case IS_FUNCTION: {
+ xmlXPathFunction func;
+ int nbargs = 0;
+ xmlXPathObjectPtr top;
+
+ top = ctxt->value;
+ func = xmlXPathIsFunction(ctxt, name);
+ if (func == NULL) {
+ free(name);
+ ERROR(XPATH_UNKNOWN_FUNC_ERROR);
+ }
+#ifdef DEBUG_EXPR
+ fprintf(xmlXPathDebug, "Calling function %s\n", name);
+#endif
+
+ if (CUR != '(') {
+ free(name);
+ ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+
+ while (CUR != ')') {
+ xmlXPathEvalExpr(ctxt);
+ nbargs++;
+ if (CUR == ')') break;
+ if (CUR != ',') {
+ free(name);
+ ERROR(XPATH_EXPR_ERROR);
+ }
+ NEXT;
+ }
+ NEXT;
+ free(name);
+ func(ctxt, nbargs);
+ if ((ctxt->value != top) &&
+ (ctxt->value != NULL) &&
+ (ctxt->value->type == XPATH_NODESET)) {
+ xmlXPathObjectPtr cur;
+
+ cur = valuePop(ctxt);
+ ctxt->context->nodelist = cur->nodesetval;
+ ctxt->context->node = NULL;
+ cur->nodesetval = NULL;
+ xmlXPathFreeObject(cur);
+ }
+ return;
+ }
/*
* Simple case: no axis seach all given node types.
*/
@@ -3772,22 +4020,29 @@
goto search_nodes;
case NODE_TYPE_PI:
if (CUR != '(') break;
+ if (name != NULL) free(name);
+ name = NULL;
if (NXT(1) != ')') {
+ xmlXPathObjectPtr cur;
+
/*
* Specific case: search a PI by name.
*/
- SKIP(2);
+ NEXT;
nodetest = NODE_TEST_PI;
xmlXPathEvalLiteral(ctxt);
CHECK_ERROR;
if (CUR != ')')
ERROR(XPATH_UNCLOSED_ERROR);
- xmlXPathSearchPI(ctxt, 0);
- return;
- }
- SKIP(2);
- nodetest = NODE_TEST_TYPE;
- nodetype = XML_PI_NODE;
+ NEXT;
+ xmlXPathStringFunction(ctxt, 1);
+ CHECK_ERROR;
+ cur = valuePop(ctxt);
+ name = xmlStrdup(cur->stringval);
+ xmlXPathFreeObject(cur);
+ } else
+ SKIP(2);
+ nodetest = NODE_TEST_PI;
goto search_nodes;
/*
@@ -3820,6 +4075,7 @@
default:
nodetest = NODE_TEST_NAME;
}
+parse_NodeTest:
if (nodetest == NODE_TEST_NONE) {
if (CUR == '*') {
NEXT;
@@ -3859,22 +4115,29 @@
goto search_nodes;
case NODE_TYPE_PI:
if (CUR != '(') break;
+ if (name != NULL) free(name);
+ name = NULL;
if (NXT(1) != ')') {
+ xmlXPathObjectPtr cur;
+
/*
* Specific case: search a PI by name.
*/
- SKIP(2);
+ NEXT;
nodetest = NODE_TEST_PI;
xmlXPathEvalLiteral(ctxt);
CHECK_ERROR;
if (CUR != ')')
ERROR(XPATH_UNCLOSED_ERROR);
- xmlXPathSearchPI(ctxt, 0);
- return;
- }
- SKIP(2);
- nodetest = NODE_TEST_TYPE;
- nodetype = XML_PI_NODE;
+ NEXT;
+ xmlXPathStringFunction(ctxt, 1);
+ CHECK_ERROR;
+ cur = valuePop(ctxt);
+ name = xmlStrdup(cur->stringval);
+ xmlXPathFreeObject(cur);
+ } else
+ SKIP(2);
+ nodetest = NODE_TEST_PI;
goto search_nodes;
}
nodetest = NODE_TEST_NAME;
@@ -4081,7 +4344,7 @@
xmlXPathObjectPtr
xmlXPathEval(const CHAR *str, xmlXPathContextPtr ctxt) {
xmlXPathParserContextPtr pctxt;
- xmlXPathObjectPtr res;
+ xmlXPathObjectPtr res = NULL, tmp;
xmlXPathInit();
@@ -4092,12 +4355,14 @@
pctxt = xmlXPathNewParserContext(str, ctxt);
xmlXPathEvalLocationPath(pctxt);
+ /* TODO: cleanup nodelist, res = valuePop(pctxt); */
do {
- res = valuePop(pctxt);
-#ifdef DEBUG
-#endif
- } while (res != NULL);
- res = xmlXPathNewNodeSetList(pctxt->context->nodelist);
+ tmp = valuePop(pctxt);
+ if (tmp != NULL);
+ xmlXPathFreeObject(tmp);
+ } while (tmp != NULL);
+ if (res == NULL)
+ res = xmlXPathNewNodeSetList(pctxt->context->nodelist);
xmlXPathFreeParserContext(pctxt);
return(res);
}
@@ -4109,7 +4374,7 @@
*
* Evaluate the XPath expression in the given context.
*
- * Returns the xmlXPathObjectPtr resulting from the eveluation or NULL.
+ * Returns the xmlXPathObjectPtr resulting from the evaluation or NULL.
* the caller has to free the object.
*/
xmlXPathObjectPtr