2 * pattern.c: Implemetation of selectors for nodes
5 * http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
7 * http://www.w3.org/TR/1999/REC-xml-19991116
9 * See Copyright for the status of this software.
16 * - compilation flags to check for specific syntaxes
17 * using flags of xmlPatterncompile()
18 * - making clear how pattern starting with / or . need to be handled,
19 * currently push(NULL, NULL) means a reset of the streaming context
20 * and indicating we are on / (the document node), probably need
21 * something similar for .
22 * - get rid of the "compile" starting with lowercase
23 * - DONE (2006-05-16): get rid of the Strdup/Strndup in case of dictionary
30 #include <libxml/xmlmemory.h>
31 #include <libxml/tree.h>
32 #include <libxml/hash.h>
33 #include <libxml/dict.h>
34 #include <libxml/xmlerror.h>
35 #include <libxml/parserInternals.h>
36 #include <libxml/pattern.h>
38 #ifdef LIBXML_PATTERN_ENABLED
40 /* #define DEBUG_STREAMING */
45 #define ERROR(a, b, c, d)
46 #define ERROR5(a, b, c, d, e)
48 #define XML_STREAM_STEP_DESC 1
49 #define XML_STREAM_STEP_FINAL 2
50 #define XML_STREAM_STEP_ROOT 4
51 #define XML_STREAM_STEP_ATTR 8
52 #define XML_STREAM_STEP_NODE 16
53 #define XML_STREAM_STEP_IN_SET 32
56 * NOTE: Those private flags (XML_STREAM_xxx) are used
57 * in _xmlStreamCtxt->flag. They extend the public
58 * xmlPatternFlags, so be carefull not to interfere with the
59 * reserved values for xmlPatternFlags.
61 #define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
62 #define XML_STREAM_FROM_ROOT 1<<15
63 #define XML_STREAM_DESC 1<<16
66 * XML_STREAM_ANY_NODE is used for comparison against
67 * xmlElementType enums, to indicate a node of any type.
69 #define XML_STREAM_ANY_NODE 100
71 #define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
75 #define XML_STREAM_XS_IDC(c) ((c)->flags & \
76 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
78 #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
80 #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
82 #define XML_PAT_COPY_NSNAME(c, r, nsname) \
83 if ((c)->comp->dict) \
84 r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
85 else r = xmlStrdup(BAD_CAST nsname);
87 #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
89 typedef struct _xmlStreamStep xmlStreamStep;
90 typedef xmlStreamStep *xmlStreamStepPtr;
91 struct _xmlStreamStep {
92 int flags; /* properties of that step */
93 const xmlChar *name; /* first string value if NULL accept all */
94 const xmlChar *ns; /* second string value */
95 int nodeType; /* type of node */
98 typedef struct _xmlStreamComp xmlStreamComp;
99 typedef xmlStreamComp *xmlStreamCompPtr;
100 struct _xmlStreamComp {
101 xmlDict *dict; /* the dictionary if any */
102 int nbStep; /* number of steps in the automata */
103 int maxStep; /* allocated number of steps */
104 xmlStreamStepPtr steps; /* the array of steps */
108 struct _xmlStreamCtxt {
109 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
110 xmlStreamCompPtr comp; /* the compiled stream */
111 int nbState; /* number of states in the automata */
112 int maxState; /* allocated number of states */
113 int level; /* how deep are we ? */
114 int *states; /* the array of step indexes */
115 int flags; /* validation options */
119 static void xmlFreeStreamComp(xmlStreamCompPtr comp);
138 typedef struct _xmlStepState xmlStepState;
139 typedef xmlStepState *xmlStepStatePtr;
140 struct _xmlStepState {
145 typedef struct _xmlStepStates xmlStepStates;
146 typedef xmlStepStates *xmlStepStatesPtr;
147 struct _xmlStepStates {
150 xmlStepStatePtr states;
153 typedef struct _xmlStepOp xmlStepOp;
154 typedef xmlStepOp *xmlStepOpPtr;
157 const xmlChar *value;
158 const xmlChar *value2; /* The namespace name */
161 #define PAT_FROM_ROOT (1<<8)
162 #define PAT_FROM_CUR (1<<9)
165 void *data; /* the associated template */
166 xmlDictPtr dict; /* the optional dictionary */
167 struct _xmlPattern *next; /* next pattern if | is used */
168 const xmlChar *pattern; /* the pattern */
169 int flags; /* flags */
172 xmlStepOpPtr steps; /* ops for computation */
173 xmlStreamCompPtr stream; /* the streaming data if any */
176 typedef struct _xmlPatParserContext xmlPatParserContext;
177 typedef xmlPatParserContext *xmlPatParserContextPtr;
178 struct _xmlPatParserContext {
179 const xmlChar *cur; /* the current char being parsed */
180 const xmlChar *base; /* the full expression */
181 int error; /* error code */
182 xmlDictPtr dict; /* the dictionary if any */
183 xmlPatternPtr comp; /* the result */
184 xmlNodePtr elem; /* the current node if any */
185 const xmlChar **namespaces; /* the namespaces definitions */
186 int nb_namespaces; /* the number of namespaces */
189 /************************************************************************
193 ************************************************************************/
198 * Create a new XSLT Pattern
200 * Returns the newly allocated xmlPatternPtr or NULL in case of error
203 xmlNewPattern(void) {
206 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
208 ERROR(NULL, NULL, NULL,
209 "xmlNewPattern : malloc failed\n");
212 memset(cur, 0, sizeof(xmlPattern));
214 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
215 if (cur->steps == NULL) {
217 ERROR(NULL, NULL, NULL,
218 "xmlNewPattern : malloc failed\n");
226 * @comp: an XSLT comp
228 * Free up the memory allocated by @comp
231 xmlFreePattern(xmlPatternPtr comp) {
237 if (comp->next != NULL)
238 xmlFreePattern(comp->next);
239 if (comp->stream != NULL)
240 xmlFreeStreamComp(comp->stream);
241 if (comp->pattern != NULL)
242 xmlFree((xmlChar *)comp->pattern);
243 if (comp->steps != NULL) {
244 if (comp->dict == NULL) {
245 for (i = 0;i < comp->nbStep;i++) {
246 op = &comp->steps[i];
247 if (op->value != NULL)
248 xmlFree((xmlChar *) op->value);
249 if (op->value2 != NULL)
250 xmlFree((xmlChar *) op->value2);
253 xmlFree(comp->steps);
255 if (comp->dict != NULL)
256 xmlDictFree(comp->dict);
258 memset(comp, -1, sizeof(xmlPattern));
263 * xmlFreePatternList:
264 * @comp: an XSLT comp list
266 * Free up the memory allocated by all the elements of @comp
269 xmlFreePatternList(xmlPatternPtr comp) {
272 while (comp != NULL) {
281 * xmlNewPatParserContext:
282 * @pattern: the pattern context
283 * @dict: the inherited dictionary or NULL
284 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
285 * with [NULL, NULL] or NULL if no namespace is used
287 * Create a new XML pattern parser context
289 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
291 static xmlPatParserContextPtr
292 xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
293 const xmlChar **namespaces) {
294 xmlPatParserContextPtr cur;
299 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
301 ERROR(NULL, NULL, NULL,
302 "xmlNewPatParserContext : malloc failed\n");
305 memset(cur, 0, sizeof(xmlPatParserContext));
309 if (namespaces != NULL) {
311 for (i = 0;namespaces[2 * i] != NULL;i++)
313 cur->nb_namespaces = i;
315 cur->nb_namespaces = 0;
317 cur->namespaces = namespaces;
322 * xmlFreePatParserContext:
323 * @ctxt: an XSLT parser context
325 * Free up the memory allocated by @ctxt
328 xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
331 memset(ctxt, -1, sizeof(xmlPatParserContext));
337 * @comp: the compiled match expression
339 * @value: the first value
340 * @value2: the second value
342 * Add a step to an XSLT Compiled Match
344 * Returns -1 in case of failure, 0 otherwise.
347 xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
349 xmlPatOp op, xmlChar * value, xmlChar * value2)
351 if (comp->nbStep >= comp->maxStep) {
353 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
356 ERROR(ctxt, NULL, NULL,
357 "xmlPatternAdd: realloc failed\n");
363 comp->steps[comp->nbStep].op = op;
364 comp->steps[comp->nbStep].value = value;
365 comp->steps[comp->nbStep].value2 = value2;
372 * xsltSwapTopPattern:
373 * @comp: the compiled match expression
375 * reverse the two top steps.
378 xsltSwapTopPattern(xmlPatternPtr comp) {
380 int j = comp->nbStep - 1;
383 register const xmlChar *tmp;
384 register xmlPatOp op;
386 tmp = comp->steps[i].value;
387 comp->steps[i].value = comp->steps[j].value;
388 comp->steps[j].value = tmp;
389 tmp = comp->steps[i].value2;
390 comp->steps[i].value2 = comp->steps[j].value2;
391 comp->steps[j].value2 = tmp;
392 op = comp->steps[i].op;
393 comp->steps[i].op = comp->steps[j].op;
394 comp->steps[j].op = op;
401 * @comp: the compiled match expression
403 * reverse all the stack of expressions
405 * returns 0 in case of success and -1 in case of error.
408 xmlReversePattern(xmlPatternPtr comp) {
412 * remove the leading // for //a or .//a
414 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
415 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
416 comp->steps[i].value = comp->steps[j].value;
417 comp->steps[i].value2 = comp->steps[j].value2;
418 comp->steps[i].op = comp->steps[j].op;
422 if (comp->nbStep >= comp->maxStep) {
424 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
427 ERROR(ctxt, NULL, NULL,
428 "xmlReversePattern: realloc failed\n");
435 j = comp->nbStep - 1;
437 register const xmlChar *tmp;
438 register xmlPatOp op;
439 tmp = comp->steps[i].value;
440 comp->steps[i].value = comp->steps[j].value;
441 comp->steps[j].value = tmp;
442 tmp = comp->steps[i].value2;
443 comp->steps[i].value2 = comp->steps[j].value2;
444 comp->steps[j].value2 = tmp;
445 op = comp->steps[i].op;
446 comp->steps[i].op = comp->steps[j].op;
447 comp->steps[j].op = op;
451 comp->steps[comp->nbStep].value = NULL;
452 comp->steps[comp->nbStep].value2 = NULL;
453 comp->steps[comp->nbStep++].op = XML_OP_END;
457 /************************************************************************
459 * The interpreter for the precompiled patterns *
461 ************************************************************************/
464 xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
465 if ((states->states == NULL) || (states->maxstates <= 0)) {
466 states->maxstates = 4;
467 states->nbstates = 0;
468 states->states = xmlMalloc(4 * sizeof(xmlStepState));
470 else if (states->maxstates <= states->nbstates) {
473 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
474 2 * states->maxstates * sizeof(xmlStepState));
477 states->states = tmp;
478 states->maxstates *= 2;
480 states->states[states->nbstates].step = step;
481 states->states[states->nbstates++].node = node;
483 fprintf(stderr, "Push: %d, %s\n", step, node->name);
490 * @comp: the precompiled pattern
493 * Test whether the node matches the pattern
495 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
498 xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
501 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
503 if ((comp == NULL) || (node == NULL)) return(-1);
506 for (;i < comp->nbStep;i++) {
507 step = &comp->steps[i];
512 if (node->type == XML_NAMESPACE_DECL)
515 if ((node->type == XML_DOCUMENT_NODE) ||
516 #ifdef LIBXML_DOCB_ENABLED
517 (node->type == XML_DOCB_DOCUMENT_NODE) ||
519 (node->type == XML_HTML_DOCUMENT_NODE))
523 if (node->type != XML_ELEMENT_NODE)
525 if (step->value == NULL)
527 if (step->value[0] != node->name[0])
529 if (!xmlStrEqual(step->value, node->name))
533 if (node->ns == NULL) {
534 if (step->value2 != NULL)
536 } else if (node->ns->href != NULL) {
537 if (step->value2 == NULL)
539 if (!xmlStrEqual(step->value2, node->ns->href))
546 if ((node->type != XML_ELEMENT_NODE) &&
547 (node->type != XML_DOCUMENT_NODE) &&
548 #ifdef LIBXML_DOCB_ENABLED
549 (node->type != XML_DOCB_DOCUMENT_NODE) &&
551 (node->type != XML_HTML_DOCUMENT_NODE))
554 lst = node->children;
556 if (step->value != NULL) {
557 while (lst != NULL) {
558 if ((lst->type == XML_ELEMENT_NODE) &&
559 (step->value[0] == lst->name[0]) &&
560 (xmlStrEqual(step->value, lst->name)))
570 if (node->type != XML_ATTRIBUTE_NODE)
572 if (step->value != NULL) {
573 if (step->value[0] != node->name[0])
575 if (!xmlStrEqual(step->value, node->name))
579 if (node->ns == NULL) {
580 if (step->value2 != NULL)
582 } else if (step->value2 != NULL) {
583 if (!xmlStrEqual(step->value2, node->ns->href))
588 if ((node->type == XML_DOCUMENT_NODE) ||
589 (node->type == XML_HTML_DOCUMENT_NODE) ||
590 #ifdef LIBXML_DOCB_ENABLED
591 (node->type == XML_DOCB_DOCUMENT_NODE) ||
593 (node->type == XML_NAMESPACE_DECL))
598 if (step->value == NULL)
600 if (step->value[0] != node->name[0])
602 if (!xmlStrEqual(step->value, node->name))
605 if (node->ns == NULL) {
606 if (step->value2 != NULL)
608 } else if (node->ns->href != NULL) {
609 if (step->value2 == NULL)
611 if (!xmlStrEqual(step->value2, node->ns->href))
615 case XML_OP_ANCESTOR:
616 /* TODO: implement coalescing of ANCESTOR/NODE ops */
617 if (step->value == NULL) {
619 step = &comp->steps[i];
620 if (step->op == XML_OP_ROOT)
622 if (step->op != XML_OP_ELEM)
624 if (step->value == NULL)
629 if ((node->type == XML_DOCUMENT_NODE) ||
630 (node->type == XML_HTML_DOCUMENT_NODE) ||
631 #ifdef LIBXML_DOCB_ENABLED
632 (node->type == XML_DOCB_DOCUMENT_NODE) ||
634 (node->type == XML_NAMESPACE_DECL))
637 while (node != NULL) {
638 if ((node->type == XML_ELEMENT_NODE) &&
639 (step->value[0] == node->name[0]) &&
640 (xmlStrEqual(step->value, node->name))) {
642 if (node->ns == NULL) {
643 if (step->value2 == NULL)
645 } else if (node->ns->href != NULL) {
646 if ((step->value2 != NULL) &&
647 (xmlStrEqual(step->value2, node->ns->href)))
656 * prepare a potential rollback from here
657 * for ancestors of that node.
659 if (step->op == XML_OP_ANCESTOR)
660 xmlPatPushState(&states, i, node);
662 xmlPatPushState(&states, i - 1, node);
665 if (node->type != XML_ELEMENT_NODE)
667 if (node->ns == NULL) {
668 if (step->value != NULL)
670 } else if (node->ns->href != NULL) {
671 if (step->value == NULL)
673 if (!xmlStrEqual(step->value, node->ns->href))
678 if (node->type != XML_ELEMENT_NODE)
684 if (states.states != NULL) {
685 /* Free the rollback states */
686 xmlFree(states.states);
690 /* got an error try to rollback */
691 if (states.states == NULL)
693 if (states.nbstates <= 0) {
694 xmlFree(states.states);
698 i = states.states[states.nbstates].step;
699 node = states.states[states.nbstates].node;
701 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
706 /************************************************************************
708 * Dedicated parser for templates *
710 ************************************************************************/
713 xmlGenericError(xmlGenericErrorContext, \
714 "Unimplemented block at %s:%d\n", \
716 #define CUR (*ctxt->cur)
717 #define SKIP(val) ctxt->cur += (val)
718 #define NXT(val) ctxt->cur[(val)]
719 #define PEEKPREV(val) ctxt->cur[-(val)]
720 #define CUR_PTR ctxt->cur
722 #define SKIP_BLANKS \
723 while (IS_BLANK_CH(CUR)) NEXT
725 #define CURRENT (*ctxt->cur)
726 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
729 #define PUSH(op, val, val2) \
730 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
732 #define XSLT_ERROR(X) \
733 { xsltError(ctxt, __FILE__, __LINE__, X); \
734 ctxt->error = (X); return; }
736 #define XSLT_ERROR0(X) \
737 { xsltError(ctxt, __FILE__, __LINE__, X); \
738 ctxt->error = (X); return(0); }
743 * @ctxt: the XPath Parser context
745 * Parse an XPath Litteral:
747 * [29] Literal ::= '"' [^"]* '"'
750 * Returns the Literal parsed or NULL
754 xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
755 const xmlChar *q, *cur;
763 val = xmlStringCurrentChar(NULL, cur, &len);
764 while ((IS_CHAR(val)) && (val != '"')) {
766 val = xmlStringCurrentChar(NULL, cur, &len);
773 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
775 ret = xmlStrndup(q, cur - q);
779 } else if (CUR == '\'') {
782 val = xmlStringCurrentChar(NULL, cur, &len);
783 while ((IS_CHAR(val)) && (val != '\'')) {
785 val = xmlStringCurrentChar(NULL, cur, &len);
792 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
794 ret = xmlStrndup(q, cur - q);
799 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
809 * @ctxt: the XPath Parser context
811 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
812 * CombiningChar | Extender
814 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
816 * [6] Names ::= Name (S Name)*
818 * Returns the Name parsed or NULL
822 xmlPatScanName(xmlPatParserContextPtr ctxt) {
823 const xmlChar *q, *cur;
830 val = xmlStringCurrentChar(NULL, cur, &len);
831 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
834 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
835 (val == '.') || (val == '-') ||
837 (IS_COMBINING(val)) ||
838 (IS_EXTENDER(val))) {
840 val = xmlStringCurrentChar(NULL, cur, &len);
843 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
845 ret = xmlStrndup(q, cur - q);
852 * @ctxt: the XPath Parser context
854 * Parses a non qualified name
856 * Returns the Name parsed or NULL
860 xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
861 const xmlChar *q, *cur;
868 val = xmlStringCurrentChar(NULL, cur, &len);
869 if (!IS_LETTER(val) && (val != '_'))
872 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
873 (val == '.') || (val == '-') ||
875 (IS_COMBINING(val)) ||
876 (IS_EXTENDER(val))) {
878 val = xmlStringCurrentChar(NULL, cur, &len);
881 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
883 ret = xmlStrndup(q, cur - q);
891 * @ctxt: the XPath Parser context
892 * @prefix: the place to store the prefix
894 * Parse a qualified name
896 * Returns the Name parsed or NULL
900 xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
904 ret = xmlPatScanNCName(ctxt);
908 ret = xmlPatScanNCName(ctxt);
915 * xmlCompileAttributeTest:
916 * @ctxt: the compilation context
918 * Compile an attribute test.
921 xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
922 xmlChar *token = NULL;
923 xmlChar *name = NULL;
927 name = xmlPatScanNCName(ctxt);
930 PUSH(XML_OP_ATTR, NULL, NULL);
933 ERROR(NULL, NULL, NULL,
934 "xmlCompileAttributeTest : Name expected\n");
941 xmlChar *prefix = name;
945 if (IS_BLANK_CH(CUR)) {
946 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
947 XML_PAT_FREE_STRING(ctxt, prefix);
952 * This is a namespace match
954 token = xmlPatScanName(ctxt);
955 if ((prefix[0] == 'x') &&
956 (prefix[1] == 'm') &&
957 (prefix[2] == 'l') &&
960 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
962 for (i = 0;i < ctxt->nb_namespaces;i++) {
963 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
964 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
968 if (i >= ctxt->nb_namespaces) {
969 ERROR5(NULL, NULL, NULL,
970 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
972 XML_PAT_FREE_STRING(ctxt, prefix);
977 XML_PAT_FREE_STRING(ctxt, prefix);
981 PUSH(XML_OP_ATTR, NULL, URL);
983 ERROR(NULL, NULL, NULL,
984 "xmlCompileAttributeTest : Name expected\n");
989 PUSH(XML_OP_ATTR, token, URL);
992 PUSH(XML_OP_ATTR, name, NULL);
997 XML_PAT_FREE_STRING(ctxt, URL)
999 XML_PAT_FREE_STRING(ctxt, token);
1003 * xmlCompileStepPattern:
1004 * @ctxt: the compilation context
1006 * Compile the Step Pattern and generates a precompiled
1007 * form suitable for fast matching.
1009 * [3] Step ::= '.' | NameTest
1010 * [4] NameTest ::= QName | '*' | NCName ':' '*'
1014 xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1015 xmlChar *token = NULL;
1016 xmlChar *name = NULL;
1017 xmlChar *URL = NULL;
1026 PUSH(XML_OP_ELEM, NULL, NULL);
1033 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1034 ERROR5(NULL, NULL, NULL,
1035 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1040 xmlCompileAttributeTest(ctxt);
1041 if (ctxt->error != 0)
1045 name = xmlPatScanNCName(ctxt);
1049 PUSH(XML_OP_ALL, NULL, NULL);
1052 ERROR(NULL, NULL, NULL,
1053 "xmlCompileStepPattern : Name expected\n");
1058 if (IS_BLANK_CH(CUR)) {
1065 xmlChar *prefix = name;
1068 if (hasBlanks || IS_BLANK_CH(CUR)) {
1069 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1074 * This is a namespace match
1076 token = xmlPatScanName(ctxt);
1077 if ((prefix[0] == 'x') &&
1078 (prefix[1] == 'm') &&
1079 (prefix[2] == 'l') &&
1082 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1084 for (i = 0;i < ctxt->nb_namespaces;i++) {
1085 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1086 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1090 if (i >= ctxt->nb_namespaces) {
1091 ERROR5(NULL, NULL, NULL,
1092 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1098 XML_PAT_FREE_STRING(ctxt, prefix);
1100 if (token == NULL) {
1103 PUSH(XML_OP_NS, URL, NULL);
1105 ERROR(NULL, NULL, NULL,
1106 "xmlCompileStepPattern : Name expected\n");
1111 PUSH(XML_OP_ELEM, token, URL);
1115 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1116 XML_PAT_FREE_STRING(ctxt, name);
1117 name = xmlPatScanName(ctxt);
1121 PUSH(XML_OP_ALL, NULL, NULL);
1124 ERROR(NULL, NULL, NULL,
1125 "xmlCompileStepPattern : QName expected\n");
1131 xmlChar *prefix = name;
1135 if (IS_BLANK_CH(CUR)) {
1136 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1141 * This is a namespace match
1143 token = xmlPatScanName(ctxt);
1144 if ((prefix[0] == 'x') &&
1145 (prefix[1] == 'm') &&
1146 (prefix[2] == 'l') &&
1149 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1151 for (i = 0;i < ctxt->nb_namespaces;i++) {
1152 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1153 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1157 if (i >= ctxt->nb_namespaces) {
1158 ERROR5(NULL, NULL, NULL,
1159 "xmlCompileStepPattern : no namespace bound "
1160 "to prefix %s\n", prefix);
1165 XML_PAT_FREE_STRING(ctxt, prefix);
1167 if (token == NULL) {
1170 PUSH(XML_OP_NS, URL, NULL);
1172 ERROR(NULL, NULL, NULL,
1173 "xmlCompileStepPattern : Name expected\n");
1178 PUSH(XML_OP_CHILD, token, URL);
1181 PUSH(XML_OP_CHILD, name, NULL);
1183 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1184 XML_PAT_FREE_STRING(ctxt, name)
1186 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1187 ERROR5(NULL, NULL, NULL,
1188 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1192 xmlCompileAttributeTest(ctxt);
1193 if (ctxt->error != 0)
1197 ERROR5(NULL, NULL, NULL,
1198 "The 'element' or 'attribute' axis is expected.\n", NULL);
1203 } else if (CUR == '*') {
1209 PUSH(XML_OP_ALL, token, NULL);
1211 PUSH(XML_OP_ELEM, name, NULL);
1216 XML_PAT_FREE_STRING(ctxt, URL)
1218 XML_PAT_FREE_STRING(ctxt, token)
1220 XML_PAT_FREE_STRING(ctxt, name)
1224 * xmlCompilePathPattern:
1225 * @ctxt: the compilation context
1227 * Compile the Path Pattern and generates a precompiled
1228 * form suitable for fast matching.
1230 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1233 xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1236 ctxt->comp->flags |= PAT_FROM_ROOT;
1237 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1238 ctxt->comp->flags |= PAT_FROM_CUR;
1241 if ((CUR == '/') && (NXT(1) == '/')) {
1242 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1245 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1246 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1250 /* Check for incompleteness. */
1253 ERROR5(NULL, NULL, NULL,
1254 "Incomplete expression '%s'.\n", ctxt->base);
1261 xmlCompileAttributeTest(ctxt);
1263 /* TODO: check for incompleteness */
1265 xmlCompileStepPattern(ctxt);
1266 if (ctxt->error != 0)
1271 PUSH(XML_OP_ROOT, NULL, NULL);
1273 /* Check for incompleteness. */
1276 ERROR5(NULL, NULL, NULL,
1277 "Incomplete expression '%s'.\n", ctxt->base);
1282 xmlCompileStepPattern(ctxt);
1283 if (ctxt->error != 0)
1286 while (CUR == '/') {
1287 if (NXT(1) == '/') {
1288 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1292 xmlCompileStepPattern(ctxt);
1293 if (ctxt->error != 0)
1296 PUSH(XML_OP_PARENT, NULL, NULL);
1300 ERROR5(NULL, NULL, NULL,
1301 "Incomplete expression '%s'.\n", ctxt->base);
1305 xmlCompileStepPattern(ctxt);
1306 if (ctxt->error != 0)
1312 ERROR5(NULL, NULL, NULL,
1313 "Failed to compile pattern %s\n", ctxt->base);
1321 * xmlCompileIDCXPathPath:
1322 * @ctxt: the compilation context
1324 * Compile the Path Pattern and generates a precompiled
1325 * form suitable for fast matching.
1327 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1330 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1333 ERROR5(NULL, NULL, NULL,
1334 "Unexpected selection of the document root in '%s'.\n",
1338 ctxt->comp->flags |= PAT_FROM_CUR;
1341 /* "." - "self::node()" */
1346 * Selection of the context node.
1348 PUSH(XML_OP_ELEM, NULL, NULL);
1352 /* TODO: A more meaningful error message. */
1353 ERROR5(NULL, NULL, NULL,
1354 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1357 /* "./" - "self::node()/" */
1361 if (IS_BLANK_CH(PEEKPREV(1))) {
1365 ERROR5(NULL, NULL, NULL,
1366 "Unexpected '/' token in '%s'.\n", ctxt->base);
1369 /* ".//" - "self:node()/descendant-or-self::node()/" */
1370 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1375 goto error_unfinished;
1381 xmlCompileStepPattern(ctxt);
1382 if (ctxt->error != 0)
1387 PUSH(XML_OP_PARENT, NULL, NULL);
1392 * Disallow subsequent '//'.
1394 ERROR5(NULL, NULL, NULL,
1395 "Unexpected subsequent '//' in '%s'.\n",
1400 goto error_unfinished;
1405 ERROR5(NULL, NULL, NULL,
1406 "Failed to compile expression '%s'.\n", ctxt->base);
1416 ERROR5(NULL, NULL, NULL,
1417 "Unfinished expression '%s'.\n", ctxt->base);
1421 /************************************************************************
1423 * The streaming code *
1425 ************************************************************************/
1427 #ifdef DEBUG_STREAMING
1429 xmlDebugStreamComp(xmlStreamCompPtr stream) {
1432 if (stream == NULL) {
1433 printf("Stream: NULL\n");
1436 printf("Stream: %d steps\n", stream->nbStep);
1437 for (i = 0;i < stream->nbStep;i++) {
1438 if (stream->steps[i].ns != NULL) {
1439 printf("{%s}", stream->steps[i].ns);
1441 if (stream->steps[i].name == NULL) {
1444 printf("%s ", stream->steps[i].name);
1446 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1448 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1450 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1456 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1460 printf("Stream: NULL\n");
1463 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1465 printf("matches\n");
1468 for (i = 0;i < ctxt->nbState;i++) {
1469 if (ctxt->states[2 * i] < 0)
1470 printf(" %d: free\n", i);
1472 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1473 ctxt->states[(2 * i) + 1]);
1474 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1475 XML_STREAM_STEP_DESC)
1485 * @size: the number of expected steps
1487 * build a new compiled pattern for streaming
1489 * Returns the new structure or NULL in case of error.
1491 static xmlStreamCompPtr
1492 xmlNewStreamComp(int size) {
1493 xmlStreamCompPtr cur;
1498 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1500 ERROR(NULL, NULL, NULL,
1501 "xmlNewStreamComp: malloc failed\n");
1504 memset(cur, 0, sizeof(xmlStreamComp));
1505 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1506 if (cur->steps == NULL) {
1508 ERROR(NULL, NULL, NULL,
1509 "xmlNewStreamComp: malloc failed\n");
1513 cur->maxStep = size;
1518 * xmlFreeStreamComp:
1519 * @comp: the compiled pattern for streaming
1521 * Free the compiled pattern for streaming
1524 xmlFreeStreamComp(xmlStreamCompPtr comp) {
1526 if (comp->steps != NULL)
1527 xmlFree(comp->steps);
1528 if (comp->dict != NULL)
1529 xmlDictFree(comp->dict);
1535 * xmlStreamCompAddStep:
1536 * @comp: the compiled pattern for streaming
1537 * @name: the first string, the name, or NULL for *
1538 * @ns: the second step, the namespace name
1539 * @flags: the flags for that step
1541 * Add a new step to the compiled pattern
1543 * Returns -1 in case of error or the step index if successful
1546 xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1547 const xmlChar *ns, int nodeType, int flags) {
1548 xmlStreamStepPtr cur;
1550 if (comp->nbStep >= comp->maxStep) {
1551 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1552 comp->maxStep * 2 * sizeof(xmlStreamStep));
1554 ERROR(NULL, NULL, NULL,
1555 "xmlNewStreamComp: malloc failed\n");
1561 cur = &comp->steps[comp->nbStep++];
1565 cur->nodeType = nodeType;
1566 return(comp->nbStep - 1);
1571 * @comp: the precompiled pattern
1573 * Tries to stream compile a pattern
1575 * Returns -1 in case of failure and 0 in case of success.
1578 xmlStreamCompile(xmlPatternPtr comp) {
1579 xmlStreamCompPtr stream;
1580 int i, s = 0, root = 0, flags = 0, prevs = -1;
1583 if ((comp == NULL) || (comp->steps == NULL))
1586 * special case for .
1588 if ((comp->nbStep == 1) &&
1589 (comp->steps[0].op == XML_OP_ELEM) &&
1590 (comp->steps[0].value == NULL) &&
1591 (comp->steps[0].value2 == NULL)) {
1592 stream = xmlNewStreamComp(0);
1595 /* Note that the stream will have no steps in this case. */
1596 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1597 comp->stream = stream;
1601 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1604 if (comp->dict != NULL) {
1605 stream->dict = comp->dict;
1606 xmlDictReference(stream->dict);
1610 if (comp->flags & PAT_FROM_ROOT)
1611 stream->flags |= XML_STREAM_FROM_ROOT;
1613 for (;i < comp->nbStep;i++) {
1614 step = comp->steps[i];
1624 s = xmlStreamCompAddStep(stream, NULL, step.value,
1625 XML_ELEMENT_NODE, flags);
1632 flags |= XML_STREAM_STEP_ATTR;
1634 s = xmlStreamCompAddStep(stream,
1635 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1641 if ((step.value == NULL) && (step.value2 == NULL)) {
1643 * We have a "." or "self::node()" here.
1644 * Eliminate redundant self::node() tests like in "/./."
1646 * The only case we won't eliminate is "//.", i.e. if
1647 * self::node() is the last node test and we had
1648 * continuation somewhere beforehand.
1650 if ((comp->nbStep == i + 1) &&
1651 (flags & XML_STREAM_STEP_DESC)) {
1653 * Mark the special case where the expression resolves
1654 * to any type of node.
1656 if (comp->nbStep == i + 1) {
1657 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1659 flags |= XML_STREAM_STEP_NODE;
1660 s = xmlStreamCompAddStep(stream, NULL, NULL,
1661 XML_STREAM_ANY_NODE, flags);
1666 * If there was a previous step, mark it to be added to
1667 * the result node-set; this is needed since only
1668 * the last step will be marked as "final" and only
1669 * "final" nodes are added to the resulting set.
1672 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1678 /* Just skip this one. */
1682 /* An element node. */
1683 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1684 XML_ELEMENT_NODE, flags);
1691 /* An element node child. */
1692 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1693 XML_ELEMENT_NODE, flags);
1700 s = xmlStreamCompAddStep(stream, NULL, NULL,
1701 XML_ELEMENT_NODE, flags);
1709 case XML_OP_ANCESTOR:
1710 /* Skip redundant continuations. */
1711 if (flags & XML_STREAM_STEP_DESC)
1713 flags |= XML_STREAM_STEP_DESC;
1715 * Mark the expression as having "//".
1717 if ((stream->flags & XML_STREAM_DESC) == 0)
1718 stream->flags |= XML_STREAM_DESC;
1722 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1724 * If this should behave like a real pattern, we will mark
1725 * the first step as having "//", to be reentrant on every
1728 if ((stream->flags & XML_STREAM_DESC) == 0)
1729 stream->flags |= XML_STREAM_DESC;
1731 if (stream->nbStep > 0) {
1732 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1733 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1736 if (stream->nbStep <= s)
1738 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1740 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1741 #ifdef DEBUG_STREAMING
1742 xmlDebugStreamComp(stream);
1744 comp->stream = stream;
1747 xmlFreeStreamComp(stream);
1753 * @size: the number of expected states
1755 * build a new stream context
1757 * Returns the new structure or NULL in case of error.
1759 static xmlStreamCtxtPtr
1760 xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1761 xmlStreamCtxtPtr cur;
1763 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1765 ERROR(NULL, NULL, NULL,
1766 "xmlNewStreamCtxt: malloc failed\n");
1769 memset(cur, 0, sizeof(xmlStreamCtxt));
1770 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1771 if (cur->states == NULL) {
1773 ERROR(NULL, NULL, NULL,
1774 "xmlNewStreamCtxt: malloc failed\n");
1781 cur->blockLevel = -1;
1786 * xmlFreeStreamCtxt:
1787 * @stream: the stream context
1789 * Free the stream context
1792 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1793 xmlStreamCtxtPtr next;
1795 while (stream != NULL) {
1796 next = stream->next;
1797 if (stream->states != NULL)
1798 xmlFree(stream->states);
1805 * xmlStreamCtxtAddState:
1806 * @comp: the stream context
1807 * @idx: the step index for that streaming state
1809 * Add a new state to the stream context
1811 * Returns -1 in case of error or the state index if successful
1814 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1816 for (i = 0;i < comp->nbState;i++) {
1817 if (comp->states[2 * i] < 0) {
1818 comp->states[2 * i] = idx;
1819 comp->states[2 * i + 1] = level;
1823 if (comp->nbState >= comp->maxState) {
1826 cur = (int *) xmlRealloc(comp->states,
1827 comp->maxState * 4 * sizeof(int));
1829 ERROR(NULL, NULL, NULL,
1830 "xmlNewStreamCtxt: malloc failed\n");
1834 comp->maxState *= 2;
1836 comp->states[2 * comp->nbState] = idx;
1837 comp->states[2 * comp->nbState++ + 1] = level;
1838 return(comp->nbState - 1);
1842 * xmlStreamPushInternal:
1843 * @stream: the stream context
1844 * @name: the current name
1845 * @ns: the namespace name
1846 * @nodeType: the type of the node
1848 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1849 * indicated a dictionary, then strings for name and ns will be expected
1850 * to come from the dictionary.
1851 * Both @name and @ns being NULL means the / i.e. the root of the document.
1852 * This can also act as a reset.
1854 * Returns: -1 in case of error, 1 if the current state in the stream is a
1855 * match and 0 otherwise.
1858 xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1859 const xmlChar *name, const xmlChar *ns,
1861 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1862 xmlStreamCompPtr comp;
1864 #ifdef DEBUG_STREAMING
1865 xmlStreamCtxtPtr orig = stream;
1868 if ((stream == NULL) || (stream->nbState < 0))
1871 while (stream != NULL) {
1872 comp = stream->comp;
1874 if ((nodeType == XML_ELEMENT_NODE) &&
1875 (name == NULL) && (ns == NULL)) {
1876 /* We have a document node here (or a reset). */
1877 stream->nbState = 0;
1879 stream->blockLevel = -1;
1880 if (comp->flags & XML_STREAM_FROM_ROOT) {
1881 if (comp->nbStep == 0) {
1882 /* TODO: We have a "/." here? */
1885 if ((comp->nbStep == 1) &&
1886 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1887 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1890 * In the case of "//." the document node will match
1894 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1895 /* TODO: Do we need this ? */
1896 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1902 stream = stream->next;
1903 continue; /* while */
1907 * Fast check for ".".
1909 if (comp->nbStep == 0) {
1911 * / and . are handled at the XPath node set creation
1912 * level by checking min depth
1914 if (stream->flags & XML_PATTERN_XPATH) {
1915 stream = stream->next;
1916 continue; /* while */
1919 * For non-pattern like evaluation like XML Schema IDCs
1920 * or traditional XPath expressions, this will match if
1921 * we are at the first level only, otherwise on every level.
1923 if ((nodeType != XML_ATTRIBUTE_NODE) &&
1924 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1925 (stream->level == 0))) {
1931 if (stream->blockLevel != -1) {
1933 * Skip blocked expressions.
1939 if ((nodeType != XML_ELEMENT_NODE) &&
1940 (nodeType != XML_ATTRIBUTE_NODE) &&
1941 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1943 * No need to process nodes of other types if we don't
1944 * resolve to those types.
1945 * TODO: Do we need to block the context here?
1952 * Check evolution of existing states
1955 m = stream->nbState;
1957 if ((comp->flags & XML_STREAM_DESC) == 0) {
1959 * If there is no "//", then only the last
1960 * added state is of interest.
1962 stepNr = stream->states[2 * (stream->nbState -1)];
1964 * TODO: Security check, should not happen, remove it.
1966 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1975 * If there are "//", then we need to process every "//"
1976 * occuring in the states, plus any other state for this
1979 stepNr = stream->states[2 * i];
1981 /* TODO: should not happen anymore: dead states */
1985 tmp = stream->states[(2 * i) + 1];
1987 /* skip new states just added */
1988 if (tmp > stream->level)
1991 /* skip states at ancestor levels, except if "//" */
1992 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1993 if ((tmp < stream->level) && (!desc))
1997 * Check for correct node-type.
1999 step = comp->steps[stepNr];
2000 if (step.nodeType != nodeType) {
2001 if (step.nodeType == XML_ATTRIBUTE_NODE) {
2003 * Block this expression for deeper evaluation.
2005 if ((comp->flags & XML_STREAM_DESC) == 0)
2006 stream->blockLevel = stream->level +1;
2008 } else if (step.nodeType != XML_STREAM_ANY_NODE)
2012 * Compare local/namespace-name.
2015 if (step.nodeType == XML_STREAM_ANY_NODE) {
2017 } else if (step.name == NULL) {
2018 if (step.ns == NULL) {
2020 * This lets through all elements/attributes.
2023 } else if (ns != NULL)
2024 match = xmlStrEqual(step.ns, ns);
2025 } else if (((step.ns != NULL) == (ns != NULL)) &&
2027 (step.name[0] == name[0]) &&
2028 xmlStrEqual(step.name, name) &&
2029 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2035 * TODO: Pointer comparison won't work, since not guaranteed that the given
2036 * values are in the same dict; especially if it's the namespace name,
2037 * normally coming from ns->href. We need a namespace dict mechanism !
2039 } else if (comp->dict) {
2040 if (step.name == NULL) {
2041 if (step.ns == NULL)
2044 match = (step.ns == ns);
2046 match = ((step.name == name) && (step.ns == ns));
2048 #endif /* if 0 ------------------------------------------------------- */
2050 final = step.flags & XML_STREAM_STEP_FINAL;
2055 /* descending match create a new state */
2056 xmlStreamCtxtAddState(stream, stepNr + 1,
2063 xmlStreamCtxtAddState(stream, stepNr + 1,
2067 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2069 * Check if we have a special case like "foo/bar//.", where
2070 * "foo" is selected as well.
2075 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2076 ((! match) || final)) {
2078 * Mark this expression as blocked for any evaluation at
2079 * deeper levels. Note that this includes "/foo"
2080 * expressions if the *pattern* behaviour is used.
2082 stream->blockLevel = stream->level +1;
2091 * Re/enter the expression.
2092 * Don't reenter if it's an absolute expression like "/foo",
2095 step = comp->steps[0];
2096 if (step.flags & XML_STREAM_STEP_ROOT)
2099 desc = step.flags & XML_STREAM_STEP_DESC;
2100 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2102 * Re/enter the expression if it is a "descendant" one,
2103 * or if we are at the 1st level of evaluation.
2106 if (stream->level == 1) {
2107 if (XML_STREAM_XS_IDC(stream)) {
2109 * XS-IDC: The missing "self::node()" will always
2110 * match the first given node.
2117 * A "//" is always reentrant.
2123 * XS-IDC: Process the 2nd level, since the missing
2124 * "self::node()" is responsible for the 2nd level being
2125 * the real start level.
2127 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2135 * Check expected node-type.
2137 if (step.nodeType != nodeType) {
2138 if (nodeType == XML_ATTRIBUTE_NODE)
2140 else if (step.nodeType != XML_STREAM_ANY_NODE)
2144 * Compare local/namespace-name.
2147 if (step.nodeType == XML_STREAM_ANY_NODE) {
2149 } else if (step.name == NULL) {
2150 if (step.ns == NULL) {
2152 * This lets through all elements/attributes.
2155 } else if (ns != NULL)
2156 match = xmlStrEqual(step.ns, ns);
2157 } else if (((step.ns != NULL) == (ns != NULL)) &&
2159 (step.name[0] == name[0]) &&
2160 xmlStrEqual(step.name, name) &&
2161 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2165 final = step.flags & XML_STREAM_STEP_FINAL;
2170 xmlStreamCtxtAddState(stream, 1, stream->level);
2171 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2173 * Check if we have a special case like "foo//.", where
2174 * "foo" is selected as well.
2179 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2180 ((! match) || final)) {
2182 * Mark this expression as blocked for any evaluation at
2185 stream->blockLevel = stream->level;
2189 stream = stream->next;
2190 } /* while stream != NULL */
2194 #ifdef DEBUG_STREAMING
2195 xmlDebugStreamCtxt(orig, ret);
2202 * @stream: the stream context
2203 * @name: the current name
2204 * @ns: the namespace name
2206 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2207 * indicated a dictionary, then strings for name and ns will be expected
2208 * to come from the dictionary.
2209 * Both @name and @ns being NULL means the / i.e. the root of the document.
2210 * This can also act as a reset.
2211 * Otherwise the function will act as if it has been given an element-node.
2213 * Returns: -1 in case of error, 1 if the current state in the stream is a
2214 * match and 0 otherwise.
2217 xmlStreamPush(xmlStreamCtxtPtr stream,
2218 const xmlChar *name, const xmlChar *ns) {
2219 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2223 * xmlStreamPushNode:
2224 * @stream: the stream context
2225 * @name: the current name
2226 * @ns: the namespace name
2227 * @nodeType: the type of the node being pushed
2229 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2230 * indicated a dictionary, then strings for name and ns will be expected
2231 * to come from the dictionary.
2232 * Both @name and @ns being NULL means the / i.e. the root of the document.
2233 * This can also act as a reset.
2234 * Different from xmlStreamPush() this function can be fed with nodes of type:
2235 * element-, attribute-, text-, cdata-section-, comment- and
2236 * processing-instruction-node.
2238 * Returns: -1 in case of error, 1 if the current state in the stream is a
2239 * match and 0 otherwise.
2242 xmlStreamPushNode(xmlStreamCtxtPtr stream,
2243 const xmlChar *name, const xmlChar *ns,
2246 return (xmlStreamPushInternal(stream, name, ns,
2251 * xmlStreamPushAttr:
2252 * @stream: the stream context
2253 * @name: the current name
2254 * @ns: the namespace name
2256 * Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2257 * indicated a dictionary, then strings for name and ns will be expected
2258 * to come from the dictionary.
2259 * Both @name and @ns being NULL means the / i.e. the root of the document.
2260 * This can also act as a reset.
2261 * Otherwise the function will act as if it has been given an attribute-node.
2263 * Returns: -1 in case of error, 1 if the current state in the stream is a
2264 * match and 0 otherwise.
2267 xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2268 const xmlChar *name, const xmlChar *ns) {
2269 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
2274 * @stream: the stream context
2276 * push one level from the stream.
2278 * Returns: -1 in case of error, 0 otherwise.
2281 xmlStreamPop(xmlStreamCtxtPtr stream) {
2286 while (stream != NULL) {
2288 * Reset block-level.
2290 if (stream->blockLevel == stream->level)
2291 stream->blockLevel = -1;
2294 * stream->level can be zero when XML_FINAL_IS_ANY_NODE is set
2295 * (see the thread at
2296 * http://mail.gnome.org/archives/xslt/2008-July/msg00027.html)
2301 * Check evolution of existing states
2303 for (i = stream->nbState -1; i >= 0; i--) {
2304 /* discard obsoleted states */
2305 lev = stream->states[(2 * i) + 1];
2306 if (lev > stream->level)
2308 if (lev <= stream->level)
2311 stream = stream->next;
2317 * xmlStreamWantsAnyNode:
2318 * @streamCtxt: the stream context
2320 * Query if the streaming pattern additionally needs to be fed with
2321 * text-, cdata-section-, comment- and processing-instruction-nodes.
2322 * If the result is 0 then only element-nodes and attribute-nodes
2323 * need to be pushed.
2325 * Returns: 1 in case of need of nodes of the above described types,
2326 * 0 otherwise. -1 on API errors.
2329 xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2331 if (streamCtxt == NULL)
2333 while (streamCtxt != NULL) {
2334 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2336 streamCtxt = streamCtxt->next;
2341 /************************************************************************
2343 * The public interfaces *
2345 ************************************************************************/
2348 * xmlPatterncompile:
2349 * @pattern: the pattern to compile
2350 * @dict: an optional dictionary for interned strings
2351 * @flags: compilation flags, see xmlPatternFlags
2352 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2354 * Compile a pattern.
2356 * Returns the compiled form of the pattern or NULL in case of error
2359 xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2360 const xmlChar **namespaces) {
2361 xmlPatternPtr ret = NULL, cur;
2362 xmlPatParserContextPtr ctxt = NULL;
2363 const xmlChar *or, *start;
2364 xmlChar *tmp = NULL;
2368 if (pattern == NULL)
2375 while ((*or != 0) && (*or != '|')) or++;
2377 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2379 tmp = xmlStrndup(start, or - start);
2381 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2385 if (ctxt == NULL) goto error;
2386 cur = xmlNewPattern();
2387 if (cur == NULL) goto error;
2389 * Assign string dict.
2393 xmlDictReference(dict);
2398 cur->next = ret->next;
2404 if (XML_STREAM_XS_IDC(cur))
2405 xmlCompileIDCXPathPath(ctxt);
2407 xmlCompilePathPattern(ctxt);
2408 if (ctxt->error != 0)
2410 xmlFreePatParserContext(ctxt);
2416 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2417 } else if (type == PAT_FROM_ROOT) {
2418 if (cur->flags & PAT_FROM_CUR)
2420 } else if (type == PAT_FROM_CUR) {
2421 if (cur->flags & PAT_FROM_ROOT)
2426 xmlStreamCompile(cur);
2427 if (xmlReversePattern(cur) < 0)
2435 if (streamable == 0) {
2437 while (cur != NULL) {
2438 if (cur->stream != NULL) {
2439 xmlFreeStreamComp(cur->stream);
2448 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2449 if (ret != NULL) xmlFreePattern(ret);
2450 if (tmp != NULL) xmlFree(tmp);
2456 * @comp: the precompiled pattern
2459 * Test whether the node matches the pattern
2461 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2464 xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2468 if ((comp == NULL) || (node == NULL))
2471 while (comp != NULL) {
2472 ret = xmlPatMatch(comp, node);
2481 * xmlPatternGetStreamCtxt:
2482 * @comp: the precompiled pattern
2484 * Get a streaming context for that pattern
2485 * Use xmlFreeStreamCtxt to free the context.
2487 * Returns a pointer to the context or NULL in case of failure
2490 xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2492 xmlStreamCtxtPtr ret = NULL, cur;
2494 if ((comp == NULL) || (comp->stream == NULL))
2497 while (comp != NULL) {
2498 if (comp->stream == NULL)
2500 cur = xmlNewStreamCtxt(comp->stream);
2506 cur->next = ret->next;
2509 cur->flags = comp->flags;
2514 xmlFreeStreamCtxt(ret);
2519 * xmlPatternStreamable:
2520 * @comp: the precompiled pattern
2522 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2525 * Returns 1 if streamable, 0 if not and -1 in case of error.
2528 xmlPatternStreamable(xmlPatternPtr comp) {
2531 while (comp != NULL) {
2532 if (comp->stream == NULL)
2540 * xmlPatternMaxDepth:
2541 * @comp: the precompiled pattern
2543 * Check the maximum depth reachable by a pattern
2545 * Returns -2 if no limit (using //), otherwise the depth,
2546 * and -1 in case of error
2549 xmlPatternMaxDepth(xmlPatternPtr comp) {
2553 while (comp != NULL) {
2554 if (comp->stream == NULL)
2556 for (i = 0;i < comp->stream->nbStep;i++)
2557 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2559 if (comp->stream->nbStep > ret)
2560 ret = comp->stream->nbStep;
2567 * xmlPatternMinDepth:
2568 * @comp: the precompiled pattern
2570 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2573 * Returns -1 in case of error otherwise the depth,
2577 xmlPatternMinDepth(xmlPatternPtr comp) {
2581 while (comp != NULL) {
2582 if (comp->stream == NULL)
2584 if (comp->stream->nbStep < ret)
2585 ret = comp->stream->nbStep;
2594 * xmlPatternFromRoot:
2595 * @comp: the precompiled pattern
2597 * Check if the pattern must be looked at from the root.
2599 * Returns 1 if true, 0 if false and -1 in case of error
2602 xmlPatternFromRoot(xmlPatternPtr comp) {
2605 while (comp != NULL) {
2606 if (comp->stream == NULL)
2608 if (comp->flags & PAT_FROM_ROOT)
2616 #define bottom_pattern
2617 #include "elfgcchack.h"
2618 #endif /* LIBXML_PATTERN_ENABLED */