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 */
42 #define ERROR(a, b, c, d)
43 #define ERROR5(a, b, c, d, e)
45 #define XML_STREAM_STEP_DESC 1
46 #define XML_STREAM_STEP_FINAL 2
47 #define XML_STREAM_STEP_ROOT 4
48 #define XML_STREAM_STEP_ATTR 8
49 #define XML_STREAM_STEP_NODE 16
50 #define XML_STREAM_STEP_IN_SET 32
53 * NOTE: Those private flags (XML_STREAM_xxx) are used
54 * in _xmlStreamCtxt->flag. They extend the public
55 * xmlPatternFlags, so be carefull not to interfere with the
56 * reserved values for xmlPatternFlags.
58 #define XML_STREAM_FINAL_IS_ANY_NODE 1<<14
59 #define XML_STREAM_FROM_ROOT 1<<15
60 #define XML_STREAM_DESC 1<<16
63 * XML_STREAM_ANY_NODE is used for comparison against
64 * xmlElementType enums, to indicate a node of any type.
66 #define XML_STREAM_ANY_NODE 100
68 #define XML_PATTERN_NOTPATTERN (XML_PATTERN_XPATH | \
72 #define XML_STREAM_XS_IDC(c) ((c)->flags & \
73 (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
75 #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
77 #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
79 #define XML_PAT_COPY_NSNAME(c, r, nsname) \
80 if ((c)->comp->dict) \
81 r = (xmlChar *) xmlDictLookup((c)->comp->dict, BAD_CAST nsname, -1); \
82 else r = xmlStrdup(BAD_CAST nsname);
84 #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
86 typedef struct _xmlStreamStep xmlStreamStep;
87 typedef xmlStreamStep *xmlStreamStepPtr;
88 struct _xmlStreamStep {
89 int flags; /* properties of that step */
90 const xmlChar *name; /* first string value if NULL accept all */
91 const xmlChar *ns; /* second string value */
92 int nodeType; /* type of node */
95 typedef struct _xmlStreamComp xmlStreamComp;
96 typedef xmlStreamComp *xmlStreamCompPtr;
97 struct _xmlStreamComp {
98 xmlDict *dict; /* the dictionary if any */
99 int nbStep; /* number of steps in the automata */
100 int maxStep; /* allocated number of steps */
101 xmlStreamStepPtr steps; /* the array of steps */
105 struct _xmlStreamCtxt {
106 struct _xmlStreamCtxt *next;/* link to next sub pattern if | */
107 xmlStreamCompPtr comp; /* the compiled stream */
108 int nbState; /* number of states in the automata */
109 int maxState; /* allocated number of states */
110 int level; /* how deep are we ? */
111 int *states; /* the array of step indexes */
112 int flags; /* validation options */
116 static void xmlFreeStreamComp(xmlStreamCompPtr comp);
135 typedef struct _xmlStepState xmlStepState;
136 typedef xmlStepState *xmlStepStatePtr;
137 struct _xmlStepState {
142 typedef struct _xmlStepStates xmlStepStates;
143 typedef xmlStepStates *xmlStepStatesPtr;
144 struct _xmlStepStates {
147 xmlStepStatePtr states;
150 typedef struct _xmlStepOp xmlStepOp;
151 typedef xmlStepOp *xmlStepOpPtr;
154 const xmlChar *value;
155 const xmlChar *value2; /* The namespace name */
158 #define PAT_FROM_ROOT (1<<8)
159 #define PAT_FROM_CUR (1<<9)
162 void *data; /* the associated template */
163 xmlDictPtr dict; /* the optional dictionary */
164 struct _xmlPattern *next; /* next pattern if | is used */
165 const xmlChar *pattern; /* the pattern */
166 int flags; /* flags */
169 xmlStepOpPtr steps; /* ops for computation */
170 xmlStreamCompPtr stream; /* the streaming data if any */
173 typedef struct _xmlPatParserContext xmlPatParserContext;
174 typedef xmlPatParserContext *xmlPatParserContextPtr;
175 struct _xmlPatParserContext {
176 const xmlChar *cur; /* the current char being parsed */
177 const xmlChar *base; /* the full expression */
178 int error; /* error code */
179 xmlDictPtr dict; /* the dictionary if any */
180 xmlPatternPtr comp; /* the result */
181 xmlNodePtr elem; /* the current node if any */
182 const xmlChar **namespaces; /* the namespaces definitions */
183 int nb_namespaces; /* the number of namespaces */
186 /************************************************************************
190 ************************************************************************/
195 * Create a new XSLT Pattern
197 * Returns the newly allocated xmlPatternPtr or NULL in case of error
200 xmlNewPattern(void) {
203 cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
205 ERROR(NULL, NULL, NULL,
206 "xmlNewPattern : malloc failed\n");
209 memset(cur, 0, sizeof(xmlPattern));
211 cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
212 if (cur->steps == NULL) {
214 ERROR(NULL, NULL, NULL,
215 "xmlNewPattern : malloc failed\n");
223 * @comp: an XSLT comp
225 * Free up the memory allocated by @comp
228 xmlFreePattern(xmlPatternPtr comp) {
234 if (comp->next != NULL)
235 xmlFreePattern(comp->next);
236 if (comp->stream != NULL)
237 xmlFreeStreamComp(comp->stream);
238 if (comp->pattern != NULL)
239 xmlFree((xmlChar *)comp->pattern);
240 if (comp->steps != NULL) {
241 if (comp->dict == NULL) {
242 for (i = 0;i < comp->nbStep;i++) {
243 op = &comp->steps[i];
244 if (op->value != NULL)
245 xmlFree((xmlChar *) op->value);
246 if (op->value2 != NULL)
247 xmlFree((xmlChar *) op->value2);
250 xmlFree(comp->steps);
252 if (comp->dict != NULL)
253 xmlDictFree(comp->dict);
255 memset(comp, -1, sizeof(xmlPattern));
260 * xmlFreePatternList:
261 * @comp: an XSLT comp list
263 * Free up the memory allocated by all the elements of @comp
266 xmlFreePatternList(xmlPatternPtr comp) {
269 while (comp != NULL) {
278 * xmlNewPatParserContext:
279 * @pattern: the pattern context
280 * @dict: the inherited dictionary or NULL
281 * @namespaces: the prefix definitions, array of [URI, prefix] terminated
282 * with [NULL, NULL] or NULL if no namespace is used
284 * Create a new XML pattern parser context
286 * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
288 static xmlPatParserContextPtr
289 xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
290 const xmlChar **namespaces) {
291 xmlPatParserContextPtr cur;
296 cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
298 ERROR(NULL, NULL, NULL,
299 "xmlNewPatParserContext : malloc failed\n");
302 memset(cur, 0, sizeof(xmlPatParserContext));
306 if (namespaces != NULL) {
308 for (i = 0;namespaces[2 * i] != NULL;i++);
309 cur->nb_namespaces = i;
311 cur->nb_namespaces = 0;
313 cur->namespaces = namespaces;
318 * xmlFreePatParserContext:
319 * @ctxt: an XSLT parser context
321 * Free up the memory allocated by @ctxt
324 xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
327 memset(ctxt, -1, sizeof(xmlPatParserContext));
333 * @comp: the compiled match expression
335 * @value: the first value
336 * @value2: the second value
338 * Add a step to an XSLT Compiled Match
340 * Returns -1 in case of failure, 0 otherwise.
343 xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
345 xmlPatOp op, xmlChar * value, xmlChar * value2)
347 if (comp->nbStep >= comp->maxStep) {
349 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
352 ERROR(ctxt, NULL, NULL,
353 "xmlPatternAdd: realloc failed\n");
359 comp->steps[comp->nbStep].op = op;
360 comp->steps[comp->nbStep].value = value;
361 comp->steps[comp->nbStep].value2 = value2;
368 * xsltSwapTopPattern:
369 * @comp: the compiled match expression
371 * reverse the two top steps.
374 xsltSwapTopPattern(xmlPatternPtr comp) {
376 int j = comp->nbStep - 1;
379 register const xmlChar *tmp;
380 register xmlPatOp op;
382 tmp = comp->steps[i].value;
383 comp->steps[i].value = comp->steps[j].value;
384 comp->steps[j].value = tmp;
385 tmp = comp->steps[i].value2;
386 comp->steps[i].value2 = comp->steps[j].value2;
387 comp->steps[j].value2 = tmp;
388 op = comp->steps[i].op;
389 comp->steps[i].op = comp->steps[j].op;
390 comp->steps[j].op = op;
397 * @comp: the compiled match expression
399 * reverse all the stack of expressions
401 * returns 0 in case of success and -1 in case of error.
404 xmlReversePattern(xmlPatternPtr comp) {
408 * remove the leading // for //a or .//a
410 if ((comp->nbStep > 0) && (comp->steps[0].op == XML_OP_ANCESTOR)) {
411 for (i = 0, j = 1;j < comp->nbStep;i++,j++) {
412 comp->steps[i].value = comp->steps[j].value;
413 comp->steps[i].value2 = comp->steps[j].value2;
414 comp->steps[i].op = comp->steps[j].op;
418 if (comp->nbStep >= comp->maxStep) {
420 temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
423 ERROR(ctxt, NULL, NULL,
424 "xmlReversePattern: realloc failed\n");
431 j = comp->nbStep - 1;
433 register const xmlChar *tmp;
434 register xmlPatOp op;
435 tmp = comp->steps[i].value;
436 comp->steps[i].value = comp->steps[j].value;
437 comp->steps[j].value = tmp;
438 tmp = comp->steps[i].value2;
439 comp->steps[i].value2 = comp->steps[j].value2;
440 comp->steps[j].value2 = tmp;
441 op = comp->steps[i].op;
442 comp->steps[i].op = comp->steps[j].op;
443 comp->steps[j].op = op;
447 comp->steps[comp->nbStep].value = NULL;
448 comp->steps[comp->nbStep].value2 = NULL;
449 comp->steps[comp->nbStep++].op = XML_OP_END;
453 /************************************************************************
455 * The interpreter for the precompiled patterns *
457 ************************************************************************/
460 xmlPatPushState(xmlStepStates *states, int step, xmlNodePtr node) {
461 if ((states->states == NULL) || (states->maxstates <= 0)) {
462 states->maxstates = 4;
463 states->nbstates = 0;
464 states->states = xmlMalloc(4 * sizeof(xmlStepState));
466 else if (states->maxstates <= states->nbstates) {
469 tmp = (xmlStepStatePtr) xmlRealloc(states->states,
470 2 * states->maxstates * sizeof(xmlStepState));
473 states->states = tmp;
474 states->maxstates *= 2;
476 states->states[states->nbstates].step = step;
477 states->states[states->nbstates++].node = node;
479 fprintf(stderr, "Push: %d, %s\n", step, node->name);
486 * @comp: the precompiled pattern
489 * Test whether the node matches the pattern
491 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
494 xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
497 xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
499 if ((comp == NULL) || (node == NULL)) return(-1);
502 for (;i < comp->nbStep;i++) {
503 step = &comp->steps[i];
508 if (node->type == XML_NAMESPACE_DECL)
511 if ((node->type == XML_DOCUMENT_NODE) ||
512 #ifdef LIBXML_DOCB_ENABLED
513 (node->type == XML_DOCB_DOCUMENT_NODE) ||
515 (node->type == XML_HTML_DOCUMENT_NODE))
519 if (node->type != XML_ELEMENT_NODE)
521 if (step->value == NULL)
523 if (step->value[0] != node->name[0])
525 if (!xmlStrEqual(step->value, node->name))
529 if (node->ns == NULL) {
530 if (step->value2 != NULL)
532 } else if (node->ns->href != NULL) {
533 if (step->value2 == NULL)
535 if (!xmlStrEqual(step->value2, node->ns->href))
542 if ((node->type != XML_ELEMENT_NODE) &&
543 (node->type != XML_DOCUMENT_NODE) &&
544 #ifdef LIBXML_DOCB_ENABLED
545 (node->type != XML_DOCB_DOCUMENT_NODE) &&
547 (node->type != XML_HTML_DOCUMENT_NODE))
550 lst = node->children;
552 if (step->value != NULL) {
553 while (lst != NULL) {
554 if ((lst->type == XML_ELEMENT_NODE) &&
555 (step->value[0] == lst->name[0]) &&
556 (xmlStrEqual(step->value, lst->name)))
566 if (node->type != XML_ATTRIBUTE_NODE)
568 if (step->value != NULL) {
569 if (step->value[0] != node->name[0])
571 if (!xmlStrEqual(step->value, node->name))
575 if (node->ns == NULL) {
576 if (step->value2 != NULL)
578 } else if (step->value2 != NULL) {
579 if (!xmlStrEqual(step->value2, node->ns->href))
584 if ((node->type == XML_DOCUMENT_NODE) ||
585 (node->type == XML_HTML_DOCUMENT_NODE) ||
586 #ifdef LIBXML_DOCB_ENABLED
587 (node->type == XML_DOCB_DOCUMENT_NODE) ||
589 (node->type == XML_NAMESPACE_DECL))
594 if (step->value == NULL)
596 if (step->value[0] != node->name[0])
598 if (!xmlStrEqual(step->value, node->name))
601 if (node->ns == NULL) {
602 if (step->value2 != NULL)
604 } else if (node->ns->href != NULL) {
605 if (step->value2 == NULL)
607 if (!xmlStrEqual(step->value2, node->ns->href))
611 case XML_OP_ANCESTOR:
612 /* TODO: implement coalescing of ANCESTOR/NODE ops */
613 if (step->value == NULL) {
615 step = &comp->steps[i];
616 if (step->op == XML_OP_ROOT)
618 if (step->op != XML_OP_ELEM)
620 if (step->value == NULL)
625 if ((node->type == XML_DOCUMENT_NODE) ||
626 (node->type == XML_HTML_DOCUMENT_NODE) ||
627 #ifdef LIBXML_DOCB_ENABLED
628 (node->type == XML_DOCB_DOCUMENT_NODE) ||
630 (node->type == XML_NAMESPACE_DECL))
633 while (node != NULL) {
634 if ((node->type == XML_ELEMENT_NODE) &&
635 (step->value[0] == node->name[0]) &&
636 (xmlStrEqual(step->value, node->name))) {
638 if (node->ns == NULL) {
639 if (step->value2 == NULL)
641 } else if (node->ns->href != NULL) {
642 if ((step->value2 != NULL) &&
643 (xmlStrEqual(step->value2, node->ns->href)))
652 * prepare a potential rollback from here
653 * for ancestors of that node.
655 if (step->op == XML_OP_ANCESTOR)
656 xmlPatPushState(&states, i, node);
658 xmlPatPushState(&states, i - 1, node);
661 if (node->type != XML_ELEMENT_NODE)
663 if (node->ns == NULL) {
664 if (step->value != NULL)
666 } else if (node->ns->href != NULL) {
667 if (step->value == NULL)
669 if (!xmlStrEqual(step->value, node->ns->href))
674 if (node->type != XML_ELEMENT_NODE)
680 if (states.states != NULL) {
681 /* Free the rollback states */
682 xmlFree(states.states);
686 /* got an error try to rollback */
687 if (states.states == NULL)
689 if (states.nbstates <= 0) {
690 xmlFree(states.states);
694 i = states.states[states.nbstates].step;
695 node = states.states[states.nbstates].node;
697 fprintf(stderr, "Pop: %d, %s\n", i, node->name);
702 /************************************************************************
704 * Dedicated parser for templates *
706 ************************************************************************/
709 xmlGenericError(xmlGenericErrorContext, \
710 "Unimplemented block at %s:%d\n", \
712 #define CUR (*ctxt->cur)
713 #define SKIP(val) ctxt->cur += (val)
714 #define NXT(val) ctxt->cur[(val)]
715 #define PEEKPREV(val) ctxt->cur[-(val)]
716 #define CUR_PTR ctxt->cur
718 #define SKIP_BLANKS \
719 while (IS_BLANK_CH(CUR)) NEXT
721 #define CURRENT (*ctxt->cur)
722 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
725 #define PUSH(op, val, val2) \
726 if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
728 #define XSLT_ERROR(X) \
729 { xsltError(ctxt, __FILE__, __LINE__, X); \
730 ctxt->error = (X); return; }
732 #define XSLT_ERROR0(X) \
733 { xsltError(ctxt, __FILE__, __LINE__, X); \
734 ctxt->error = (X); return(0); }
739 * @ctxt: the XPath Parser context
741 * Parse an XPath Litteral:
743 * [29] Literal ::= '"' [^"]* '"'
746 * Returns the Literal parsed or NULL
750 xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
751 const xmlChar *q, *cur;
759 val = xmlStringCurrentChar(NULL, cur, &len);
760 while ((IS_CHAR(val)) && (val != '"')) {
762 val = xmlStringCurrentChar(NULL, cur, &len);
769 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
771 ret = xmlStrndup(q, cur - q);
775 } else if (CUR == '\'') {
778 val = xmlStringCurrentChar(NULL, cur, &len);
779 while ((IS_CHAR(val)) && (val != '\'')) {
781 val = xmlStringCurrentChar(NULL, cur, &len);
788 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
790 ret = xmlStrndup(q, cur - q);
795 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
805 * @ctxt: the XPath Parser context
807 * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
808 * CombiningChar | Extender
810 * [5] Name ::= (Letter | '_' | ':') (NameChar)*
812 * [6] Names ::= Name (S Name)*
814 * Returns the Name parsed or NULL
818 xmlPatScanName(xmlPatParserContextPtr ctxt) {
819 const xmlChar *q, *cur;
826 val = xmlStringCurrentChar(NULL, cur, &len);
827 if (!IS_LETTER(val) && (val != '_') && (val != ':'))
830 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
831 (val == '.') || (val == '-') ||
833 (IS_COMBINING(val)) ||
834 (IS_EXTENDER(val))) {
836 val = xmlStringCurrentChar(NULL, cur, &len);
839 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
841 ret = xmlStrndup(q, cur - q);
848 * @ctxt: the XPath Parser context
850 * Parses a non qualified name
852 * Returns the Name parsed or NULL
856 xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
857 const xmlChar *q, *cur;
864 val = xmlStringCurrentChar(NULL, cur, &len);
865 if (!IS_LETTER(val) && (val != '_'))
868 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
869 (val == '.') || (val == '-') ||
871 (IS_COMBINING(val)) ||
872 (IS_EXTENDER(val))) {
874 val = xmlStringCurrentChar(NULL, cur, &len);
877 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
879 ret = xmlStrndup(q, cur - q);
887 * @ctxt: the XPath Parser context
888 * @prefix: the place to store the prefix
890 * Parse a qualified name
892 * Returns the Name parsed or NULL
896 xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
900 ret = xmlPatScanNCName(ctxt);
904 ret = xmlPatScanNCName(ctxt);
911 * xmlCompileAttributeTest:
912 * @ctxt: the compilation context
914 * Compile an attribute test.
917 xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
918 xmlChar *token = NULL;
919 xmlChar *name = NULL;
923 name = xmlPatScanNCName(ctxt);
926 PUSH(XML_OP_ATTR, NULL, NULL);
929 ERROR(NULL, NULL, NULL,
930 "xmlCompileAttributeTest : Name expected\n");
937 xmlChar *prefix = name;
941 if (IS_BLANK_CH(CUR)) {
942 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
943 XML_PAT_FREE_STRING(ctxt, prefix);
948 * This is a namespace match
950 token = xmlPatScanName(ctxt);
951 if ((prefix[0] == 'x') &&
952 (prefix[1] == 'm') &&
953 (prefix[2] == 'l') &&
956 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
958 for (i = 0;i < ctxt->nb_namespaces;i++) {
959 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
960 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
964 if (i >= ctxt->nb_namespaces) {
965 ERROR5(NULL, NULL, NULL,
966 "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
972 XML_PAT_FREE_STRING(ctxt, prefix);
976 PUSH(XML_OP_ATTR, NULL, URL);
978 ERROR(NULL, NULL, NULL,
979 "xmlCompileAttributeTest : Name expected\n");
984 PUSH(XML_OP_ATTR, token, URL);
987 PUSH(XML_OP_ATTR, name, NULL);
992 XML_PAT_FREE_STRING(ctxt, URL)
994 XML_PAT_FREE_STRING(ctxt, token);
998 * xmlCompileStepPattern:
999 * @ctxt: the compilation context
1001 * Compile the Step Pattern and generates a precompiled
1002 * form suitable for fast matching.
1004 * [3] Step ::= '.' | NameTest
1005 * [4] NameTest ::= QName | '*' | NCName ':' '*'
1009 xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1010 xmlChar *token = NULL;
1011 xmlChar *name = NULL;
1012 xmlChar *URL = NULL;
1021 PUSH(XML_OP_ELEM, NULL, NULL);
1028 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1029 ERROR5(NULL, NULL, NULL,
1030 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1035 xmlCompileAttributeTest(ctxt);
1036 if (ctxt->error != 0)
1040 name = xmlPatScanNCName(ctxt);
1044 PUSH(XML_OP_ALL, NULL, NULL);
1047 ERROR(NULL, NULL, NULL,
1048 "xmlCompileStepPattern : Name expected\n");
1053 if (IS_BLANK_CH(CUR)) {
1060 xmlChar *prefix = name;
1063 if (hasBlanks || IS_BLANK_CH(CUR)) {
1064 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1069 * This is a namespace match
1071 token = xmlPatScanName(ctxt);
1072 if ((prefix[0] == 'x') &&
1073 (prefix[1] == 'm') &&
1074 (prefix[2] == 'l') &&
1077 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1079 for (i = 0;i < ctxt->nb_namespaces;i++) {
1080 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1081 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1085 if (i >= ctxt->nb_namespaces) {
1086 ERROR5(NULL, NULL, NULL,
1087 "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1093 XML_PAT_FREE_STRING(ctxt, prefix);
1094 if (token == NULL) {
1097 PUSH(XML_OP_NS, URL, NULL);
1099 ERROR(NULL, NULL, NULL,
1100 "xmlCompileStepPattern : Name expected\n");
1105 PUSH(XML_OP_ELEM, token, URL);
1109 if (xmlStrEqual(name, (const xmlChar *) "child")) {
1110 XML_PAT_FREE_STRING(ctxt, name);
1111 name = xmlPatScanName(ctxt);
1115 PUSH(XML_OP_ALL, NULL, NULL);
1118 ERROR(NULL, NULL, NULL,
1119 "xmlCompileStepPattern : QName expected\n");
1125 xmlChar *prefix = name;
1129 if (IS_BLANK_CH(CUR)) {
1130 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1135 * This is a namespace match
1137 token = xmlPatScanName(ctxt);
1138 if ((prefix[0] == 'x') &&
1139 (prefix[1] == 'm') &&
1140 (prefix[2] == 'l') &&
1143 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1145 for (i = 0;i < ctxt->nb_namespaces;i++) {
1146 if (xmlStrEqual(ctxt->namespaces[2 * i + 1], prefix)) {
1147 XML_PAT_COPY_NSNAME(ctxt, URL, ctxt->namespaces[2 * i])
1151 if (i >= ctxt->nb_namespaces) {
1152 ERROR5(NULL, NULL, NULL,
1153 "xmlCompileStepPattern : no namespace bound "
1154 "to prefix %s\n", prefix);
1159 XML_PAT_FREE_STRING(ctxt, prefix);
1160 if (token == NULL) {
1163 PUSH(XML_OP_NS, URL, NULL);
1165 ERROR(NULL, NULL, NULL,
1166 "xmlCompileStepPattern : Name expected\n");
1171 PUSH(XML_OP_CHILD, token, URL);
1174 PUSH(XML_OP_CHILD, name, NULL);
1176 } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1177 XML_PAT_FREE_STRING(ctxt, name)
1179 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1180 ERROR5(NULL, NULL, NULL,
1181 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1185 xmlCompileAttributeTest(ctxt);
1186 if (ctxt->error != 0)
1190 ERROR5(NULL, NULL, NULL,
1191 "The 'element' or 'attribute' axis is expected.\n", NULL);
1196 } else if (CUR == '*') {
1202 PUSH(XML_OP_ALL, token, NULL);
1204 PUSH(XML_OP_ELEM, name, NULL);
1209 XML_PAT_FREE_STRING(ctxt, URL)
1211 XML_PAT_FREE_STRING(ctxt, token)
1213 XML_PAT_FREE_STRING(ctxt, name)
1217 * xmlCompilePathPattern:
1218 * @ctxt: the compilation context
1220 * Compile the Path Pattern and generates a precompiled
1221 * form suitable for fast matching.
1223 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1226 xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1229 ctxt->comp->flags |= PAT_FROM_ROOT;
1230 } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1231 ctxt->comp->flags |= PAT_FROM_CUR;
1234 if ((CUR == '/') && (NXT(1) == '/')) {
1235 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1238 } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1239 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1243 /* Check for incompleteness. */
1246 ERROR5(NULL, NULL, NULL,
1247 "Incomplete expression '%s'.\n", ctxt->base);
1254 xmlCompileAttributeTest(ctxt);
1256 /* TODO: check for incompleteness */
1258 xmlCompileStepPattern(ctxt);
1259 if (ctxt->error != 0)
1264 PUSH(XML_OP_ROOT, NULL, NULL);
1266 /* Check for incompleteness. */
1269 ERROR5(NULL, NULL, NULL,
1270 "Incomplete expression '%s'.\n", ctxt->base);
1275 xmlCompileStepPattern(ctxt);
1276 if (ctxt->error != 0)
1279 while (CUR == '/') {
1280 if (NXT(1) == '/') {
1281 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1285 xmlCompileStepPattern(ctxt);
1286 if (ctxt->error != 0)
1289 PUSH(XML_OP_PARENT, NULL, NULL);
1293 ERROR5(NULL, NULL, NULL,
1294 "Incomplete expression '%s'.\n", ctxt->base);
1298 xmlCompileStepPattern(ctxt);
1299 if (ctxt->error != 0)
1305 ERROR5(NULL, NULL, NULL,
1306 "Failed to compile pattern %s\n", ctxt->base);
1314 * xmlCompileIDCXPathPath:
1315 * @ctxt: the compilation context
1317 * Compile the Path Pattern and generates a precompiled
1318 * form suitable for fast matching.
1320 * [5] Path ::= ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1323 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1326 ERROR5(NULL, NULL, NULL,
1327 "Unexpected selection of the document root in '%s'.\n",
1331 ctxt->comp->flags |= PAT_FROM_CUR;
1334 /* "." - "self::node()" */
1339 * Selection of the context node.
1341 PUSH(XML_OP_ELEM, NULL, NULL);
1345 /* TODO: A more meaningful error message. */
1346 ERROR5(NULL, NULL, NULL,
1347 "Unexpected token after '.' in '%s'.\n", ctxt->base);
1350 /* "./" - "self::node()/" */
1354 if (IS_BLANK_CH(PEEKPREV(1))) {
1358 ERROR5(NULL, NULL, NULL,
1359 "Unexpected '/' token in '%s'.\n", ctxt->base);
1362 /* ".//" - "self:node()/descendant-or-self::node()/" */
1363 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1368 goto error_unfinished;
1374 xmlCompileStepPattern(ctxt);
1375 if (ctxt->error != 0)
1380 PUSH(XML_OP_PARENT, NULL, NULL);
1385 * Disallow subsequent '//'.
1387 ERROR5(NULL, NULL, NULL,
1388 "Unexpected subsequent '//' in '%s'.\n",
1393 goto error_unfinished;
1398 ERROR5(NULL, NULL, NULL,
1399 "Failed to compile expression '%s'.\n", ctxt->base);
1409 ERROR5(NULL, NULL, NULL,
1410 "Unfinished expression '%s'.\n", ctxt->base);
1414 /************************************************************************
1416 * The streaming code *
1418 ************************************************************************/
1420 #ifdef DEBUG_STREAMING
1422 xmlDebugStreamComp(xmlStreamCompPtr stream) {
1425 if (stream == NULL) {
1426 printf("Stream: NULL\n");
1429 printf("Stream: %d steps\n", stream->nbStep);
1430 for (i = 0;i < stream->nbStep;i++) {
1431 if (stream->steps[i].ns != NULL) {
1432 printf("{%s}", stream->steps[i].ns);
1434 if (stream->steps[i].name == NULL) {
1437 printf("%s ", stream->steps[i].name);
1439 if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1441 if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1443 if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1449 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1453 printf("Stream: NULL\n");
1456 printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1458 printf("matches\n");
1461 for (i = 0;i < ctxt->nbState;i++) {
1462 if (ctxt->states[2 * i] < 0)
1463 printf(" %d: free\n", i);
1465 printf(" %d: step %d, level %d", i, ctxt->states[2 * i],
1466 ctxt->states[(2 * i) + 1]);
1467 if (ctxt->comp->steps[ctxt->states[2 * i]].flags &
1468 XML_STREAM_STEP_DESC)
1478 * @size: the number of expected steps
1480 * build a new compiled pattern for streaming
1482 * Returns the new structure or NULL in case of error.
1484 static xmlStreamCompPtr
1485 xmlNewStreamComp(int size) {
1486 xmlStreamCompPtr cur;
1491 cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1493 ERROR(NULL, NULL, NULL,
1494 "xmlNewStreamComp: malloc failed\n");
1497 memset(cur, 0, sizeof(xmlStreamComp));
1498 cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1499 if (cur->steps == NULL) {
1501 ERROR(NULL, NULL, NULL,
1502 "xmlNewStreamComp: malloc failed\n");
1506 cur->maxStep = size;
1511 * xmlFreeStreamComp:
1512 * @comp: the compiled pattern for streaming
1514 * Free the compiled pattern for streaming
1517 xmlFreeStreamComp(xmlStreamCompPtr comp) {
1519 if (comp->steps != NULL)
1520 xmlFree(comp->steps);
1521 if (comp->dict != NULL)
1522 xmlDictFree(comp->dict);
1528 * xmlStreamCompAddStep:
1529 * @comp: the compiled pattern for streaming
1530 * @name: the first string, the name, or NULL for *
1531 * @ns: the second step, the namespace name
1532 * @flags: the flags for that step
1534 * Add a new step to the compiled pattern
1536 * Returns -1 in case of error or the step index if successful
1539 xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1540 const xmlChar *ns, int nodeType, int flags) {
1541 xmlStreamStepPtr cur;
1543 if (comp->nbStep >= comp->maxStep) {
1544 cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1545 comp->maxStep * 2 * sizeof(xmlStreamStep));
1547 ERROR(NULL, NULL, NULL,
1548 "xmlNewStreamComp: malloc failed\n");
1554 cur = &comp->steps[comp->nbStep++];
1558 cur->nodeType = nodeType;
1559 return(comp->nbStep - 1);
1564 * @comp: the precompiled pattern
1566 * Tries to stream compile a pattern
1568 * Returns -1 in case of failure and 0 in case of success.
1571 xmlStreamCompile(xmlPatternPtr comp) {
1572 xmlStreamCompPtr stream;
1573 int i, s = 0, root = 0, flags = 0, prevs = -1;
1576 if ((comp == NULL) || (comp->steps == NULL))
1579 * special case for .
1581 if ((comp->nbStep == 1) &&
1582 (comp->steps[0].op == XML_OP_ELEM) &&
1583 (comp->steps[0].value == NULL) &&
1584 (comp->steps[0].value2 == NULL)) {
1585 stream = xmlNewStreamComp(0);
1588 /* Note that the stream will have no steps in this case. */
1589 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1590 comp->stream = stream;
1594 stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1597 if (comp->dict != NULL) {
1598 stream->dict = comp->dict;
1599 xmlDictReference(stream->dict);
1603 if (comp->flags & PAT_FROM_ROOT)
1604 stream->flags |= XML_STREAM_FROM_ROOT;
1606 for (;i < comp->nbStep;i++) {
1607 step = comp->steps[i];
1617 s = xmlStreamCompAddStep(stream, NULL, step.value,
1618 XML_ELEMENT_NODE, flags);
1625 flags |= XML_STREAM_STEP_ATTR;
1627 s = xmlStreamCompAddStep(stream,
1628 step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1634 if ((step.value == NULL) && (step.value2 == NULL)) {
1636 * We have a "." or "self::node()" here.
1637 * Eliminate redundant self::node() tests like in "/./."
1639 * The only case we won't eliminate is "//.", i.e. if
1640 * self::node() is the last node test and we had
1641 * continuation somewhere beforehand.
1643 if ((comp->nbStep == i + 1) &&
1644 (flags & XML_STREAM_STEP_DESC)) {
1646 * Mark the special case where the expression resolves
1647 * to any type of node.
1649 if (comp->nbStep == i + 1) {
1650 stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1652 flags |= XML_STREAM_STEP_NODE;
1653 s = xmlStreamCompAddStep(stream, NULL, NULL,
1654 XML_STREAM_ANY_NODE, flags);
1659 * If there was a previous step, mark it to be added to
1660 * the result node-set; this is needed since only
1661 * the last step will be marked as "final" and only
1662 * "final" nodes are added to the resulting set.
1665 stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1671 /* Just skip this one. */
1675 /* An element node. */
1676 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1677 XML_ELEMENT_NODE, flags);
1684 /* An element node child. */
1685 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1686 XML_ELEMENT_NODE, flags);
1693 s = xmlStreamCompAddStep(stream, NULL, NULL,
1694 XML_ELEMENT_NODE, flags);
1702 case XML_OP_ANCESTOR:
1703 /* Skip redundant continuations. */
1704 if (flags & XML_STREAM_STEP_DESC)
1706 flags |= XML_STREAM_STEP_DESC;
1708 * Mark the expression as having "//".
1710 if ((stream->flags & XML_STREAM_DESC) == 0)
1711 stream->flags |= XML_STREAM_DESC;
1715 if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1717 * If this should behave like a real pattern, we will mark
1718 * the first step as having "//", to be reentrant on every
1721 if ((stream->flags & XML_STREAM_DESC) == 0)
1722 stream->flags |= XML_STREAM_DESC;
1724 if (stream->nbStep > 0) {
1725 if ((stream->steps[0].flags & XML_STREAM_STEP_DESC) == 0)
1726 stream->steps[0].flags |= XML_STREAM_STEP_DESC;
1729 if (stream->nbStep <= s)
1731 stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1733 stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1734 #ifdef DEBUG_STREAMING
1735 xmlDebugStreamComp(stream);
1737 comp->stream = stream;
1740 xmlFreeStreamComp(stream);
1746 * @size: the number of expected states
1748 * build a new stream context
1750 * Returns the new structure or NULL in case of error.
1752 static xmlStreamCtxtPtr
1753 xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1754 xmlStreamCtxtPtr cur;
1756 cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1758 ERROR(NULL, NULL, NULL,
1759 "xmlNewStreamCtxt: malloc failed\n");
1762 memset(cur, 0, sizeof(xmlStreamCtxt));
1763 cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1764 if (cur->states == NULL) {
1766 ERROR(NULL, NULL, NULL,
1767 "xmlNewStreamCtxt: malloc failed\n");
1774 cur->blockLevel = -1;
1779 * xmlFreeStreamCtxt:
1780 * @stream: the stream context
1782 * Free the stream context
1785 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1786 xmlStreamCtxtPtr next;
1788 while (stream != NULL) {
1789 next = stream->next;
1790 if (stream->states != NULL)
1791 xmlFree(stream->states);
1798 * xmlStreamCtxtAddState:
1799 * @comp: the stream context
1800 * @idx: the step index for that streaming state
1802 * Add a new state to the stream context
1804 * Returns -1 in case of error or the state index if successful
1807 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1809 for (i = 0;i < comp->nbState;i++) {
1810 if (comp->states[2 * i] < 0) {
1811 comp->states[2 * i] = idx;
1812 comp->states[2 * i + 1] = level;
1816 if (comp->nbState >= comp->maxState) {
1819 cur = (int *) xmlRealloc(comp->states,
1820 comp->maxState * 4 * sizeof(int));
1822 ERROR(NULL, NULL, NULL,
1823 "xmlNewStreamCtxt: malloc failed\n");
1827 comp->maxState *= 2;
1829 comp->states[2 * comp->nbState] = idx;
1830 comp->states[2 * comp->nbState++ + 1] = level;
1831 return(comp->nbState - 1);
1835 * xmlStreamPushInternal:
1836 * @stream: the stream context
1837 * @name: the current name
1838 * @ns: the namespace name
1839 * @nodeType: the type of the node
1841 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
1842 * indicated a dictionary, then strings for name and ns will be expected
1843 * to come from the dictionary.
1844 * Both @name and @ns being NULL means the / i.e. the root of the document.
1845 * This can also act as a reset.
1847 * Returns: -1 in case of error, 1 if the current state in the stream is a
1848 * match and 0 otherwise.
1851 xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1852 const xmlChar *name, const xmlChar *ns,
1854 int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1855 xmlStreamCompPtr comp;
1857 #ifdef DEBUG_STREAMING
1858 xmlStreamCtxtPtr orig = stream;
1861 if ((stream == NULL) || (stream->nbState < 0))
1864 while (stream != NULL) {
1865 comp = stream->comp;
1867 if ((nodeType == XML_ELEMENT_NODE) &&
1868 (name == NULL) && (ns == NULL)) {
1869 /* We have a document node here (or a reset). */
1870 stream->nbState = 0;
1872 stream->blockLevel = -1;
1873 if (comp->flags & XML_STREAM_FROM_ROOT) {
1874 if (comp->nbStep == 0) {
1875 /* TODO: We have a "/." here? */
1878 if ((comp->nbStep == 1) &&
1879 (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1880 (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1883 * In the case of "//." the document node will match
1887 } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1888 /* TODO: Do we need this ? */
1889 tmp = xmlStreamCtxtAddState(stream, 0, 0);
1895 stream = stream->next;
1896 continue; /* while */
1900 * Fast check for ".".
1902 if (comp->nbStep == 0) {
1904 * / and . are handled at the XPath node set creation
1905 * level by checking min depth
1907 if (stream->flags & XML_PATTERN_XPATH) {
1908 stream = stream->next;
1909 continue; /* while */
1912 * For non-pattern like evaluation like XML Schema IDCs
1913 * or traditional XPath expressions, this will match if
1914 * we are at the first level only, otherwise on every level.
1916 if ((nodeType != XML_ATTRIBUTE_NODE) &&
1917 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1918 (stream->level == 0))) {
1924 if (stream->blockLevel != -1) {
1926 * Skip blocked expressions.
1932 if ((nodeType != XML_ELEMENT_NODE) &&
1933 (nodeType != XML_ATTRIBUTE_NODE) &&
1934 ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1936 * No need to process nodes of other types if we don't
1937 * resolve to those types.
1938 * TODO: Do we need to block the context here?
1945 * Check evolution of existing states
1948 m = stream->nbState;
1950 if ((comp->flags & XML_STREAM_DESC) == 0) {
1952 * If there is no "//", then only the last
1953 * added state is of interest.
1955 stepNr = stream->states[2 * (stream->nbState -1)];
1957 * TODO: Security check, should not happen, remove it.
1959 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1968 * If there are "//", then we need to process every "//"
1969 * occuring in the states, plus any other state for this
1972 stepNr = stream->states[2 * i];
1974 /* TODO: should not happen anymore: dead states */
1978 tmp = stream->states[(2 * i) + 1];
1980 /* skip new states just added */
1981 if (tmp > stream->level)
1984 /* skip states at ancestor levels, except if "//" */
1985 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1986 if ((tmp < stream->level) && (!desc))
1990 * Check for correct node-type.
1992 step = comp->steps[stepNr];
1993 if (step.nodeType != nodeType) {
1994 if (step.nodeType == XML_ATTRIBUTE_NODE) {
1996 * Block this expression for deeper evaluation.
1998 if ((comp->flags & XML_STREAM_DESC) == 0)
1999 stream->blockLevel = stream->level +1;
2001 } else if (step.nodeType != XML_STREAM_ANY_NODE)
2005 * Compare local/namespace-name.
2008 if (step.nodeType == XML_STREAM_ANY_NODE) {
2010 } else if (step.name == NULL) {
2011 if (step.ns == NULL) {
2013 * This lets through all elements/attributes.
2016 } else if (ns != NULL)
2017 match = xmlStrEqual(step.ns, ns);
2018 } else if (((step.ns != NULL) == (ns != NULL)) &&
2020 (step.name[0] == name[0]) &&
2021 xmlStrEqual(step.name, name) &&
2022 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2028 * TODO: Pointer comparison won't work, since not guaranteed that the given
2029 * values are in the same dict; especially if it's the namespace name,
2030 * normally coming from ns->href. We need a namespace dict mechanism !
2032 } else if (comp->dict) {
2033 if (step.name == NULL) {
2034 if (step.ns == NULL)
2037 match = (step.ns == ns);
2039 match = ((step.name == name) && (step.ns == ns));
2041 #endif /* if 0 ------------------------------------------------------- */
2043 final = step.flags & XML_STREAM_STEP_FINAL;
2048 /* descending match create a new state */
2049 xmlStreamCtxtAddState(stream, stepNr + 1,
2056 xmlStreamCtxtAddState(stream, stepNr + 1,
2060 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2062 * Check if we have a special case like "foo/bar//.", where
2063 * "foo" is selected as well.
2068 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2069 ((! match) || final)) {
2071 * Mark this expression as blocked for any evaluation at
2072 * deeper levels. Note that this includes "/foo"
2073 * expressions if the *pattern* behaviour is used.
2075 stream->blockLevel = stream->level +1;
2084 * Re/enter the expression.
2085 * Don't reenter if it's an absolute expression like "/foo",
2088 step = comp->steps[0];
2089 if (step.flags & XML_STREAM_STEP_ROOT)
2092 desc = step.flags & XML_STREAM_STEP_DESC;
2093 if (stream->flags & XML_PATTERN_NOTPATTERN) {
2095 * Re/enter the expression if it is a "descendant" one,
2096 * or if we are at the 1st level of evaluation.
2099 if (stream->level == 1) {
2100 if (XML_STREAM_XS_IDC(stream)) {
2102 * XS-IDC: The missing "self::node()" will always
2103 * match the first given node.
2110 * A "//" is always reentrant.
2116 * XS-IDC: Process the 2nd level, since the missing
2117 * "self::node()" is responsible for the 2nd level being
2118 * the real start level.
2120 if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2128 * Check expected node-type.
2130 if (step.nodeType != nodeType) {
2131 if (nodeType == XML_ATTRIBUTE_NODE)
2133 else if (step.nodeType != XML_STREAM_ANY_NODE)
2137 * Compare local/namespace-name.
2140 if (step.nodeType == XML_STREAM_ANY_NODE) {
2142 } else if (step.name == NULL) {
2143 if (step.ns == NULL) {
2145 * This lets through all elements/attributes.
2148 } else if (ns != NULL)
2149 match = xmlStrEqual(step.ns, ns);
2150 } else if (((step.ns != NULL) == (ns != NULL)) &&
2152 (step.name[0] == name[0]) &&
2153 xmlStrEqual(step.name, name) &&
2154 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2158 final = step.flags & XML_STREAM_STEP_FINAL;
2163 xmlStreamCtxtAddState(stream, 1, stream->level);
2164 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2166 * Check if we have a special case like "foo//.", where
2167 * "foo" is selected as well.
2172 if (((comp->flags & XML_STREAM_DESC) == 0) &&
2173 ((! match) || final)) {
2175 * Mark this expression as blocked for any evaluation at
2178 stream->blockLevel = stream->level;
2182 stream = stream->next;
2183 } /* while stream != NULL */
2187 #ifdef DEBUG_STREAMING
2188 xmlDebugStreamCtxt(orig, ret);
2195 * @stream: the stream context
2196 * @name: the current name
2197 * @ns: the namespace name
2199 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2200 * indicated a dictionary, then strings for name and ns will be expected
2201 * to come from the dictionary.
2202 * Both @name and @ns being NULL means the / i.e. the root of the document.
2203 * This can also act as a reset.
2204 * Otherwise the function will act as if it has been given an element-node.
2206 * Returns: -1 in case of error, 1 if the current state in the stream is a
2207 * match and 0 otherwise.
2210 xmlStreamPush(xmlStreamCtxtPtr stream,
2211 const xmlChar *name, const xmlChar *ns) {
2212 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2216 * xmlStreamPushNode:
2217 * @stream: the stream context
2218 * @name: the current name
2219 * @ns: the namespace name
2220 * @nodeType: the type of the node being pushed
2222 * Push new data onto the stream. NOTE: if the call xmlPatterncompile()
2223 * indicated a dictionary, then strings for name and ns will be expected
2224 * to come from the dictionary.
2225 * Both @name and @ns being NULL means the / i.e. the root of the document.
2226 * This can also act as a reset.
2227 * Different from xmlStreamPush() this function can be fed with nodes of type:
2228 * element-, attribute-, text-, cdata-section-, comment- and
2229 * processing-instruction-node.
2231 * Returns: -1 in case of error, 1 if the current state in the stream is a
2232 * match and 0 otherwise.
2235 xmlStreamPushNode(xmlStreamCtxtPtr stream,
2236 const xmlChar *name, const xmlChar *ns,
2239 return (xmlStreamPushInternal(stream, name, ns,
2244 * xmlStreamPushAttr:
2245 * @stream: the stream context
2246 * @name: the current name
2247 * @ns: the namespace name
2249 * Push new attribute data onto the stream. NOTE: if the call xmlPatterncompile()
2250 * indicated a dictionary, then strings for name and ns will be expected
2251 * to come from the dictionary.
2252 * Both @name and @ns being NULL means the / i.e. the root of the document.
2253 * This can also act as a reset.
2254 * Otherwise the function will act as if it has been given an attribute-node.
2256 * Returns: -1 in case of error, 1 if the current state in the stream is a
2257 * match and 0 otherwise.
2260 xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2261 const xmlChar *name, const xmlChar *ns) {
2262 return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
2267 * @stream: the stream context
2269 * push one level from the stream.
2271 * Returns: -1 in case of error, 0 otherwise.
2274 xmlStreamPop(xmlStreamCtxtPtr stream) {
2279 while (stream != NULL) {
2281 * Reset block-level.
2283 if (stream->blockLevel == stream->level)
2284 stream->blockLevel = -1;
2287 if (stream->level < 0)
2290 * Check evolution of existing states
2292 for (i = stream->nbState -1; i >= 0; i--) {
2293 /* discard obsoleted states */
2294 lev = stream->states[(2 * i) + 1];
2295 if (lev > stream->level)
2297 if (lev <= stream->level)
2300 stream = stream->next;
2306 * xmlStreamWantsAnyNode:
2307 * @streamCtxt: the stream context
2309 * Query if the streaming pattern additionally needs to be fed with
2310 * text-, cdata-section-, comment- and processing-instruction-nodes.
2311 * If the result is 0 then only element-nodes and attribute-nodes
2312 * need to be pushed.
2314 * Returns: 1 in case of need of nodes of the above described types,
2315 * 0 otherwise. -1 on API errors.
2318 xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2320 if (streamCtxt == NULL)
2322 while (streamCtxt != NULL) {
2323 if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2325 streamCtxt = streamCtxt->next;
2330 /************************************************************************
2332 * The public interfaces *
2334 ************************************************************************/
2337 * xmlPatterncompile:
2338 * @pattern: the pattern to compile
2339 * @dict: an optional dictionary for interned strings
2340 * @flags: compilation flags, see xmlPatternFlags
2341 * @namespaces: the prefix definitions, array of [URI, prefix] or NULL
2343 * Compile a pattern.
2345 * Returns the compiled form of the pattern or NULL in case of error
2348 xmlPatterncompile(const xmlChar *pattern, xmlDict *dict, int flags,
2349 const xmlChar **namespaces) {
2350 xmlPatternPtr ret = NULL, cur;
2351 xmlPatParserContextPtr ctxt = NULL;
2352 const xmlChar *or, *start;
2353 xmlChar *tmp = NULL;
2357 if (pattern == NULL)
2364 while ((*or != 0) && (*or != '|')) or++;
2366 ctxt = xmlNewPatParserContext(start, dict, namespaces);
2368 tmp = xmlStrndup(start, or - start);
2370 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2374 if (ctxt == NULL) goto error;
2375 cur = xmlNewPattern();
2376 if (cur == NULL) goto error;
2378 * Assign string dict.
2382 xmlDictReference(dict);
2387 cur->next = ret->next;
2393 if (XML_STREAM_XS_IDC(cur))
2394 xmlCompileIDCXPathPath(ctxt);
2396 xmlCompilePathPattern(ctxt);
2397 if (ctxt->error != 0)
2399 xmlFreePatParserContext(ctxt);
2405 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2406 } else if (type == PAT_FROM_ROOT) {
2407 if (cur->flags & PAT_FROM_CUR)
2409 } else if (type == PAT_FROM_CUR) {
2410 if (cur->flags & PAT_FROM_ROOT)
2415 xmlStreamCompile(cur);
2416 if (xmlReversePattern(cur) < 0)
2424 if (streamable == 0) {
2426 while (cur != NULL) {
2427 if (cur->stream != NULL) {
2428 xmlFreeStreamComp(cur->stream);
2437 if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2438 if (ret != NULL) xmlFreePattern(ret);
2439 if (tmp != NULL) xmlFree(tmp);
2445 * @comp: the precompiled pattern
2448 * Test whether the node matches the pattern
2450 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2453 xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2457 if ((comp == NULL) || (node == NULL))
2460 while (comp != NULL) {
2461 ret = xmlPatMatch(comp, node);
2470 * xmlPatternGetStreamCtxt:
2471 * @comp: the precompiled pattern
2473 * Get a streaming context for that pattern
2474 * Use xmlFreeStreamCtxt to free the context.
2476 * Returns a pointer to the context or NULL in case of failure
2479 xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2481 xmlStreamCtxtPtr ret = NULL, cur;
2483 if ((comp == NULL) || (comp->stream == NULL))
2486 while (comp != NULL) {
2487 if (comp->stream == NULL)
2489 cur = xmlNewStreamCtxt(comp->stream);
2495 cur->next = ret->next;
2498 cur->flags = comp->flags;
2503 xmlFreeStreamCtxt(ret);
2508 * xmlPatternStreamable:
2509 * @comp: the precompiled pattern
2511 * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2514 * Returns 1 if streamable, 0 if not and -1 in case of error.
2517 xmlPatternStreamable(xmlPatternPtr comp) {
2520 while (comp != NULL) {
2521 if (comp->stream == NULL)
2529 * xmlPatternMaxDepth:
2530 * @comp: the precompiled pattern
2532 * Check the maximum depth reachable by a pattern
2534 * Returns -2 if no limit (using //), otherwise the depth,
2535 * and -1 in case of error
2538 xmlPatternMaxDepth(xmlPatternPtr comp) {
2542 while (comp != NULL) {
2543 if (comp->stream == NULL)
2545 for (i = 0;i < comp->stream->nbStep;i++)
2546 if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2548 if (comp->stream->nbStep > ret)
2549 ret = comp->stream->nbStep;
2556 * xmlPatternMinDepth:
2557 * @comp: the precompiled pattern
2559 * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2562 * Returns -1 in case of error otherwise the depth,
2566 xmlPatternMinDepth(xmlPatternPtr comp) {
2570 while (comp != NULL) {
2571 if (comp->stream == NULL)
2573 if (comp->stream->nbStep < ret)
2574 ret = comp->stream->nbStep;
2583 * xmlPatternFromRoot:
2584 * @comp: the precompiled pattern
2586 * Check if the pattern must be looked at from the root.
2588 * Returns 1 if true, 0 if false and -1 in case of error
2591 xmlPatternFromRoot(xmlPatternPtr comp) {
2594 while (comp != NULL) {
2595 if (comp->stream == NULL)
2597 if (comp->flags & PAT_FROM_ROOT)
2605 #define bottom_pattern
2606 #include "elfgcchack.h"
2607 #endif /* LIBXML_PATTERN_ENABLED */