📄 pattern.c
字号:
* * Returns -1 in case of failure and 0 in case of success. */static intxmlStreamCompile(xmlPatternPtr comp) { xmlStreamCompPtr stream; int i, s = 0, root = 0, flags = 0, prevs = -1; xmlStepOp step; if ((comp == NULL) || (comp->steps == NULL)) return(-1); /* * special case for . */ if ((comp->nbStep == 1) && (comp->steps[0].op == XML_OP_ELEM) && (comp->steps[0].value == NULL) && (comp->steps[0].value2 == NULL)) { 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); } stream = xmlNewStreamComp((comp->nbStep / 2) + 1); if (stream == NULL) return(-1); if (comp->dict != NULL) { stream->dict = comp->dict; xmlDictReference(stream->dict); } i = 0; if (comp->flags & PAT_FROM_ROOT) stream->flags |= XML_STREAM_FROM_ROOT; for (;i < comp->nbStep;i++) { step = comp->steps[i]; switch (step.op) { case XML_OP_END: break; case XML_OP_ROOT: if (i != 0) goto error; root = 1; break; case XML_OP_NS: 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; 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 ((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: /* 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, XML_ELEMENT_NODE, flags); if (s < 0) goto error; prevs = s; flags = 0; break; 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 "//". */ if ((stream->flags & XML_STREAM_DESC) == 0) stream->flags |= XML_STREAM_DESC; break; } } if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) { /* * If this should behave like a real pattern, we will mark * the first step as having "//", to be reentrant on every * tree level. */ if ((stream->flags & XML_STREAM_DESC) == 0) stream->flags |= XML_STREAM_DESC; if (stream->nbStep > 0) { if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0) 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;#ifdef DEBUG_STREAMING xmlDebugStreamComp(stream);#endif comp->stream = stream; return(0);error: xmlFreeStreamComp(stream); return(0);}/** * xmlNewStreamCtxt: * @size: the number of expected states * * build a new stream context * * Returns the new structure or NULL in case of error. */static xmlStreamCtxtPtrxmlNewStreamCtxt(xmlStreamCompPtr stream) { xmlStreamCtxtPtr cur; cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt)); if (cur == NULL) { ERROR(NULL, NULL, NULL, "xmlNewStreamCtxt: malloc failed\n"); return(NULL); } memset(cur, 0, sizeof(xmlStreamCtxt)); cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int)); if (cur->states == NULL) { xmlFree(cur); ERROR(NULL, NULL, NULL, "xmlNewStreamCtxt: malloc failed\n"); return(NULL); } cur->nbState = 0; cur->maxState = 4; cur->level = 0; cur->comp = stream; cur->blockLevel = -1; return(cur);}/** * xmlFreeStreamCtxt: * @stream: the stream context * * Free the stream context */voidxmlFreeStreamCtxt(xmlStreamCtxtPtr stream) { xmlStreamCtxtPtr next; while (stream != NULL) { next = stream->next; if (stream->states != NULL) xmlFree(stream->states); xmlFree(stream); stream = next; }}/** * xmlStreamCtxtAddState: * @comp: the stream context * @idx: the step index for that streaming state * * Add a new state to the stream context * * Returns -1 in case of error or the state index if successful */static intxmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) { int i; for (i = 0;i < comp->nbState;i++) { if (comp->states[2 * i] < 0) { comp->states[2 * i] = idx; comp->states[2 * i + 1] = level; return(i); } } if (comp->nbState >= comp->maxState) { int *cur; cur = (int *) xmlRealloc(comp->states, comp->maxState * 4 * sizeof(int)); if (cur == NULL) { ERROR(NULL, NULL, NULL, "xmlNewStreamCtxt: malloc failed\n"); return(-1); } comp->states = cur; comp->maxState *= 2; } comp->states[2 * comp->nbState] = idx; comp->states[2 * comp->nbState++ + 1] = level; return(comp->nbState - 1);}/** * xmlStreamPushInternal: * @stream: the stream context * @name: the current name * @ns: the namespace name * @nodeType: the type of the node * * 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. * * Returns: -1 in case of error, 1 if the current state in the stream is a * match and 0 otherwise. */static intxmlStreamPushInternal(xmlStreamCtxtPtr stream, const xmlChar *name, const xmlChar *ns, int nodeType) { int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc; xmlStreamCompPtr comp; xmlStreamStep step;#ifdef DEBUG_STREAMING xmlStreamCtxtPtr orig = stream;#endif if ((stream == NULL) || (stream->nbState < 0)) return(-1); while (stream != NULL) { comp = stream->comp; 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->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 */ } /* * Fast check for ".". */ if (comp->nbStep == 0) { /* * / and . are handled at the XPath node set creation * level by checking min depth */ if (stream->flags & XML_PATTERN_XPATH) { stream = stream->next; continue; /* while */ } /* * For non-pattern like evaluation like XML Schema IDCs * or traditional XPath expressions, this will match if * we are at the first level only, otherwise on every level. */ if ((nodeType != XML_ATTRIBUTE_NODE) && (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) || (stream->level == 0))) { ret = 1; } stream->level++; goto stream_next; } if (stream->blockLevel != -1) { /* * Skip blocked expressions. */ 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 */ i = 0; m = stream->nbState; while (i < m) { if ((comp->flags & XML_STREAM_DESC) == 0) { /* * If there is no "//", then only the last * added state is of interest. */ stepNr = stream->states[2 * (stream->nbState -1)]; /* * TODO: Security check, should not happen, remove it. */ if (stream->states[(2 * (stream->nbState -1)) + 1] < stream->level) { return (-1); } desc = 0; /* loop-stopper */ i = m; } else { /* * If there are "//", then we need to process every "//" * occuring in the states, plus any other state for this * level. */ stepNr = stream->states[2 * i]; /* TODO: should not happen anymore: dead states */ if (stepNr < 0) goto next_state; tmp = stream->states[(2 * i) + 1]; /* skip new states just added */ if (tmp > stream->level) goto next_state; /* skip states at ancestor levels, except if "//" */ desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC; if ((tmp < stream->level) && (!desc)) goto next_state; } /* * Check for correct node-type. */ step = comp->steps[stepNr]; if (step.nodeType != nodeType) { if (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 (step.nodeType != XML_STREAM_ANY_NODE) goto next_state; } /* * Compare local/namespace-name. */ match = 0; if (step.nodeType == XML_STREAM_ANY_NODE) { match = 1; } else if (step.name == NULL) { if (step.ns == NULL) { /* * This lets through all elements/attributes. */ match = 1; } else if (ns != NULL) match = xmlStrEqual(step.ns, ns); } else if (((step.ns != NULL) == (ns != NULL)) && (name != NULL) && (step.name[0] == name[0]) && xmlStrEqual(step.name, name) && ((step.ns == ns) || xmlStrEqual(step.ns, ns))) { match = 1; } #if 0 /** TODO: Pointer comparison won't work, since not guaranteed that the given* values are in the same dict; especially if it's the namespace name,* normally coming from ns->href. We need a namespace dict mechanism !*/ } else if (comp->dict) { if (step.name == NULL) { if (step.ns == NULL) match = 1; else match = (step.ns == ns); } else { match = ((step.name == name) && (step.ns == ns)); }#endif /* if 0 ------------------------------------------------------- */ if (match) { final = step.flags & XML_STREAM_STEP_FINAL; if (desc) { if (final) { ret = 1; } else { /* descending match create a new state */ xmlStreamCtxtAddState(stream, stepNr + 1, stream->level + 1); } } else { if (final) { ret = 1; } else { xmlStreamCtxtAddState(stream, stepNr + 1, stream->level + 1); } } if ((ret != 1) && (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)) { /* * Mark this expression as blocked for any evaluation at * deeper levels. Note that this includes "/foo" * expressions if the *pattern* behaviour is used. */ stream->blockLevel = stream->level +1; }next_state: i++; } stream->level++; /* * Re/enter the expression. * Don't reenter if it's an absolute expression like "/foo", * except "//foo". */ step = comp->steps[0];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -