summaryrefslogtreecommitdiff
path: root/pattern.c
diff options
context:
space:
mode:
authorMike Hommey <glandium@debian.org>2006-01-06 18:28:17 +0100
committerMike Hommey <glandium@debian.org>2006-01-06 18:28:17 +0100
commit0fd83af441e251fc23fc1af7959fd6ecfa105fe1 (patch)
treea2b35749a66abce02e6f07983ef50618d93bef58 /pattern.c
parent17049f05f9ef09b3dc2a9c5d1de3f21de7c03193 (diff)
downloadlibxml2-0fd83af441e251fc23fc1af7959fd6ecfa105fe1.tar.gz
Load /tmp/tmp.U9vXwU/libxml2-2.6.23 intoupstream/2.6.23
local/libxml2/branches/upstream/current.
Diffstat (limited to 'pattern.c')
-rw-r--r--pattern.c517
1 files changed, 435 insertions, 82 deletions
diff --git a/pattern.c b/pattern.c
index a76a58c..7b3e508 100644
--- a/pattern.c
+++ b/pattern.c
@@ -46,26 +46,43 @@
#define XML_STREAM_STEP_FINAL 2
#define XML_STREAM_STEP_ROOT 4
#define XML_STREAM_STEP_ATTR 8
+#define XML_STREAM_STEP_NODE 16
+#define XML_STREAM_STEP_IN_SET 32
/*
-* TODO: This is used on _xmlStreamCtxt, so don't use any values
-* from xmlPatternFlags.
+* NOTE: Those private flags (XML_STREAM_xxx) are used
+* in _xmlStreamCtxt->flag. They extend the public
+* xmlPatternFlags, so be carefull not to interfere with the
+* reserved values for xmlPatternFlags.
*/
+#define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
+#define XML_STREAM_FROM_ROOT 1<<15
#define XML_STREAM_DESC 1<<16
+/*
+* XML_STREAM_ANY_NODE is used for comparison against
+* xmlElementType enums, to indicate a node of any type.
+*/
+#define XML_STREAM_ANY_NODE 100
+
#define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
XML_PATTERN_XSSEL | \
XML_PATTERN_XSFIELD)
-#define XML_STREAM_XS_IDC(item) (item->flags & \
+#define XML_STREAM_XS_IDC(c) ((c)->flags & \
(XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
+#define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
+
+#define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
+
typedef struct _xmlStreamStep xmlStreamStep;
typedef xmlStreamStep *xmlStreamStepPtr;
struct _xmlStreamStep {
int flags; /* properties of that step */
const xmlChar *name; /* first string value if NULL accept all */
const xmlChar *ns; /* second string value */
+ int nodeType; /* type of node */
};
typedef struct _xmlStreamComp xmlStreamComp;
@@ -690,6 +707,7 @@ rollback:
#define CUR (*ctxt->cur)
#define SKIP(val) ctxt->cur += (val)
#define NXT(val) ctxt->cur[(val)]
+#define PEEKPREV(val) ctxt->cur[-(val)]
#define CUR_PTR ctxt->cur
#define SKIP_BLANKS \
@@ -884,6 +902,7 @@ xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
xmlChar *name = NULL;
xmlChar *URL = NULL;
+ SKIP_BLANKS;
name = xmlPatScanNCName(ctxt);
if (name == NULL) {
if (CUR == '*') {
@@ -901,6 +920,13 @@ xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
xmlChar *prefix = name;
NEXT;
+
+ if (IS_BLANK_CH(CUR)) {
+ ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
+ xmlFree(prefix);
+ ctxt->error = 1;
+ goto error;
+ }
/*
* This is a namespace match
*/
@@ -967,25 +993,39 @@ xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
xmlChar *token = NULL;
xmlChar *name = NULL;
xmlChar *URL = NULL;
+ int hasBlanks = 0;
SKIP_BLANKS;
if (CUR == '.') {
+ /*
+ * Context node.
+ */
NEXT;
PUSH(XML_OP_ELEM, NULL, NULL);
return;
}
+ if (CUR == '@') {
+ /*
+ * Attribute test.
+ */
+ if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
+ ERROR5(NULL, NULL, NULL,
+ "Unexpected attribute axis in '%s'.\n", ctxt->base);
+ ctxt->error = 1;
+ return;
+ }
+ NEXT;
+ xmlCompileAttributeTest(ctxt);
+ if (ctxt->error != 0)
+ goto error;
+ return;
+ }
name = xmlPatScanNCName(ctxt);
if (name == NULL) {
if (CUR == '*') {
NEXT;
PUSH(XML_OP_ALL, NULL, NULL);
return;
- } else if (CUR == '@') {
- NEXT;
- xmlCompileAttributeTest(ctxt);
- if (ctxt->error != 0)
- goto error;
- return;
} else {
ERROR(NULL, NULL, NULL,
"xmlCompileStepPattern : Name expected\n");
@@ -993,13 +1033,21 @@ xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
return;
}
}
- SKIP_BLANKS;
+ if (IS_BLANK_CH(CUR)) {
+ hasBlanks = 1;
+ SKIP_BLANKS;
+ }
if (CUR == ':') {
NEXT;
if (CUR != ':') {
xmlChar *prefix = name;
- int i;
+ int i;
+ if (hasBlanks || IS_BLANK_CH(CUR)) {
+ ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
+ ctxt->error = 1;
+ goto error;
+ }
/*
* This is a namespace match
*/
@@ -1060,6 +1108,11 @@ xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
int i;
NEXT;
+ if (IS_BLANK_CH(CUR)) {
+ ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
+ ctxt->error = 1;
+ goto error;
+ }
/*
* This is a namespace match
*/
@@ -1104,13 +1157,19 @@ xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
} else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
xmlFree(name);
name = NULL;
+ if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
+ ERROR5(NULL, NULL, NULL,
+ "Unexpected attribute axis in '%s'.\n", ctxt->base);
+ ctxt->error = 1;
+ goto error;
+ }
xmlCompileAttributeTest(ctxt);
if (ctxt->error != 0)
goto error;
return;
} else {
- ERROR(NULL, NULL, NULL,
- "xmlCompileStepPattern : 'child' or 'attribute' expected\n");
+ ERROR5(NULL, NULL, NULL,
+ "The 'element' or 'attribute' axis is expected.\n", NULL);
ctxt->error = 1;
goto error;
}
@@ -1167,20 +1226,41 @@ xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
NEXT;
NEXT;
NEXT;
+ /* Check for incompleteness. */
+ SKIP_BLANKS;
+ if (CUR == 0) {
+ ERROR5(NULL, NULL, NULL,
+ "Incomplete expression '%s'.\n", ctxt->base);
+ ctxt->error = 1;
+ goto error;
+ }
}
if (CUR == '@') {
NEXT;
xmlCompileAttributeTest(ctxt);
SKIP_BLANKS;
+ /* TODO: check for incompleteness */
if (CUR != 0) {
xmlCompileStepPattern(ctxt);
+ if (ctxt->error != 0)
+ goto error;
}
} else {
if (CUR == '/') {
PUSH(XML_OP_ROOT, NULL, NULL);
NEXT;
+ /* Check for incompleteness. */
+ SKIP_BLANKS;
+ if (CUR == 0) {
+ ERROR5(NULL, NULL, NULL,
+ "Incomplete expression '%s'.\n", ctxt->base);
+ ctxt->error = 1;
+ goto error;
+ }
}
xmlCompileStepPattern(ctxt);
+ if (ctxt->error != 0)
+ goto error;
SKIP_BLANKS;
while (CUR == '/') {
if (NXT(1) == '/') {
@@ -1189,13 +1269,21 @@ xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
NEXT;
SKIP_BLANKS;
xmlCompileStepPattern(ctxt);
+ if (ctxt->error != 0)
+ goto error;
} else {
PUSH(XML_OP_PARENT, NULL, NULL);
NEXT;
SKIP_BLANKS;
- if (CUR != 0) {
- xmlCompileStepPattern(ctxt);
+ if (CUR == 0) {
+ ERROR5(NULL, NULL, NULL,
+ "Incomplete expression '%s'.\n", ctxt->base);
+ ctxt->error = 1;
+ goto error;
}
+ xmlCompileStepPattern(ctxt);
+ if (ctxt->error != 0)
+ goto error;
}
}
}
@@ -1208,6 +1296,107 @@ error:
return;
}
+/**
+ * xmlCompileIDCXPathPath:
+ * @ctxt: the compilation context
+ *
+ * Compile the Path Pattern and generates a precompiled
+ * form suitable for fast matching.
+ *
+ * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
+ */
+static void
+xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
+ SKIP_BLANKS;
+ if (CUR == '/') {
+ ERROR5(NULL, NULL, NULL,
+ "Unexpected selection of the document root in '%s'.\n",
+ ctxt->base);
+ goto error;
+ }
+ ctxt->comp->flags |= PAT_FROM_CUR;
+
+ if (CUR == '.') {
+ /* "." - "self::node()" */
+ NEXT;
+ SKIP_BLANKS;
+ if (CUR == 0) {
+ /*
+ * Selection of the context node.
+ */
+ PUSH(XML_OP_ELEM, NULL, NULL);
+ return;
+ }
+ if (CUR != '/') {
+ /* TODO: A more meaningful error message. */
+ ERROR5(NULL, NULL, NULL,
+ "Unexpected token after '.' in '%s'.\n", ctxt->base);
+ goto error;
+ }
+ /* "./" - "self::node()/" */
+ NEXT;
+ SKIP_BLANKS;
+ if (CUR == '/') {
+ if (IS_BLANK_CH(PEEKPREV(1))) {
+ /*
+ * Disallow "./ /"
+ */
+ ERROR5(NULL, NULL, NULL,
+ "Unexpected '/' token in '%s'.\n", ctxt->base);
+ goto error;
+ }
+ /* ".//" - "self:node()/descendant-or-self::node()/" */
+ PUSH(XML_OP_ANCESTOR, NULL, NULL);
+ NEXT;
+ SKIP_BLANKS;
+ }
+ if (CUR == 0)
+ goto error_unfinished;
+ }
+ /*
+ * Process steps.
+ */
+ do {
+ xmlCompileStepPattern(ctxt);
+ if (ctxt->error != 0)
+ goto error;
+ SKIP_BLANKS;
+ if (CUR != '/')
+ break;
+ PUSH(XML_OP_PARENT, NULL, NULL);
+ NEXT;
+ SKIP_BLANKS;
+ if (CUR == '/') {
+ /*
+ * Disallow subsequent '//'.
+ */
+ ERROR5(NULL, NULL, NULL,
+ "Unexpected subsequent '//' in '%s'.\n",
+ ctxt->base);
+ goto error;
+ }
+ if (CUR == 0)
+ goto error_unfinished;
+
+ } while (CUR != 0);
+
+ if (CUR != 0) {
+ ERROR5(NULL, NULL, NULL,
+ "Failed to compile expression '%s'.\n", ctxt->base);
+ ctxt->error = 1;
+ }
+ return;
+error:
+ ctxt->error = 1;
+ return;
+
+error_unfinished:
+ ctxt->error = 1;
+ ERROR5(NULL, NULL, NULL,
+ "Unfinished expression '%s'.\n", ctxt->base);
+ return;
+}
+
/************************************************************************
* *
* The streaming code *
@@ -1334,7 +1523,7 @@ xmlFreeStreamComp(xmlStreamCompPtr comp) {
*/
static int
xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
- const xmlChar *ns, int flags) {
+ const xmlChar *ns, int nodeType, int flags) {
xmlStreamStepPtr cur;
if (comp->nbStep >= comp->maxStep) {
@@ -1352,6 +1541,7 @@ xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
cur->flags = flags;
cur->name = name;
cur->ns = ns;
+ cur->nodeType = nodeType;
return(comp->nbStep - 1);
}
@@ -1366,7 +1556,8 @@ xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
static int
xmlStreamCompile(xmlPatternPtr comp) {
xmlStreamCompPtr stream;
- int i, s = 0, root = 0, flags = 0;
+ int i, s = 0, root = 0, flags = 0, prevs = -1;
+ xmlStepOp step;
if ((comp == NULL) || (comp->steps == NULL))
return(-1);
@@ -1380,6 +1571,8 @@ xmlStreamCompile(xmlPatternPtr comp) {
stream = xmlNewStreamComp(0);
if (stream == NULL)
return(-1);
+ /* Note that the stream will have no steps in this case. */
+ stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
comp->stream = stream;
return(0);
}
@@ -1392,19 +1585,13 @@ xmlStreamCompile(xmlPatternPtr comp) {
xmlDictReference(stream->dict);
}
- /*
- * Skip leading ./ on relative paths
- */
- i = 0;
- while ((comp->flags & PAT_FROM_CUR) && (comp->nbStep > i + 2) &&
- (comp->steps[i].op == XML_OP_ELEM) &&
- (comp->steps[i].value == NULL) &&
- (comp->steps[i].value2 == NULL) &&
- (comp->steps[i + 1].op == XML_OP_PARENT)) {
- i += 2;
- }
+ i = 0;
+ if (comp->flags & PAT_FROM_ROOT)
+ stream->flags |= XML_STREAM_FROM_ROOT;
+
for (;i < comp->nbStep;i++) {
- switch (comp->steps[i].op) {
+ step = comp->steps[i];
+ switch (step.op) {
case XML_OP_END:
break;
case XML_OP_ROOT:
@@ -1413,51 +1600,95 @@ xmlStreamCompile(xmlPatternPtr comp) {
root = 1;
break;
case XML_OP_NS:
- s = xmlStreamCompAddStep(stream, NULL,
- comp->steps[i].value, flags);
- flags = 0;
+ s = xmlStreamCompAddStep(stream, NULL, step.value,
+ XML_ELEMENT_NODE, flags);
if (s < 0)
goto error;
+ prevs = s;
+ flags = 0;
break;
case XML_OP_ATTR:
flags |= XML_STREAM_STEP_ATTR;
- s = xmlStreamCompAddStep(stream, comp->steps[i].value,
- comp->steps[i].value2, flags);
+ prevs = -1;
+ s = xmlStreamCompAddStep(stream,
+ step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
flags = 0;
if (s < 0)
goto error;
break;
- case XML_OP_ELEM:
- if ((comp->steps[i].value == NULL) &&
- (comp->steps[i].value2 == NULL) &&
- (comp->nbStep > i + 2) &&
- (comp->steps[i + 1].op == XML_OP_PARENT)) {
- i++;
- continue;
- }
+ case XML_OP_ELEM:
+ if ((step.value == NULL) && (step.value2 == NULL)) {
+ /*
+ * We have a "." or "self::node()" here.
+ * Eliminate redundant self::node() tests like in "/./."
+ * or "//./"
+ * The only case we won't eliminate is "//.", i.e. if
+ * self::node() is the last node test and we had
+ * continuation somewhere beforehand.
+ */
+ if ((comp->nbStep == i + 1) &&
+ (flags & XML_STREAM_STEP_DESC)) {
+ /*
+ * Mark the special case where the expression resolves
+ * to any type of node.
+ */
+ if (comp->nbStep == i + 1) {
+ stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
+ }
+ flags |= XML_STREAM_STEP_NODE;
+ s = xmlStreamCompAddStep(stream, NULL, NULL,
+ XML_STREAM_ANY_NODE, flags);
+ if (s < 0)
+ goto error;
+ flags = 0;
+ /*
+ * If there was a previous step, mark it to be added to
+ * the result node-set; this is needed since only
+ * the last step will be marked as "final" and only
+ * "final" nodes are added to the resulting set.
+ */
+ if (prevs != -1) {
+ stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
+ prevs = -1;
+ }
+ break;
+
+ } else {
+ /* Just skip this one. */
+ continue;
+ }
+ }
+ /* An element node. */
+ s = xmlStreamCompAddStep(stream, step.value, step.value2,
+ XML_ELEMENT_NODE, flags);
+ if (s < 0)
+ goto error;
+ prevs = s;
+ flags = 0;
+ break;
case XML_OP_CHILD:
- s = xmlStreamCompAddStep(stream, comp->steps[i].value,
- comp->steps[i].value2, flags);
- flags = 0;
+ /* An element node child. */
+ s = xmlStreamCompAddStep(stream, step.value, step.value2,
+ XML_ELEMENT_NODE, flags);
if (s < 0)
goto error;
+ prevs = s;
+ flags = 0;
break;
case XML_OP_ALL:
- s = xmlStreamCompAddStep(stream, NULL, NULL, flags);
- flags = 0;
+ s = xmlStreamCompAddStep(stream, NULL, NULL,
+ XML_ELEMENT_NODE, flags);
if (s < 0)
goto error;
+ prevs = s;
+ flags = 0;
break;
- case XML_OP_PARENT:
- if ((comp->nbStep > i + 1) &&
- (comp->steps[i + 1].op == XML_OP_ELEM) &&
- (comp->steps[i + 1].value == NULL) &&
- (comp->steps[i + 1].value2 == NULL)) {
- i++;
- continue;
- }
+ case XML_OP_PARENT:
break;
case XML_OP_ANCESTOR:
+ /* Skip redundant continuations. */
+ if (flags & XML_STREAM_STEP_DESC)
+ break;
flags |= XML_STREAM_STEP_DESC;
/*
* Mark the expression as having "//".
@@ -1481,6 +1712,8 @@ xmlStreamCompile(xmlPatternPtr comp) {
stream->steps[0].flags |= XML_STREAM_STEP_DESC;
}
}
+ if (stream->nbStep <= s)
+ goto error;
stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
if (root)
stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
@@ -1603,7 +1836,7 @@ xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
static int
xmlStreamPushInternal(xmlStreamCtxtPtr stream,
const xmlChar *name, const xmlChar *ns,
- xmlElementType nodeType) {
+ int nodeType) {
int ret = 0, err = 0, final = 0, tmp, i, m, match, step, desc;
xmlStreamCompPtr comp;
#ifdef DEBUG_STREAMING
@@ -1615,16 +1848,34 @@ xmlStreamPushInternal(xmlStreamCtxtPtr stream,
while (stream != NULL) {
comp = stream->comp;
- if ((name == NULL) && (ns == NULL)) {
+
+ if ((nodeType == XML_ELEMENT_NODE) &&
+ (name == NULL) && (ns == NULL)) {
+ /* We have a document node here (or a reset). */
stream->nbState = 0;
stream->level = 0;
stream->blockLevel = -1;
- if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
- tmp = xmlStreamCtxtAddState(stream, 0, 0);
- if (tmp < 0)
- err++;
- if (comp->nbStep == 0)
+ if (comp->flags & XML_STREAM_FROM_ROOT) {
+ if (comp->nbStep == 0) {
+ /* TODO: We have a "/." here? */
ret = 1;
+ } else {
+ if ((comp->nbStep == 1) &&
+ (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
+ (comp->steps[0].flags & XML_STREAM_STEP_DESC))
+ {
+ /*
+ * In the case of "//." the document node will match
+ * as well.
+ */
+ ret = 1;
+ } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
+ /* TODO: Do we need this ? */
+ tmp = xmlStreamCtxtAddState(stream, 0, 0);
+ if (tmp < 0)
+ err++;
+ }
+ }
}
stream = stream->next;
continue; /* while */
@@ -1647,7 +1898,7 @@ xmlStreamPushInternal(xmlStreamCtxtPtr stream,
* or traditional XPath expressions, this will match if
* we are at the first level only, otherwise on every level.
*/
- if ((nodeType == XML_ELEMENT_NODE) &&
+ if ((nodeType != XML_ATTRIBUTE_NODE) &&
(((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
(stream->level == 0))) {
ret = 1;
@@ -1662,6 +1913,19 @@ xmlStreamPushInternal(xmlStreamCtxtPtr stream,
stream->level++;
goto stream_next;
}
+
+ if ((nodeType != XML_ELEMENT_NODE) &&
+ (nodeType != XML_ATTRIBUTE_NODE) &&
+ ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
+ /*
+ * No need to process nodes of other types if we don't
+ * resolve to those types.
+ * TODO: Do we need to block the context here?
+ */
+ stream->level++;
+ goto stream_next;
+ }
+
/*
* Check evolution of existing states
*/
@@ -1710,14 +1974,24 @@ xmlStreamPushInternal(xmlStreamCtxtPtr stream,
/*
* Check for correct node-type.
*/
- if ((nodeType == XML_ATTRIBUTE_NODE) &&
- ((comp->steps[step].flags & XML_STREAM_STEP_ATTR) == 0))
- goto next_state;
+ if (comp->steps[step].nodeType != nodeType) {
+ if (comp->steps[step].nodeType == XML_ATTRIBUTE_NODE) {
+ /*
+ * Block this expression for deeper evaluation.
+ */
+ if ((comp->flags & XML_STREAM_DESC) == 0)
+ stream->blockLevel = stream->level +1;
+ goto next_state;
+ } else if (comp->steps[step].nodeType != XML_STREAM_ANY_NODE)
+ goto next_state;
+ }
/*
* Compare local/namespace-name.
*/
match = 0;
- if (comp->dict) {
+ if (comp->steps[step].nodeType == XML_STREAM_ANY_NODE) {
+ match = 1;
+ } else if (comp->dict) {
if (comp->steps[step].name == NULL) {
if (comp->steps[step].ns == NULL)
match = 1;
@@ -1756,7 +2030,15 @@ xmlStreamPushInternal(xmlStreamCtxtPtr stream,
stream->level + 1);
}
}
- }
+ if ((ret != 1) &&
+ (comp->steps[step].flags & XML_STREAM_STEP_IN_SET)) {
+ /*
+ * Check if we have a special case like "foo/bar//.", where
+ * "foo" is selected as well.
+ */
+ ret = 1;
+ }
+ }
if (((comp->flags & XML_STREAM_DESC) == 0) &&
((! match) || final)) {
/*
@@ -1774,6 +2056,8 @@ next_state:
/*
* Re/enter the expression.
+ * Don't reenter if it's an absolute expression like "/foo",
+ * except "//foo".
*/
if (comp->steps[0].flags & XML_STREAM_STEP_ROOT)
goto stream_next;
@@ -1816,14 +2100,19 @@ compare:
/*
* Check expected node-type.
*/
- if ((nodeType == XML_ATTRIBUTE_NODE) &&
- ((comp->steps[0].flags & XML_STREAM_STEP_ATTR) == 0))
- goto stream_next;
+ if (comp->steps[0].nodeType != nodeType) {
+ if (nodeType == XML_ATTRIBUTE_NODE)
+ goto stream_next;
+ else if (comp->steps[0].nodeType != XML_STREAM_ANY_NODE)
+ goto stream_next;
+ }
/*
* Compare local/namespace-name.
*/
match = 0;
- if (comp->steps[0].name == NULL) {
+ if (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) {
+ match = 1;
+ } else if (comp->steps[0].name == NULL) {
if (comp->steps[0].ns == NULL)
match = 1;
else {
@@ -1840,12 +2129,20 @@ compare:
match = ((xmlStrEqual(comp->steps[0].name, name)) &&
(xmlStrEqual(comp->steps[0].ns, ns)));
}
- if (match) {
- final = comp->steps[0].flags & XML_STREAM_STEP_FINAL;
+ final = comp->steps[0].flags & XML_STREAM_STEP_FINAL;
+ if (match) {
if (final)
ret = 1;
else
xmlStreamCtxtAddState(stream, 1, stream->level);
+ if ((ret != 1) &&
+ (comp->steps[0].flags & XML_STREAM_STEP_IN_SET)) {
+ /*
+ * Check if we have a special case like "foo//.", where
+ * "foo" is selected as well.
+ */
+ ret = 1;
+ }
}
if (((comp->flags & XML_STREAM_DESC) == 0) &&
((! match) || final)) {
@@ -1855,7 +2152,7 @@ compare:
*/
stream->blockLevel = stream->level;
}
-
+
stream_next:
stream = stream->next;
} /* while stream != NULL */
@@ -1879,6 +2176,7 @@ stream_next:
* to come from the dictionary.
* Both @name and @ns being NULL means the / i.e. the root of the document.
* This can also act as a reset.
+ * Otherwise the function will act as if it has been given an element-node.
*
* Returns: -1 in case of error, 1 if the current state in the stream is a
* match and 0 otherwise.
@@ -1886,7 +2184,35 @@ stream_next:
int
xmlStreamPush(xmlStreamCtxtPtr stream,
const xmlChar *name, const xmlChar *ns) {
- return (xmlStreamPushInternal(stream, name, ns, XML_ELEMENT_NODE));
+ return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
+}
+
+/**
+ * xmlStreamPushNode:
+ * @stream: the stream context
+ * @name: the current name
+ * @ns: the namespace name
+ * @nodeType: the type of the node being pushed
+ *
+ * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
+ * indicated a dictionary, then strings for name and ns will be expected
+ * to come from the dictionary.
+ * Both @name and @ns being NULL means the / i.e. the root of the document.
+ * This can also act as a reset.
+ * Different from xmlStreamPush() this function can be fed with nodes of type:
+ * element-, attribute-, text-, cdata-section-, comment- and
+ * processing-instruction-node.
+ *
+ * Returns: -1 in case of error, 1 if the current state in the stream is a
+ * match and 0 otherwise.
+ */
+int
+xmlStreamPushNode(xmlStreamCtxtPtr stream,
+ const xmlChar *name, const xmlChar *ns,
+ int nodeType)
+{
+ return (xmlStreamPushInternal(stream, name, ns,
+ nodeType));
}
/**
@@ -1900,6 +2226,7 @@ xmlStreamPush(xmlStreamCtxtPtr stream,
* to come from the dictionary.
* Both @name and @ns being NULL means the / i.e. the root of the document.
* This can also act as a reset.
+* Otherwise the function will act as if it has been given an attribute-node.
*
* Returns: -1 in case of error, 1 if the current state in the stream is a
* match and 0 otherwise.
@@ -1907,7 +2234,7 @@ xmlStreamPush(xmlStreamCtxtPtr stream,
int
xmlStreamPushAttr(xmlStreamCtxtPtr stream,
const xmlChar *name, const xmlChar *ns) {
- return (xmlStreamPushInternal(stream, name, ns, XML_ATTRIBUTE_NODE));
+ return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
}
/**
@@ -1921,11 +2248,9 @@ xmlStreamPushAttr(xmlStreamCtxtPtr stream,
int
xmlStreamPop(xmlStreamCtxtPtr stream) {
int i, lev;
- int ret;
-
+
if (stream == NULL)
return(-1);
- ret = 0;
while (stream != NULL) {
/*
* Reset block-level.
@@ -1935,7 +2260,7 @@ xmlStreamPop(xmlStreamCtxtPtr stream) {
stream->level--;
if (stream->level < 0)
- ret = -1;
+ return(-1);
/*
* Check evolution of existing states
*/
@@ -1952,6 +2277,31 @@ xmlStreamPop(xmlStreamCtxtPtr stream) {
return(0);
}
+/**
+ * xmlStreamWantsAnyNode:
+ * @streamCtxt: the stream context
+ *
+ * Query if the streaming pattern additionally needs to be fed with
+ * text-, cdata-section-, comment- and processing-instruction-nodes.
+ * If the result is 0 then only element-nodes and attribute-nodes
+ * need to be pushed.
+ *
+ * Returns: 1 in case of need of nodes of the above described types,
+ * 0 otherwise. -1 on API errors.
+ */
+int
+xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
+{
+ if (streamCtxt == NULL)
+ return(-1);
+ while (streamCtxt != NULL) {
+ if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
+ return(1);
+ streamCtxt = streamCtxt->next;
+ }
+ return(0);
+}
+
/************************************************************************
* *
* The public interfaces *
@@ -2008,7 +2358,10 @@ xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
cur->flags = flags;
ctxt->comp = cur;
- xmlCompilePathPattern(ctxt);
+ if (XML_STREAM_XS_IDC(cur))
+ xmlCompileIDCXPathPath(ctxt);
+ else
+ xmlCompilePathPattern(ctxt);
if (ctxt->error != 0)
goto error;
xmlFreePatParserContext(ctxt);