[xslt] [PATCH] Bug: xsltCompilePattern : failed to compile '@node()'
- From: Nick Wellnhofer <wellnhofer aevum de>
- To: The Gnome XSLT library mailing-list <xslt gnome org>
- Subject: [xslt] [PATCH] Bug: xsltCompilePattern : failed to compile '@node()'
- Date: Sat, 11 Jul 2009 19:40:05 +0200
I have always used match="@*" which works as expected.
But, being curious, I had a deeper look at xsltCompileStepPattern. The problem is that "@node()", "attribute::node()", "child::node()" and "node()" are all handled in different code paths and only the latter works.
Then, I noticed that the handling of match="child::name" is wrong. It matches the parents of <name> elements although it should be treated exactly like match="name". So the whole XSLT_OP_CHILD stuff is unneeded.
I also found that xsltScanName behaves a bit strange with regard to ':' characters. It doesn't parse an XML Name like the documentation says. It's better to use xsltScanNCName instead.
Another minor issue is that the parser currently allows invalid expressions like match="element*" because of lines 1745-1747 in pattern.c in trunk.
Attached is a patch against trunk that fixes all these issues. It merges the parsing of NodeTests in StepPatterns into a single code path keeping track of the axis that has been specified previously. The patch also contains some test cases.
Nick
--
aevum gmbh
rumfordstr. 4
80469 münchen
germany
tel: +49 89 3838 0653
http://aevum.de/
diff --git a/libxslt/pattern.c b/libxslt/pattern.c
index 815013b..d1064d6 100644
--- a/libxslt/pattern.c
+++ b/libxslt/pattern.c
@@ -46,7 +46,6 @@ typedef enum {
XSLT_OP_END=0,
XSLT_OP_ROOT,
XSLT_OP_ELEM,
- XSLT_OP_CHILD,
XSLT_OP_ATTR,
XSLT_OP_PARENT,
XSLT_OP_ANCESTOR,
@@ -61,6 +60,11 @@ typedef enum {
XSLT_OP_PREDICATE
} xsltOp;
+typedef enum {
+ AXIS_CHILD=1,
+ AXIS_ATTRIBUTE
+} xsltAxis;
+
typedef struct _xsltStepState xsltStepState;
typedef xsltStepState *xsltStepStatePtr;
struct _xsltStepState {
@@ -697,32 +701,6 @@ restart:
goto rollback;
}
continue;
- case XSLT_OP_CHILD: {
- xmlNodePtr lst;
-
- if ((node->type != XML_ELEMENT_NODE) &&
- (node->type != XML_DOCUMENT_NODE) &&
-#ifdef LIBXML_DOCB_ENABLED
- (node->type != XML_DOCB_DOCUMENT_NODE) &&
-#endif
- (node->type != XML_HTML_DOCUMENT_NODE))
- goto rollback;
-
- lst = node->children;
-
- if (step->value != NULL) {
- while (lst != NULL) {
- if ((lst->type == XML_ELEMENT_NODE) &&
- (step->value[0] == lst->name[0]) &&
- (xmlStrEqual(step->value, lst->name)))
- break;
- lst = lst->next;
- }
- if (lst != NULL)
- continue;
- }
- goto rollback;
- }
case XSLT_OP_ATTR:
if (node->type != XML_ATTRIBUTE_NODE)
goto rollback;
@@ -1330,46 +1308,6 @@ xsltScanLiteral(xsltParserContextPtr ctxt) {
}
/**
- * xsltScanName:
- * @ctxt: the XPath Parser context
- *
- * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
- * CombiningChar | Extender
- *
- * [5] Name ::= (Letter | '_' | ':') (NameChar)*
- *
- * [6] Names ::= Name (S Name)*
- *
- * Returns the Name parsed or NULL
- */
-
-static xmlChar *
-xsltScanName(xsltParserContextPtr ctxt) {
- const xmlChar *q, *cur;
- xmlChar *ret = NULL;
- int val, len;
-
- SKIP_BLANKS;
-
- cur = q = CUR_PTR;
- val = xmlStringCurrentChar(NULL, cur, &len);
- if (!IS_LETTER(val) && (val != '_') && (val != ':'))
- return(NULL);
-
- while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
- (val == '.') || (val == '-') ||
- (val == '_') ||
- (IS_COMBINING(val)) ||
- (IS_EXTENDER(val))) {
- cur += len;
- val = xmlStringCurrentChar(NULL, cur, &len);
- }
- ret = xmlStrndup(q, cur - q);
- CUR_PTR = cur;
- return(ret);
-}
-
-/**
* xsltScanNCName:
* @ctxt: the XPath Parser context
*
@@ -1404,30 +1342,6 @@ xsltScanNCName(xsltParserContextPtr ctxt) {
return(ret);
}
-/**
- * xsltScanQName:
- * @ctxt: the XPath Parser context
- * @prefix: the place to store the prefix
- *
- * Parse a qualified name
- *
- * Returns the Name parsed or NULL
- */
-
-static xmlChar *
-xsltScanQName(xsltParserContextPtr ctxt, xmlChar **prefix) {
- xmlChar *ret = NULL;
-
- *prefix = NULL;
- ret = xsltScanNCName(ctxt);
- if (CUR == ':') {
- *prefix = ret;
- NEXT;
- ret = xsltScanNCName(ctxt);
- }
- return(ret);
-}
-
/*
* xsltCompileIdKeyPattern:
* @ctxt: the compilation context
@@ -1447,7 +1361,7 @@ xsltScanQName(xsltParserContextPtr ctxt, xmlChar **prefix) {
*/
static void
xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
- int aid, int novar) {
+ int aid, int novar, xsltAxis axis) {
xmlChar *lit = NULL;
xmlChar *lit2 = NULL;
@@ -1458,6 +1372,12 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
return;
}
if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
+ if (axis != 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : NodeTest expected\n");
+ ctxt->error = 1;
+ return;
+ }
NEXT;
SKIP_BLANKS;
lit = xsltScanLiteral(ctxt);
@@ -1473,6 +1393,12 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
NEXT;
PUSH(XSLT_OP_ID, lit, NULL, novar);
} else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
+ if (axis != 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileIdKeyPattern : NodeTest expected\n");
+ ctxt->error = 1;
+ return;
+ }
NEXT;
SKIP_BLANKS;
lit = xsltScanLiteral(ctxt);
@@ -1549,7 +1475,12 @@ xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
return;
}
NEXT;
- PUSH(XSLT_OP_NODE, NULL, NULL, novar);
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
+ }
+ else {
+ PUSH(XSLT_OP_NODE, NULL, NULL, novar);
+ }
} else if (aid) {
xsltTransformError(NULL, NULL, NULL,
"xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
@@ -1594,51 +1525,25 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
const xmlChar *URI = NULL;
xmlChar *URL = NULL;
int level;
+ xsltAxis axis = 0;
SKIP_BLANKS;
if ((token == NULL) && (CUR == '@')) {
- xmlChar *prefix = NULL;
-
NEXT;
- if (CUR == '*') {
- NEXT;
- PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
- goto parse_predicate;
- }
- token = xsltScanQName(ctxt, &prefix);
- if (prefix != NULL) {
- xmlNsPtr ns;
-
- ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
- if (ns == NULL) {
- xsltTransformError(NULL, NULL, NULL,
- "xsltCompileStepPattern : no namespace bound to prefix %s\n",
- prefix);
- } else {
- URL = xmlStrdup(ns->href);
- }
- xmlFree(prefix);
- }
- if (token == NULL) {
- if (CUR == '*') {
- NEXT;
- PUSH(XSLT_OP_ATTR, NULL, URL, novar);
- return;
- }
- xsltTransformError(NULL, NULL, NULL,
- "xsltCompileStepPattern : Name expected\n");
- ctxt->error = 1;
- goto error;
- }
- PUSH(XSLT_OP_ATTR, token, URL, novar);
- goto parse_predicate;
+ axis = AXIS_ATTRIBUTE;
}
+parse_node_test:
if (token == NULL)
- token = xsltScanName(ctxt);
+ token = xsltScanNCName(ctxt);
if (token == NULL) {
if (CUR == '*') {
NEXT;
- PUSH(XSLT_OP_ALL, token, NULL, novar);
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
+ }
+ else {
+ PUSH(XSLT_OP_ALL, NULL, NULL, novar);
+ }
goto parse_predicate;
} else {
xsltTransformError(NULL, NULL, NULL,
@@ -1651,7 +1556,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
SKIP_BLANKS;
if (CUR == '(') {
- xsltCompileIdKeyPattern(ctxt, token, 0, novar);
+ xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
if (ctxt->error)
goto error;
} else if (CUR == ':') {
@@ -1663,7 +1568,7 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
/*
* This is a namespace match
*/
- token = xsltScanName(ctxt);
+ token = xsltScanNCName(ctxt);
ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
if (ns == NULL) {
xsltTransformError(NULL, NULL, NULL,
@@ -1678,7 +1583,12 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
if (token == NULL) {
if (CUR == '*') {
NEXT;
- PUSH(XSLT_OP_NS, URL, NULL, novar);
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, NULL, URL, novar);
+ }
+ else {
+ PUSH(XSLT_OP_NS, URL, NULL, novar);
+ }
} else {
xsltTransformError(NULL, NULL, NULL,
"xsltCompileStepPattern : Name expected\n");
@@ -1686,54 +1596,25 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
goto error;
}
} else {
- PUSH(XSLT_OP_ELEM, token, URL, novar);
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, token, URL, novar);
+ }
+ else {
+ PUSH(XSLT_OP_ELEM, token, URL, novar);
+ }
}
} else {
+ if (axis != 0) {
+ xsltTransformError(NULL, NULL, NULL,
+ "xsltCompileStepPattern : NodeTest expected\n");
+ ctxt->error = 1;
+ goto error;
+ }
NEXT;
if (xmlStrEqual(token, (const xmlChar *) "child")) {
- xmlFree(token);
- token = xsltScanName(ctxt);
- if (token == NULL) {
- if (CUR == '*') {
- NEXT;
- PUSH(XSLT_OP_ALL, token, NULL, novar);
- goto parse_predicate;
- } else {
- xsltTransformError(NULL, NULL, NULL,
- "xsltCompileStepPattern : QName expected\n");
- ctxt->error = 1;
- goto error;
- }
- }
- URI = xsltGetQNameURI(ctxt->elem, &token);
- if (token == NULL) {
- ctxt->error = 1;
- goto error;
- } else {
- name = xmlStrdup(token);
- if (URI != NULL)
- URL = xmlStrdup(URI);
- }
- PUSH(XSLT_OP_CHILD, name, URL, novar);
+ axis = AXIS_CHILD;
} else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
- xmlFree(token);
- token = xsltScanName(ctxt);
- if (token == NULL) {
- xsltTransformError(NULL, NULL, NULL,
- "xsltCompileStepPattern : QName expected\n");
- ctxt->error = 1;
- goto error;
- }
- URI = xsltGetQNameURI(ctxt->elem, &token);
- if (token == NULL) {
- ctxt->error = 1;
- goto error;
- } else {
- name = xmlStrdup(token);
- if (URI != NULL)
- URL = xmlStrdup(URI);
- }
- PUSH(XSLT_OP_ATTR, name, URL, novar);
+ axis = AXIS_ATTRIBUTE;
} else {
xsltTransformError(NULL, NULL, NULL,
"xsltCompileStepPattern : 'child' or 'attribute' expected\n");
@@ -1741,10 +1622,10 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
goto error;
}
xmlFree(token);
+ SKIP_BLANKS;
+ token = xsltScanNCName(ctxt);
+ goto parse_node_test;
}
- } else if (CUR == '*') {
- NEXT;
- PUSH(XSLT_OP_ALL, token, NULL, novar);
} else {
URI = xsltGetQNameURI(ctxt->elem, &token);
if (token == NULL) {
@@ -1753,7 +1634,12 @@ xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
}
if (URI != NULL)
URL = xmlStrdup(URI);
- PUSH(XSLT_OP_ELEM, token, URL, novar);
+ if (axis == AXIS_ATTRIBUTE) {
+ PUSH(XSLT_OP_ATTR, token, URL, novar);
+ }
+ else {
+ PUSH(XSLT_OP_ELEM, token, URL, novar);
+ }
}
parse_predicate:
SKIP_BLANKS;
@@ -1890,7 +1776,7 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
xsltCompileRelativePathPattern(ctxt, NULL, novar);
} else {
xmlChar *name;
- name = xsltScanName(ctxt);
+ name = xsltScanNCName(ctxt);
if (name == NULL) {
xsltTransformError(NULL, NULL, NULL,
"xsltCompileLocationPathPattern : Name expected\n");
@@ -1899,7 +1785,7 @@ xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
}
SKIP_BLANKS;
if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
- xsltCompileIdKeyPattern(ctxt, name, 1, novar);
+ xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
if ((CUR == '/') && (NXT(1) == '/')) {
PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
NEXT;
@@ -2177,7 +2063,6 @@ xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
else
top = &(style->attrMatch);
break;
- case XSLT_OP_CHILD:
case XSLT_OP_PARENT:
case XSLT_OP_ANCESTOR:
top = &(style->elemMatch);
diff --git a/tests/REC/test-5.2-19.out b/tests/REC/test-5.2-19.out
new file mode 100644
index 0000000..eab8e77
--- /dev/null
+++ b/tests/REC/test-5.2-19.out
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<result>element</result>
diff --git a/tests/REC/test-5.2-19.xml b/tests/REC/test-5.2-19.xml
new file mode 100644
index 0000000..5694448
--- /dev/null
+++ b/tests/REC/test-5.2-19.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<root>
+ <element/>
+</root>
diff --git a/tests/REC/test-5.2-19.xsl b/tests/REC/test-5.2-19.xsl
new file mode 100644
index 0000000..d3998e6
--- /dev/null
+++ b/tests/REC/test-5.2-19.xsl
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="text()"/>
+
+<xsl:template match="child::element">
+ <result>
+ <xsl:value-of select="name()"/>
+ </result>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/tests/REC/test-5.2-20.out b/tests/REC/test-5.2-20.out
new file mode 100644
index 0000000..67a2a01
--- /dev/null
+++ b/tests/REC/test-5.2-20.out
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<result>value</result>
diff --git a/tests/REC/test-5.2-20.xml b/tests/REC/test-5.2-20.xml
new file mode 100644
index 0000000..ca946bc
--- /dev/null
+++ b/tests/REC/test-5.2-20.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<root>
+ <element attribute="value"/>
+</root>
diff --git a/tests/REC/test-5.2-20.xsl b/tests/REC/test-5.2-20.xsl
new file mode 100644
index 0000000..6590f66
--- /dev/null
+++ b/tests/REC/test-5.2-20.xsl
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="*">
+ <xsl:apply-templates select="*|@*"/>
+</xsl:template>
+
+<xsl:template match="@node()">
+ <result>
+ <xsl:value-of select="."/>
+ </result>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/tests/REC/test-5.2-21.out b/tests/REC/test-5.2-21.out
new file mode 100644
index 0000000..67a2a01
--- /dev/null
+++ b/tests/REC/test-5.2-21.out
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<result>value</result>
diff --git a/tests/REC/test-5.2-21.xml b/tests/REC/test-5.2-21.xml
new file mode 100644
index 0000000..ca946bc
--- /dev/null
+++ b/tests/REC/test-5.2-21.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<root>
+ <element attribute="value"/>
+</root>
diff --git a/tests/REC/test-5.2-21.xsl b/tests/REC/test-5.2-21.xsl
new file mode 100644
index 0000000..15a80fb
--- /dev/null
+++ b/tests/REC/test-5.2-21.xsl
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="*">
+ <xsl:apply-templates select="*|@*"/>
+</xsl:template>
+
+<xsl:template match="attribute::node()">
+ <result>
+ <xsl:value-of select="."/>
+ </result>
+</xsl:template>
+
+</xsl:stylesheet>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]