Imported Upstream version 2.9.5_rc2
[platform/upstream/libxml2.git] / pattern.c
1 /*
2  * pattern.c: Implemetation of selectors for nodes
3  *
4  * Reference:
5  *   http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/
6  *   to some extent
7  *   http://www.w3.org/TR/1999/REC-xml-19991116
8  *
9  * See Copyright for the status of this software.
10  *
11  * daniel@veillard.com
12  */
13
14 /*
15  * TODO:
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
24  */
25
26 #define IN_LIBXML
27 #include "libxml.h"
28
29 #include <string.h>
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>
37
38 #ifdef LIBXML_PATTERN_ENABLED
39
40 /* #define DEBUG_STREAMING */
41
42 #ifdef ERROR
43 #undef ERROR
44 #endif
45 #define ERROR(a, b, c, d)
46 #define ERROR5(a, b, c, d, e)
47
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
54
55 /*
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.
60 */
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
64
65 /*
66 * XML_STREAM_ANY_NODE is used for comparison against
67 * xmlElementType enums, to indicate a node of any type.
68 */
69 #define XML_STREAM_ANY_NODE 100
70
71 #define XML_PATTERN_NOTPATTERN  (XML_PATTERN_XPATH | \
72                                  XML_PATTERN_XSSEL | \
73                                  XML_PATTERN_XSFIELD)
74
75 #define XML_STREAM_XS_IDC(c) ((c)->flags & \
76     (XML_PATTERN_XSSEL | XML_PATTERN_XSFIELD))
77
78 #define XML_STREAM_XS_IDC_SEL(c) ((c)->flags & XML_PATTERN_XSSEL)
79
80 #define XML_STREAM_XS_IDC_FIELD(c) ((c)->flags & XML_PATTERN_XSFIELD)
81
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);
86
87 #define XML_PAT_FREE_STRING(c, r) if ((c)->comp->dict == NULL) xmlFree(r);
88
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 */
96 };
97
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 */
105     int flags;
106 };
107
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 */
116     int blockLevel;
117 };
118
119 static void xmlFreeStreamComp(xmlStreamCompPtr comp);
120
121 /*
122  * Types are private:
123  */
124
125 typedef enum {
126     XML_OP_END=0,
127     XML_OP_ROOT,
128     XML_OP_ELEM,
129     XML_OP_CHILD,
130     XML_OP_ATTR,
131     XML_OP_PARENT,
132     XML_OP_ANCESTOR,
133     XML_OP_NS,
134     XML_OP_ALL
135 } xmlPatOp;
136
137
138 typedef struct _xmlStepState xmlStepState;
139 typedef xmlStepState *xmlStepStatePtr;
140 struct _xmlStepState {
141     int step;
142     xmlNodePtr node;
143 };
144
145 typedef struct _xmlStepStates xmlStepStates;
146 typedef xmlStepStates *xmlStepStatesPtr;
147 struct _xmlStepStates {
148     int nbstates;
149     int maxstates;
150     xmlStepStatePtr states;
151 };
152
153 typedef struct _xmlStepOp xmlStepOp;
154 typedef xmlStepOp *xmlStepOpPtr;
155 struct _xmlStepOp {
156     xmlPatOp op;
157     const xmlChar *value;
158     const xmlChar *value2; /* The namespace name */
159 };
160
161 #define PAT_FROM_ROOT   (1<<8)
162 #define PAT_FROM_CUR    (1<<9)
163
164 struct _xmlPattern {
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 */
170     int nbStep;
171     int maxStep;
172     xmlStepOpPtr steps;        /* ops for computation */
173     xmlStreamCompPtr stream;    /* the streaming data if any */
174 };
175
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 */
187 };
188
189 /************************************************************************
190  *                                                                      *
191  *                      Type functions                                  *
192  *                                                                      *
193  ************************************************************************/
194
195 /**
196  * xmlNewPattern:
197  *
198  * Create a new XSLT Pattern
199  *
200  * Returns the newly allocated xmlPatternPtr or NULL in case of error
201  */
202 static xmlPatternPtr
203 xmlNewPattern(void) {
204     xmlPatternPtr cur;
205
206     cur = (xmlPatternPtr) xmlMalloc(sizeof(xmlPattern));
207     if (cur == NULL) {
208         ERROR(NULL, NULL, NULL,
209                 "xmlNewPattern : malloc failed\n");
210         return(NULL);
211     }
212     memset(cur, 0, sizeof(xmlPattern));
213     cur->maxStep = 10;
214     cur->steps = (xmlStepOpPtr) xmlMalloc(cur->maxStep * sizeof(xmlStepOp));
215     if (cur->steps == NULL) {
216         xmlFree(cur);
217         ERROR(NULL, NULL, NULL,
218                 "xmlNewPattern : malloc failed\n");
219         return(NULL);
220     }
221     return(cur);
222 }
223
224 /**
225  * xmlFreePattern:
226  * @comp:  an XSLT comp
227  *
228  * Free up the memory allocated by @comp
229  */
230 void
231 xmlFreePattern(xmlPatternPtr comp) {
232     xmlStepOpPtr op;
233     int i;
234
235     if (comp == NULL)
236         return;
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);
251             }
252         }
253         xmlFree(comp->steps);
254     }
255     if (comp->dict != NULL)
256         xmlDictFree(comp->dict);
257
258     memset(comp, -1, sizeof(xmlPattern));
259     xmlFree(comp);
260 }
261
262 /**
263  * xmlFreePatternList:
264  * @comp:  an XSLT comp list
265  *
266  * Free up the memory allocated by all the elements of @comp
267  */
268 void
269 xmlFreePatternList(xmlPatternPtr comp) {
270     xmlPatternPtr cur;
271
272     while (comp != NULL) {
273         cur = comp;
274         comp = comp->next;
275         cur->next = NULL;
276         xmlFreePattern(cur);
277     }
278 }
279
280 /**
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
286  *
287  * Create a new XML pattern parser context
288  *
289  * Returns the newly allocated xmlPatParserContextPtr or NULL in case of error
290  */
291 static xmlPatParserContextPtr
292 xmlNewPatParserContext(const xmlChar *pattern, xmlDictPtr dict,
293                        const xmlChar **namespaces) {
294     xmlPatParserContextPtr cur;
295
296     if (pattern == NULL)
297         return(NULL);
298
299     cur = (xmlPatParserContextPtr) xmlMalloc(sizeof(xmlPatParserContext));
300     if (cur == NULL) {
301         ERROR(NULL, NULL, NULL,
302                 "xmlNewPatParserContext : malloc failed\n");
303         return(NULL);
304     }
305     memset(cur, 0, sizeof(xmlPatParserContext));
306     cur->dict = dict;
307     cur->cur = pattern;
308     cur->base = pattern;
309     if (namespaces != NULL) {
310         int i;
311         for (i = 0;namespaces[2 * i] != NULL;i++)
312             ;
313         cur->nb_namespaces = i;
314     } else {
315         cur->nb_namespaces = 0;
316     }
317     cur->namespaces = namespaces;
318     return(cur);
319 }
320
321 /**
322  * xmlFreePatParserContext:
323  * @ctxt:  an XSLT parser context
324  *
325  * Free up the memory allocated by @ctxt
326  */
327 static void
328 xmlFreePatParserContext(xmlPatParserContextPtr ctxt) {
329     if (ctxt == NULL)
330         return;
331     memset(ctxt, -1, sizeof(xmlPatParserContext));
332     xmlFree(ctxt);
333 }
334
335 /**
336  * xmlPatternAdd:
337  * @comp:  the compiled match expression
338  * @op:  an op
339  * @value:  the first value
340  * @value2:  the second value
341  *
342  * Add a step to an XSLT Compiled Match
343  *
344  * Returns -1 in case of failure, 0 otherwise.
345  */
346 static int
347 xmlPatternAdd(xmlPatParserContextPtr ctxt ATTRIBUTE_UNUSED,
348                 xmlPatternPtr comp,
349                 xmlPatOp op, xmlChar * value, xmlChar * value2)
350 {
351     if (comp->nbStep >= comp->maxStep) {
352         xmlStepOpPtr temp;
353         temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
354                                          sizeof(xmlStepOp));
355         if (temp == NULL) {
356             ERROR(ctxt, NULL, NULL,
357                              "xmlPatternAdd: realloc failed\n");
358             return (-1);
359         }
360         comp->steps = temp;
361         comp->maxStep *= 2;
362     }
363     comp->steps[comp->nbStep].op = op;
364     comp->steps[comp->nbStep].value = value;
365     comp->steps[comp->nbStep].value2 = value2;
366     comp->nbStep++;
367     return (0);
368 }
369
370 #if 0
371 /**
372  * xsltSwapTopPattern:
373  * @comp:  the compiled match expression
374  *
375  * reverse the two top steps.
376  */
377 static void
378 xsltSwapTopPattern(xmlPatternPtr comp) {
379     int i;
380     int j = comp->nbStep - 1;
381
382     if (j > 0) {
383         register const xmlChar *tmp;
384         register xmlPatOp op;
385         i = j - 1;
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;
395     }
396 }
397 #endif
398
399 /**
400  * xmlReversePattern:
401  * @comp:  the compiled match expression
402  *
403  * reverse all the stack of expressions
404  *
405  * returns 0 in case of success and -1 in case of error.
406  */
407 static int
408 xmlReversePattern(xmlPatternPtr comp) {
409     int i, j;
410
411     /*
412      * remove the leading // for //a or .//a
413      */
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;
419         }
420         comp->nbStep--;
421     }
422     if (comp->nbStep >= comp->maxStep) {
423         xmlStepOpPtr temp;
424         temp = (xmlStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
425                                          sizeof(xmlStepOp));
426         if (temp == NULL) {
427             ERROR(ctxt, NULL, NULL,
428                              "xmlReversePattern: realloc failed\n");
429             return (-1);
430         }
431         comp->steps = temp;
432         comp->maxStep *= 2;
433     }
434     i = 0;
435     j = comp->nbStep - 1;
436     while (j > i) {
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;
448         j--;
449         i++;
450     }
451     comp->steps[comp->nbStep].value = NULL;
452     comp->steps[comp->nbStep].value2 = NULL;
453     comp->steps[comp->nbStep++].op = XML_OP_END;
454     return(0);
455 }
456
457 /************************************************************************
458  *                                                                      *
459  *              The interpreter for the precompiled patterns            *
460  *                                                                      *
461  ************************************************************************/
462
463 static int
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));
469     }
470     else if (states->maxstates <= states->nbstates) {
471         xmlStepState *tmp;
472
473         tmp = (xmlStepStatePtr) xmlRealloc(states->states,
474                                2 * states->maxstates * sizeof(xmlStepState));
475         if (tmp == NULL)
476             return(-1);
477         states->states = tmp;
478         states->maxstates *= 2;
479     }
480     states->states[states->nbstates].step = step;
481     states->states[states->nbstates++].node = node;
482 #if 0
483     fprintf(stderr, "Push: %d, %s\n", step, node->name);
484 #endif
485     return(0);
486 }
487
488 /**
489  * xmlPatMatch:
490  * @comp: the precompiled pattern
491  * @node: a node
492  *
493  * Test whether the node matches the pattern
494  *
495  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
496  */
497 static int
498 xmlPatMatch(xmlPatternPtr comp, xmlNodePtr node) {
499     int i;
500     xmlStepOpPtr step;
501     xmlStepStates states = {0, 0, NULL}; /* // may require backtrack */
502
503     if ((comp == NULL) || (node == NULL)) return(-1);
504     i = 0;
505 restart:
506     for (;i < comp->nbStep;i++) {
507         step = &comp->steps[i];
508         switch (step->op) {
509             case XML_OP_END:
510                 goto found;
511             case XML_OP_ROOT:
512                 if (node->type == XML_NAMESPACE_DECL)
513                     goto rollback;
514                 node = node->parent;
515                 if ((node->type == XML_DOCUMENT_NODE) ||
516 #ifdef LIBXML_DOCB_ENABLED
517                     (node->type == XML_DOCB_DOCUMENT_NODE) ||
518 #endif
519                     (node->type == XML_HTML_DOCUMENT_NODE))
520                     continue;
521                 goto rollback;
522             case XML_OP_ELEM:
523                 if (node->type != XML_ELEMENT_NODE)
524                     goto rollback;
525                 if (step->value == NULL)
526                     continue;
527                 if (step->value[0] != node->name[0])
528                     goto rollback;
529                 if (!xmlStrEqual(step->value, node->name))
530                     goto rollback;
531
532                 /* Namespace test */
533                 if (node->ns == NULL) {
534                     if (step->value2 != NULL)
535                         goto rollback;
536                 } else if (node->ns->href != NULL) {
537                     if (step->value2 == NULL)
538                         goto rollback;
539                     if (!xmlStrEqual(step->value2, node->ns->href))
540                         goto rollback;
541                 }
542                 continue;
543             case XML_OP_CHILD: {
544                 xmlNodePtr lst;
545
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) &&
550 #endif
551                     (node->type != XML_HTML_DOCUMENT_NODE))
552                     goto rollback;
553
554                 lst = node->children;
555
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)))
561                             break;
562                         lst = lst->next;
563                     }
564                     if (lst != NULL)
565                         continue;
566                 }
567                 goto rollback;
568             }
569             case XML_OP_ATTR:
570                 if (node->type != XML_ATTRIBUTE_NODE)
571                     goto rollback;
572                 if (step->value != NULL) {
573                     if (step->value[0] != node->name[0])
574                         goto rollback;
575                     if (!xmlStrEqual(step->value, node->name))
576                         goto rollback;
577                 }
578                 /* Namespace test */
579                 if (node->ns == NULL) {
580                     if (step->value2 != NULL)
581                         goto rollback;
582                 } else if (step->value2 != NULL) {
583                     if (!xmlStrEqual(step->value2, node->ns->href))
584                         goto rollback;
585                 }
586                 continue;
587             case XML_OP_PARENT:
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) ||
592 #endif
593                     (node->type == XML_NAMESPACE_DECL))
594                     goto rollback;
595                 node = node->parent;
596                 if (node == NULL)
597                     goto rollback;
598                 if (step->value == NULL)
599                     continue;
600                 if (step->value[0] != node->name[0])
601                     goto rollback;
602                 if (!xmlStrEqual(step->value, node->name))
603                     goto rollback;
604                 /* Namespace test */
605                 if (node->ns == NULL) {
606                     if (step->value2 != NULL)
607                         goto rollback;
608                 } else if (node->ns->href != NULL) {
609                     if (step->value2 == NULL)
610                         goto rollback;
611                     if (!xmlStrEqual(step->value2, node->ns->href))
612                         goto rollback;
613                 }
614                 continue;
615             case XML_OP_ANCESTOR:
616                 /* TODO: implement coalescing of ANCESTOR/NODE ops */
617                 if (step->value == NULL) {
618                     i++;
619                     step = &comp->steps[i];
620                     if (step->op == XML_OP_ROOT)
621                         goto found;
622                     if (step->op != XML_OP_ELEM)
623                         goto rollback;
624                     if (step->value == NULL)
625                         return(-1);
626                 }
627                 if (node == NULL)
628                     goto rollback;
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) ||
633 #endif
634                     (node->type == XML_NAMESPACE_DECL))
635                     goto rollback;
636                 node = node->parent;
637                 while (node != NULL) {
638                     if ((node->type == XML_ELEMENT_NODE) &&
639                         (step->value[0] == node->name[0]) &&
640                         (xmlStrEqual(step->value, node->name))) {
641                         /* Namespace test */
642                         if (node->ns == NULL) {
643                             if (step->value2 == NULL)
644                                 break;
645                         } else if (node->ns->href != NULL) {
646                             if ((step->value2 != NULL) &&
647                                 (xmlStrEqual(step->value2, node->ns->href)))
648                                 break;
649                         }
650                     }
651                     node = node->parent;
652                 }
653                 if (node == NULL)
654                     goto rollback;
655                 /*
656                  * prepare a potential rollback from here
657                  * for ancestors of that node.
658                  */
659                 if (step->op == XML_OP_ANCESTOR)
660                     xmlPatPushState(&states, i, node);
661                 else
662                     xmlPatPushState(&states, i - 1, node);
663                 continue;
664             case XML_OP_NS:
665                 if (node->type != XML_ELEMENT_NODE)
666                     goto rollback;
667                 if (node->ns == NULL) {
668                     if (step->value != NULL)
669                         goto rollback;
670                 } else if (node->ns->href != NULL) {
671                     if (step->value == NULL)
672                         goto rollback;
673                     if (!xmlStrEqual(step->value, node->ns->href))
674                         goto rollback;
675                 }
676                 break;
677             case XML_OP_ALL:
678                 if (node->type != XML_ELEMENT_NODE)
679                     goto rollback;
680                 break;
681         }
682     }
683 found:
684     if (states.states != NULL) {
685         /* Free the rollback states */
686         xmlFree(states.states);
687     }
688     return(1);
689 rollback:
690     /* got an error try to rollback */
691     if (states.states == NULL)
692         return(0);
693     if (states.nbstates <= 0) {
694         xmlFree(states.states);
695         return(0);
696     }
697     states.nbstates--;
698     i = states.states[states.nbstates].step;
699     node = states.states[states.nbstates].node;
700 #if 0
701     fprintf(stderr, "Pop: %d, %s\n", i, node->name);
702 #endif
703     goto restart;
704 }
705
706 /************************************************************************
707  *                                                                      *
708  *                      Dedicated parser for templates                  *
709  *                                                                      *
710  ************************************************************************/
711
712 #define TODO                                                            \
713     xmlGenericError(xmlGenericErrorContext,                             \
714             "Unimplemented block at %s:%d\n",                           \
715             __FILE__, __LINE__);
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
721
722 #define SKIP_BLANKS                                                     \
723     while (IS_BLANK_CH(CUR)) NEXT
724
725 #define CURRENT (*ctxt->cur)
726 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
727
728
729 #define PUSH(op, val, val2)                                             \
730     if (xmlPatternAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
731
732 #define XSLT_ERROR(X)                                                   \
733     { xsltError(ctxt, __FILE__, __LINE__, X);                           \
734       ctxt->error = (X); return; }
735
736 #define XSLT_ERROR0(X)                                                  \
737     { xsltError(ctxt, __FILE__, __LINE__, X);                           \
738       ctxt->error = (X); return(0); }
739
740 #if 0
741 /**
742  * xmlPatScanLiteral:
743  * @ctxt:  the XPath Parser context
744  *
745  * Parse an XPath Litteral:
746  *
747  * [29] Literal ::= '"' [^"]* '"'
748  *                | "'" [^']* "'"
749  *
750  * Returns the Literal parsed or NULL
751  */
752
753 static xmlChar *
754 xmlPatScanLiteral(xmlPatParserContextPtr ctxt) {
755     const xmlChar *q, *cur;
756     xmlChar *ret = NULL;
757     int val, len;
758
759     SKIP_BLANKS;
760     if (CUR == '"') {
761         NEXT;
762         cur = q = CUR_PTR;
763         val = xmlStringCurrentChar(NULL, cur, &len);
764         while ((IS_CHAR(val)) && (val != '"')) {
765             cur += len;
766             val = xmlStringCurrentChar(NULL, cur, &len);
767         }
768         if (!IS_CHAR(val)) {
769             ctxt->error = 1;
770             return(NULL);
771         } else {
772             if (ctxt->dict)
773                 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
774             else
775                 ret = xmlStrndup(q, cur - q);
776         }
777         cur += len;
778         CUR_PTR = cur;
779     } else if (CUR == '\'') {
780         NEXT;
781         cur = q = CUR_PTR;
782         val = xmlStringCurrentChar(NULL, cur, &len);
783         while ((IS_CHAR(val)) && (val != '\'')) {
784             cur += len;
785             val = xmlStringCurrentChar(NULL, cur, &len);
786         }
787         if (!IS_CHAR(val)) {
788             ctxt->error = 1;
789             return(NULL);
790         } else {
791             if (ctxt->dict)
792                 ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
793             else
794                 ret = xmlStrndup(q, cur - q);
795         }
796         cur += len;
797         CUR_PTR = cur;
798     } else {
799         /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
800         ctxt->error = 1;
801         return(NULL);
802     }
803     return(ret);
804 }
805 #endif
806
807 /**
808  * xmlPatScanName:
809  * @ctxt:  the XPath Parser context
810  *
811  * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' |
812  *                  CombiningChar | Extender
813  *
814  * [5] Name ::= (Letter | '_' | ':') (NameChar)*
815  *
816  * [6] Names ::= Name (S Name)*
817  *
818  * Returns the Name parsed or NULL
819  */
820
821 static xmlChar *
822 xmlPatScanName(xmlPatParserContextPtr ctxt) {
823     const xmlChar *q, *cur;
824     xmlChar *ret = NULL;
825     int val, len;
826
827     SKIP_BLANKS;
828
829     cur = q = CUR_PTR;
830     val = xmlStringCurrentChar(NULL, cur, &len);
831     if (!IS_LETTER(val) && (val != '_') && (val != ':'))
832         return(NULL);
833
834     while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
835            (val == '.') || (val == '-') ||
836            (val == '_') ||
837            (IS_COMBINING(val)) ||
838            (IS_EXTENDER(val))) {
839         cur += len;
840         val = xmlStringCurrentChar(NULL, cur, &len);
841     }
842     if (ctxt->dict)
843         ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
844     else
845         ret = xmlStrndup(q, cur - q);
846     CUR_PTR = cur;
847     return(ret);
848 }
849
850 /**
851  * xmlPatScanNCName:
852  * @ctxt:  the XPath Parser context
853  *
854  * Parses a non qualified name
855  *
856  * Returns the Name parsed or NULL
857  */
858
859 static xmlChar *
860 xmlPatScanNCName(xmlPatParserContextPtr ctxt) {
861     const xmlChar *q, *cur;
862     xmlChar *ret = NULL;
863     int val, len;
864
865     SKIP_BLANKS;
866
867     cur = q = CUR_PTR;
868     val = xmlStringCurrentChar(NULL, cur, &len);
869     if (!IS_LETTER(val) && (val != '_'))
870         return(NULL);
871
872     while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
873            (val == '.') || (val == '-') ||
874            (val == '_') ||
875            (IS_COMBINING(val)) ||
876            (IS_EXTENDER(val))) {
877         cur += len;
878         val = xmlStringCurrentChar(NULL, cur, &len);
879     }
880     if (ctxt->dict)
881         ret = (xmlChar *) xmlDictLookup(ctxt->dict, q, cur - q);
882     else
883         ret = xmlStrndup(q, cur - q);
884     CUR_PTR = cur;
885     return(ret);
886 }
887
888 #if 0
889 /**
890  * xmlPatScanQName:
891  * @ctxt:  the XPath Parser context
892  * @prefix:  the place to store the prefix
893  *
894  * Parse a qualified name
895  *
896  * Returns the Name parsed or NULL
897  */
898
899 static xmlChar *
900 xmlPatScanQName(xmlPatParserContextPtr ctxt, xmlChar **prefix) {
901     xmlChar *ret = NULL;
902
903     *prefix = NULL;
904     ret = xmlPatScanNCName(ctxt);
905     if (CUR == ':') {
906         *prefix = ret;
907         NEXT;
908         ret = xmlPatScanNCName(ctxt);
909     }
910     return(ret);
911 }
912 #endif
913
914 /**
915  * xmlCompileAttributeTest:
916  * @ctxt:  the compilation context
917  *
918  * Compile an attribute test.
919  */
920 static void
921 xmlCompileAttributeTest(xmlPatParserContextPtr ctxt) {
922     xmlChar *token = NULL;
923     xmlChar *name = NULL;
924     xmlChar *URL = NULL;
925
926     SKIP_BLANKS;
927     name = xmlPatScanNCName(ctxt);
928     if (name == NULL) {
929         if (CUR == '*') {
930             PUSH(XML_OP_ATTR, NULL, NULL);
931             NEXT;
932         } else {
933             ERROR(NULL, NULL, NULL,
934                 "xmlCompileAttributeTest : Name expected\n");
935             ctxt->error = 1;
936         }
937         return;
938     }
939     if (CUR == ':') {
940         int i;
941         xmlChar *prefix = name;
942
943         NEXT;
944
945         if (IS_BLANK_CH(CUR)) {
946             ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
947             XML_PAT_FREE_STRING(ctxt, prefix);
948             ctxt->error = 1;
949             goto error;
950         }
951         /*
952         * This is a namespace match
953         */
954         token = xmlPatScanName(ctxt);
955         if ((prefix[0] == 'x') &&
956             (prefix[1] == 'm') &&
957             (prefix[2] == 'l') &&
958             (prefix[3] == 0))
959         {
960             XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE);
961         } else {
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])
965                     break;
966                 }
967             }
968             if (i >= ctxt->nb_namespaces) {
969                 ERROR5(NULL, NULL, NULL,
970                     "xmlCompileAttributeTest : no namespace bound to prefix %s\n",
971                     prefix);
972                 XML_PAT_FREE_STRING(ctxt, prefix);
973                 ctxt->error = 1;
974                 goto error;
975             }
976         }
977         XML_PAT_FREE_STRING(ctxt, prefix);
978         if (token == NULL) {
979             if (CUR == '*') {
980                 NEXT;
981                 PUSH(XML_OP_ATTR, NULL, URL);
982             } else {
983                 ERROR(NULL, NULL, NULL,
984                     "xmlCompileAttributeTest : Name expected\n");
985                 ctxt->error = 1;
986                 goto error;
987             }
988         } else {
989             PUSH(XML_OP_ATTR, token, URL);
990         }
991     } else {
992         PUSH(XML_OP_ATTR, name, NULL);
993     }
994     return;
995 error:
996     if (URL != NULL)
997         XML_PAT_FREE_STRING(ctxt, URL)
998     if (token != NULL)
999         XML_PAT_FREE_STRING(ctxt, token);
1000 }
1001
1002 /**
1003  * xmlCompileStepPattern:
1004  * @ctxt:  the compilation context
1005  *
1006  * Compile the Step Pattern and generates a precompiled
1007  * form suitable for fast matching.
1008  *
1009  * [3]    Step    ::=    '.' | NameTest
1010  * [4]    NameTest    ::=    QName | '*' | NCName ':' '*'
1011  */
1012
1013 static void
1014 xmlCompileStepPattern(xmlPatParserContextPtr ctxt) {
1015     xmlChar *token = NULL;
1016     xmlChar *name = NULL;
1017     xmlChar *URL = NULL;
1018     int hasBlanks = 0;
1019
1020     SKIP_BLANKS;
1021     if (CUR == '.') {
1022         /*
1023         * Context node.
1024         */
1025         NEXT;
1026         PUSH(XML_OP_ELEM, NULL, NULL);
1027         return;
1028     }
1029     if (CUR == '@') {
1030         /*
1031         * Attribute test.
1032         */
1033         if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1034             ERROR5(NULL, NULL, NULL,
1035                 "Unexpected attribute axis in '%s'.\n", ctxt->base);
1036             ctxt->error = 1;
1037             return;
1038         }
1039         NEXT;
1040         xmlCompileAttributeTest(ctxt);
1041         if (ctxt->error != 0)
1042             goto error;
1043         return;
1044     }
1045     name = xmlPatScanNCName(ctxt);
1046     if (name == NULL) {
1047         if (CUR == '*') {
1048             NEXT;
1049             PUSH(XML_OP_ALL, NULL, NULL);
1050             return;
1051         } else {
1052             ERROR(NULL, NULL, NULL,
1053                     "xmlCompileStepPattern : Name expected\n");
1054             ctxt->error = 1;
1055             return;
1056         }
1057     }
1058     if (IS_BLANK_CH(CUR)) {
1059         hasBlanks = 1;
1060         SKIP_BLANKS;
1061     }
1062     if (CUR == ':') {
1063         NEXT;
1064         if (CUR != ':') {
1065             xmlChar *prefix = name;
1066             int i;
1067
1068             if (hasBlanks || IS_BLANK_CH(CUR)) {
1069                 ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1070                 ctxt->error = 1;
1071                 goto error;
1072             }
1073             /*
1074              * This is a namespace match
1075              */
1076             token = xmlPatScanName(ctxt);
1077             if ((prefix[0] == 'x') &&
1078                 (prefix[1] == 'm') &&
1079                 (prefix[2] == 'l') &&
1080                 (prefix[3] == 0))
1081             {
1082                 XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1083             } else {
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])
1087                         break;
1088                     }
1089                 }
1090                 if (i >= ctxt->nb_namespaces) {
1091                     ERROR5(NULL, NULL, NULL,
1092                         "xmlCompileStepPattern : no namespace bound to prefix %s\n",
1093                         prefix);
1094                     ctxt->error = 1;
1095                     goto error;
1096                 }
1097             }
1098             XML_PAT_FREE_STRING(ctxt, prefix);
1099             name = NULL;
1100             if (token == NULL) {
1101                 if (CUR == '*') {
1102                     NEXT;
1103                     PUSH(XML_OP_NS, URL, NULL);
1104                 } else {
1105                     ERROR(NULL, NULL, NULL,
1106                             "xmlCompileStepPattern : Name expected\n");
1107                     ctxt->error = 1;
1108                     goto error;
1109                 }
1110             } else {
1111                 PUSH(XML_OP_ELEM, token, URL);
1112             }
1113         } else {
1114             NEXT;
1115             if (xmlStrEqual(name, (const xmlChar *) "child")) {
1116                 XML_PAT_FREE_STRING(ctxt, name);
1117                 name = xmlPatScanName(ctxt);
1118                 if (name == NULL) {
1119                     if (CUR == '*') {
1120                         NEXT;
1121                         PUSH(XML_OP_ALL, NULL, NULL);
1122                         return;
1123                     } else {
1124                         ERROR(NULL, NULL, NULL,
1125                             "xmlCompileStepPattern : QName expected\n");
1126                         ctxt->error = 1;
1127                         goto error;
1128                     }
1129                 }
1130                 if (CUR == ':') {
1131                     xmlChar *prefix = name;
1132                     int i;
1133
1134                     NEXT;
1135                     if (IS_BLANK_CH(CUR)) {
1136                         ERROR5(NULL, NULL, NULL, "Invalid QName.\n", NULL);
1137                         ctxt->error = 1;
1138                         goto error;
1139                     }
1140                     /*
1141                     * This is a namespace match
1142                     */
1143                     token = xmlPatScanName(ctxt);
1144                     if ((prefix[0] == 'x') &&
1145                         (prefix[1] == 'm') &&
1146                         (prefix[2] == 'l') &&
1147                         (prefix[3] == 0))
1148                     {
1149                         XML_PAT_COPY_NSNAME(ctxt, URL, XML_XML_NAMESPACE)
1150                     } else {
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])
1154                                 break;
1155                             }
1156                         }
1157                         if (i >= ctxt->nb_namespaces) {
1158                             ERROR5(NULL, NULL, NULL,
1159                                 "xmlCompileStepPattern : no namespace bound "
1160                                 "to prefix %s\n", prefix);
1161                             ctxt->error = 1;
1162                             goto error;
1163                         }
1164                     }
1165                     XML_PAT_FREE_STRING(ctxt, prefix);
1166                     name = NULL;
1167                     if (token == NULL) {
1168                         if (CUR == '*') {
1169                             NEXT;
1170                             PUSH(XML_OP_NS, URL, NULL);
1171                         } else {
1172                             ERROR(NULL, NULL, NULL,
1173                                 "xmlCompileStepPattern : Name expected\n");
1174                             ctxt->error = 1;
1175                             goto error;
1176                         }
1177                     } else {
1178                         PUSH(XML_OP_CHILD, token, URL);
1179                     }
1180                 } else
1181                     PUSH(XML_OP_CHILD, name, NULL);
1182                 return;
1183             } else if (xmlStrEqual(name, (const xmlChar *) "attribute")) {
1184                 XML_PAT_FREE_STRING(ctxt, name)
1185                 name = NULL;
1186                 if (XML_STREAM_XS_IDC_SEL(ctxt->comp)) {
1187                     ERROR5(NULL, NULL, NULL,
1188                         "Unexpected attribute axis in '%s'.\n", ctxt->base);
1189                     ctxt->error = 1;
1190                     goto error;
1191                 }
1192                 xmlCompileAttributeTest(ctxt);
1193                 if (ctxt->error != 0)
1194                     goto error;
1195                 return;
1196             } else {
1197                 ERROR5(NULL, NULL, NULL,
1198                     "The 'element' or 'attribute' axis is expected.\n", NULL);
1199                 ctxt->error = 1;
1200                 goto error;
1201             }
1202         }
1203     } else if (CUR == '*') {
1204         if (name != NULL) {
1205             ctxt->error = 1;
1206             goto error;
1207         }
1208         NEXT;
1209         PUSH(XML_OP_ALL, token, NULL);
1210     } else {
1211         PUSH(XML_OP_ELEM, name, NULL);
1212     }
1213     return;
1214 error:
1215     if (URL != NULL)
1216         XML_PAT_FREE_STRING(ctxt, URL)
1217     if (token != NULL)
1218         XML_PAT_FREE_STRING(ctxt, token)
1219     if (name != NULL)
1220         XML_PAT_FREE_STRING(ctxt, name)
1221 }
1222
1223 /**
1224  * xmlCompilePathPattern:
1225  * @ctxt:  the compilation context
1226  *
1227  * Compile the Path Pattern and generates a precompiled
1228  * form suitable for fast matching.
1229  *
1230  * [5]    Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1231  */
1232 static void
1233 xmlCompilePathPattern(xmlPatParserContextPtr ctxt) {
1234     SKIP_BLANKS;
1235     if (CUR == '/') {
1236         ctxt->comp->flags |= PAT_FROM_ROOT;
1237     } else if ((CUR == '.') || (ctxt->comp->flags & XML_PATTERN_NOTPATTERN)) {
1238         ctxt->comp->flags |= PAT_FROM_CUR;
1239     }
1240
1241     if ((CUR == '/') && (NXT(1) == '/')) {
1242         PUSH(XML_OP_ANCESTOR, NULL, NULL);
1243         NEXT;
1244         NEXT;
1245     } else if ((CUR == '.') && (NXT(1) == '/') && (NXT(2) == '/')) {
1246         PUSH(XML_OP_ANCESTOR, NULL, NULL);
1247         NEXT;
1248         NEXT;
1249         NEXT;
1250         /* Check for incompleteness. */
1251         SKIP_BLANKS;
1252         if (CUR == 0) {
1253             ERROR5(NULL, NULL, NULL,
1254                "Incomplete expression '%s'.\n", ctxt->base);
1255             ctxt->error = 1;
1256             goto error;
1257         }
1258     }
1259     if (CUR == '@') {
1260         NEXT;
1261         xmlCompileAttributeTest(ctxt);
1262         SKIP_BLANKS;
1263         /* TODO: check for incompleteness */
1264         if (CUR != 0) {
1265             xmlCompileStepPattern(ctxt);
1266             if (ctxt->error != 0)
1267                 goto error;
1268         }
1269     } else {
1270         if (CUR == '/') {
1271             PUSH(XML_OP_ROOT, NULL, NULL);
1272             NEXT;
1273             /* Check for incompleteness. */
1274             SKIP_BLANKS;
1275             if (CUR == 0) {
1276                 ERROR5(NULL, NULL, NULL,
1277                     "Incomplete expression '%s'.\n", ctxt->base);
1278                 ctxt->error = 1;
1279                 goto error;
1280             }
1281         }
1282         xmlCompileStepPattern(ctxt);
1283         if (ctxt->error != 0)
1284             goto error;
1285         SKIP_BLANKS;
1286         while (CUR == '/') {
1287             if (NXT(1) == '/') {
1288                 PUSH(XML_OP_ANCESTOR, NULL, NULL);
1289                 NEXT;
1290                 NEXT;
1291                 SKIP_BLANKS;
1292                 xmlCompileStepPattern(ctxt);
1293                 if (ctxt->error != 0)
1294                     goto error;
1295             } else {
1296                 PUSH(XML_OP_PARENT, NULL, NULL);
1297                 NEXT;
1298                 SKIP_BLANKS;
1299                 if (CUR == 0) {
1300                     ERROR5(NULL, NULL, NULL,
1301                     "Incomplete expression '%s'.\n", ctxt->base);
1302                     ctxt->error = 1;
1303                     goto error;
1304                 }
1305                 xmlCompileStepPattern(ctxt);
1306                 if (ctxt->error != 0)
1307                     goto error;
1308             }
1309         }
1310     }
1311     if (CUR != 0) {
1312         ERROR5(NULL, NULL, NULL,
1313                "Failed to compile pattern %s\n", ctxt->base);
1314         ctxt->error = 1;
1315     }
1316 error:
1317     return;
1318 }
1319
1320 /**
1321  * xmlCompileIDCXPathPath:
1322  * @ctxt:  the compilation context
1323  *
1324  * Compile the Path Pattern and generates a precompiled
1325  * form suitable for fast matching.
1326  *
1327  * [5]    Path    ::=    ('.//')? ( Step '/' )* ( Step | '@' NameTest )
1328  */
1329 static void
1330 xmlCompileIDCXPathPath(xmlPatParserContextPtr ctxt) {
1331     SKIP_BLANKS;
1332     if (CUR == '/') {
1333         ERROR5(NULL, NULL, NULL,
1334             "Unexpected selection of the document root in '%s'.\n",
1335             ctxt->base);
1336         goto error;
1337     }
1338     ctxt->comp->flags |= PAT_FROM_CUR;
1339
1340     if (CUR == '.') {
1341         /* "." - "self::node()" */
1342         NEXT;
1343         SKIP_BLANKS;
1344         if (CUR == 0) {
1345             /*
1346             * Selection of the context node.
1347             */
1348             PUSH(XML_OP_ELEM, NULL, NULL);
1349             return;
1350         }
1351         if (CUR != '/') {
1352             /* TODO: A more meaningful error message. */
1353             ERROR5(NULL, NULL, NULL,
1354             "Unexpected token after '.' in '%s'.\n", ctxt->base);
1355             goto error;
1356         }
1357         /* "./" - "self::node()/" */
1358         NEXT;
1359         SKIP_BLANKS;
1360         if (CUR == '/') {
1361             if (IS_BLANK_CH(PEEKPREV(1))) {
1362                 /*
1363                 * Disallow "./ /"
1364                 */
1365                 ERROR5(NULL, NULL, NULL,
1366                     "Unexpected '/' token in '%s'.\n", ctxt->base);
1367                 goto error;
1368             }
1369             /* ".//" - "self:node()/descendant-or-self::node()/" */
1370             PUSH(XML_OP_ANCESTOR, NULL, NULL);
1371             NEXT;
1372             SKIP_BLANKS;
1373         }
1374         if (CUR == 0)
1375             goto error_unfinished;
1376     }
1377     /*
1378     * Process steps.
1379     */
1380     do {
1381         xmlCompileStepPattern(ctxt);
1382         if (ctxt->error != 0)
1383             goto error;
1384         SKIP_BLANKS;
1385         if (CUR != '/')
1386             break;
1387         PUSH(XML_OP_PARENT, NULL, NULL);
1388         NEXT;
1389         SKIP_BLANKS;
1390         if (CUR == '/') {
1391             /*
1392             * Disallow subsequent '//'.
1393             */
1394             ERROR5(NULL, NULL, NULL,
1395                 "Unexpected subsequent '//' in '%s'.\n",
1396                 ctxt->base);
1397             goto error;
1398         }
1399         if (CUR == 0)
1400             goto error_unfinished;
1401
1402     } while (CUR != 0);
1403
1404     if (CUR != 0) {
1405         ERROR5(NULL, NULL, NULL,
1406             "Failed to compile expression '%s'.\n", ctxt->base);
1407         ctxt->error = 1;
1408     }
1409     return;
1410 error:
1411     ctxt->error = 1;
1412     return;
1413
1414 error_unfinished:
1415     ctxt->error = 1;
1416     ERROR5(NULL, NULL, NULL,
1417         "Unfinished expression '%s'.\n", ctxt->base);
1418     return;
1419 }
1420
1421 /************************************************************************
1422  *                                                                      *
1423  *                      The streaming code                              *
1424  *                                                                      *
1425  ************************************************************************/
1426
1427 #ifdef DEBUG_STREAMING
1428 static void
1429 xmlDebugStreamComp(xmlStreamCompPtr stream) {
1430     int i;
1431
1432     if (stream == NULL) {
1433         printf("Stream: NULL\n");
1434         return;
1435     }
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);
1440         }
1441         if (stream->steps[i].name == NULL) {
1442             printf("* ");
1443         } else {
1444             printf("%s ", stream->steps[i].name);
1445         }
1446         if (stream->steps[i].flags & XML_STREAM_STEP_ROOT)
1447             printf("root ");
1448         if (stream->steps[i].flags & XML_STREAM_STEP_DESC)
1449             printf("// ");
1450         if (stream->steps[i].flags & XML_STREAM_STEP_FINAL)
1451             printf("final ");
1452         printf("\n");
1453     }
1454 }
1455 static void
1456 xmlDebugStreamCtxt(xmlStreamCtxtPtr ctxt, int match) {
1457     int i;
1458
1459     if (ctxt == NULL) {
1460         printf("Stream: NULL\n");
1461         return;
1462     }
1463     printf("Stream: level %d, %d states: ", ctxt->level, ctxt->nbState);
1464     if (match)
1465         printf("matches\n");
1466     else
1467         printf("\n");
1468     for (i = 0;i < ctxt->nbState;i++) {
1469         if (ctxt->states[2 * i] < 0)
1470             printf(" %d: free\n", i);
1471         else {
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)
1476                 printf(" //\n");
1477             else
1478                 printf("\n");
1479         }
1480     }
1481 }
1482 #endif
1483 /**
1484  * xmlNewStreamComp:
1485  * @size: the number of expected steps
1486  *
1487  * build a new compiled pattern for streaming
1488  *
1489  * Returns the new structure or NULL in case of error.
1490  */
1491 static xmlStreamCompPtr
1492 xmlNewStreamComp(int size) {
1493     xmlStreamCompPtr cur;
1494
1495     if (size < 4)
1496         size  = 4;
1497
1498     cur = (xmlStreamCompPtr) xmlMalloc(sizeof(xmlStreamComp));
1499     if (cur == NULL) {
1500         ERROR(NULL, NULL, NULL,
1501                 "xmlNewStreamComp: malloc failed\n");
1502         return(NULL);
1503     }
1504     memset(cur, 0, sizeof(xmlStreamComp));
1505     cur->steps = (xmlStreamStepPtr) xmlMalloc(size * sizeof(xmlStreamStep));
1506     if (cur->steps == NULL) {
1507         xmlFree(cur);
1508         ERROR(NULL, NULL, NULL,
1509               "xmlNewStreamComp: malloc failed\n");
1510         return(NULL);
1511     }
1512     cur->nbStep = 0;
1513     cur->maxStep = size;
1514     return(cur);
1515 }
1516
1517 /**
1518  * xmlFreeStreamComp:
1519  * @comp: the compiled pattern for streaming
1520  *
1521  * Free the compiled pattern for streaming
1522  */
1523 static void
1524 xmlFreeStreamComp(xmlStreamCompPtr comp) {
1525     if (comp != NULL) {
1526         if (comp->steps != NULL)
1527             xmlFree(comp->steps);
1528         if (comp->dict != NULL)
1529             xmlDictFree(comp->dict);
1530         xmlFree(comp);
1531     }
1532 }
1533
1534 /**
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
1540  *
1541  * Add a new step to the compiled pattern
1542  *
1543  * Returns -1 in case of error or the step index if successful
1544  */
1545 static int
1546 xmlStreamCompAddStep(xmlStreamCompPtr comp, const xmlChar *name,
1547                      const xmlChar *ns, int nodeType, int flags) {
1548     xmlStreamStepPtr cur;
1549
1550     if (comp->nbStep >= comp->maxStep) {
1551         cur = (xmlStreamStepPtr) xmlRealloc(comp->steps,
1552                                  comp->maxStep * 2 * sizeof(xmlStreamStep));
1553         if (cur == NULL) {
1554             ERROR(NULL, NULL, NULL,
1555                   "xmlNewStreamComp: malloc failed\n");
1556             return(-1);
1557         }
1558         comp->steps = cur;
1559         comp->maxStep *= 2;
1560     }
1561     cur = &comp->steps[comp->nbStep++];
1562     cur->flags = flags;
1563     cur->name = name;
1564     cur->ns = ns;
1565     cur->nodeType = nodeType;
1566     return(comp->nbStep - 1);
1567 }
1568
1569 /**
1570  * xmlStreamCompile:
1571  * @comp: the precompiled pattern
1572  *
1573  * Tries to stream compile a pattern
1574  *
1575  * Returns -1 in case of failure and 0 in case of success.
1576  */
1577 static int
1578 xmlStreamCompile(xmlPatternPtr comp) {
1579     xmlStreamCompPtr stream;
1580     int i, s = 0, root = 0, flags = 0, prevs = -1;
1581     xmlStepOp step;
1582
1583     if ((comp == NULL) || (comp->steps == NULL))
1584         return(-1);
1585     /*
1586      * special case for .
1587      */
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);
1593         if (stream == NULL)
1594             return(-1);
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;
1598         return(0);
1599     }
1600
1601     stream = xmlNewStreamComp((comp->nbStep / 2) + 1);
1602     if (stream == NULL)
1603         return(-1);
1604     if (comp->dict != NULL) {
1605         stream->dict = comp->dict;
1606         xmlDictReference(stream->dict);
1607     }
1608
1609     i = 0;
1610     if (comp->flags & PAT_FROM_ROOT)
1611         stream->flags |= XML_STREAM_FROM_ROOT;
1612
1613     for (;i < comp->nbStep;i++) {
1614         step = comp->steps[i];
1615         switch (step.op) {
1616             case XML_OP_END:
1617                 break;
1618             case XML_OP_ROOT:
1619                 if (i != 0)
1620                     goto error;
1621                 root = 1;
1622                 break;
1623             case XML_OP_NS:
1624                 s = xmlStreamCompAddStep(stream, NULL, step.value,
1625                     XML_ELEMENT_NODE, flags);
1626                 if (s < 0)
1627                     goto error;
1628                 prevs = s;
1629                 flags = 0;
1630                 break;
1631             case XML_OP_ATTR:
1632                 flags |= XML_STREAM_STEP_ATTR;
1633                 prevs = -1;
1634                 s = xmlStreamCompAddStep(stream,
1635                     step.value, step.value2, XML_ATTRIBUTE_NODE, flags);
1636                 flags = 0;
1637                 if (s < 0)
1638                     goto error;
1639                 break;
1640             case XML_OP_ELEM:
1641                 if ((step.value == NULL) && (step.value2 == NULL)) {
1642                     /*
1643                     * We have a "." or "self::node()" here.
1644                     * Eliminate redundant self::node() tests like in "/./."
1645                     * or "//./"
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.
1649                     */
1650                     if ((comp->nbStep == i + 1) &&
1651                         (flags & XML_STREAM_STEP_DESC)) {
1652                         /*
1653                         * Mark the special case where the expression resolves
1654                         * to any type of node.
1655                         */
1656                         if (comp->nbStep == i + 1) {
1657                             stream->flags |= XML_STREAM_FINAL_IS_ANY_NODE;
1658                         }
1659                         flags |= XML_STREAM_STEP_NODE;
1660                         s = xmlStreamCompAddStep(stream, NULL, NULL,
1661                             XML_STREAM_ANY_NODE, flags);
1662                         if (s < 0)
1663                             goto error;
1664                         flags = 0;
1665                         /*
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.
1670                         */
1671                         if (prevs != -1) {
1672                             stream->steps[prevs].flags |= XML_STREAM_STEP_IN_SET;
1673                             prevs = -1;
1674                         }
1675                         break;
1676
1677                     } else {
1678                         /* Just skip this one. */
1679                         continue;
1680                     }
1681                 }
1682                 /* An element node. */
1683                 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1684                     XML_ELEMENT_NODE, flags);
1685                 if (s < 0)
1686                     goto error;
1687                 prevs = s;
1688                 flags = 0;
1689                 break;
1690             case XML_OP_CHILD:
1691                 /* An element node child. */
1692                 s = xmlStreamCompAddStep(stream, step.value, step.value2,
1693                     XML_ELEMENT_NODE, flags);
1694                 if (s < 0)
1695                     goto error;
1696                 prevs = s;
1697                 flags = 0;
1698                 break;
1699             case XML_OP_ALL:
1700                 s = xmlStreamCompAddStep(stream, NULL, NULL,
1701                     XML_ELEMENT_NODE, flags);
1702                 if (s < 0)
1703                     goto error;
1704                 prevs = s;
1705                 flags = 0;
1706                 break;
1707             case XML_OP_PARENT:
1708                 break;
1709             case XML_OP_ANCESTOR:
1710                 /* Skip redundant continuations. */
1711                 if (flags & XML_STREAM_STEP_DESC)
1712                     break;
1713                 flags |= XML_STREAM_STEP_DESC;
1714                 /*
1715                 * Mark the expression as having "//".
1716                 */
1717                 if ((stream->flags & XML_STREAM_DESC) == 0)
1718                     stream->flags |= XML_STREAM_DESC;
1719                 break;
1720         }
1721     }
1722     if ((! root) && (comp->flags & XML_PATTERN_NOTPATTERN) == 0) {
1723         /*
1724         * If this should behave like a real pattern, we will mark
1725         * the first step as having "//", to be reentrant on every
1726         * tree level.
1727         */
1728         if ((stream->flags & XML_STREAM_DESC) == 0)
1729             stream->flags |= XML_STREAM_DESC;
1730
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;
1734         }
1735     }
1736     if (stream->nbStep <= s)
1737         goto error;
1738     stream->steps[s].flags |= XML_STREAM_STEP_FINAL;
1739     if (root)
1740         stream->steps[0].flags |= XML_STREAM_STEP_ROOT;
1741 #ifdef DEBUG_STREAMING
1742     xmlDebugStreamComp(stream);
1743 #endif
1744     comp->stream = stream;
1745     return(0);
1746 error:
1747     xmlFreeStreamComp(stream);
1748     return(0);
1749 }
1750
1751 /**
1752  * xmlNewStreamCtxt:
1753  * @size: the number of expected states
1754  *
1755  * build a new stream context
1756  *
1757  * Returns the new structure or NULL in case of error.
1758  */
1759 static xmlStreamCtxtPtr
1760 xmlNewStreamCtxt(xmlStreamCompPtr stream) {
1761     xmlStreamCtxtPtr cur;
1762
1763     cur = (xmlStreamCtxtPtr) xmlMalloc(sizeof(xmlStreamCtxt));
1764     if (cur == NULL) {
1765         ERROR(NULL, NULL, NULL,
1766                 "xmlNewStreamCtxt: malloc failed\n");
1767         return(NULL);
1768     }
1769     memset(cur, 0, sizeof(xmlStreamCtxt));
1770     cur->states = (int *) xmlMalloc(4 * 2 * sizeof(int));
1771     if (cur->states == NULL) {
1772         xmlFree(cur);
1773         ERROR(NULL, NULL, NULL,
1774               "xmlNewStreamCtxt: malloc failed\n");
1775         return(NULL);
1776     }
1777     cur->nbState = 0;
1778     cur->maxState = 4;
1779     cur->level = 0;
1780     cur->comp = stream;
1781     cur->blockLevel = -1;
1782     return(cur);
1783 }
1784
1785 /**
1786  * xmlFreeStreamCtxt:
1787  * @stream: the stream context
1788  *
1789  * Free the stream context
1790  */
1791 void
1792 xmlFreeStreamCtxt(xmlStreamCtxtPtr stream) {
1793     xmlStreamCtxtPtr next;
1794
1795     while (stream != NULL) {
1796         next = stream->next;
1797         if (stream->states != NULL)
1798             xmlFree(stream->states);
1799         xmlFree(stream);
1800         stream = next;
1801     }
1802 }
1803
1804 /**
1805  * xmlStreamCtxtAddState:
1806  * @comp: the stream context
1807  * @idx: the step index for that streaming state
1808  *
1809  * Add a new state to the stream context
1810  *
1811  * Returns -1 in case of error or the state index if successful
1812  */
1813 static int
1814 xmlStreamCtxtAddState(xmlStreamCtxtPtr comp, int idx, int level) {
1815     int i;
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;
1820             return(i);
1821         }
1822     }
1823     if (comp->nbState >= comp->maxState) {
1824         int *cur;
1825
1826         cur = (int *) xmlRealloc(comp->states,
1827                                  comp->maxState * 4 * sizeof(int));
1828         if (cur == NULL) {
1829             ERROR(NULL, NULL, NULL,
1830                   "xmlNewStreamCtxt: malloc failed\n");
1831             return(-1);
1832         }
1833         comp->states = cur;
1834         comp->maxState *= 2;
1835     }
1836     comp->states[2 * comp->nbState] = idx;
1837     comp->states[2 * comp->nbState++ + 1] = level;
1838     return(comp->nbState - 1);
1839 }
1840
1841 /**
1842  * xmlStreamPushInternal:
1843  * @stream: the stream context
1844  * @name: the current name
1845  * @ns: the namespace name
1846  * @nodeType: the type of the node
1847  *
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.
1853  *
1854  * Returns: -1 in case of error, 1 if the current state in the stream is a
1855  *    match and 0 otherwise.
1856  */
1857 static int
1858 xmlStreamPushInternal(xmlStreamCtxtPtr stream,
1859                       const xmlChar *name, const xmlChar *ns,
1860                       int nodeType) {
1861     int ret = 0, err = 0, final = 0, tmp, i, m, match, stepNr, desc;
1862     xmlStreamCompPtr comp;
1863     xmlStreamStep step;
1864 #ifdef DEBUG_STREAMING
1865     xmlStreamCtxtPtr orig = stream;
1866 #endif
1867
1868     if ((stream == NULL) || (stream->nbState < 0))
1869         return(-1);
1870
1871     while (stream != NULL) {
1872         comp = stream->comp;
1873
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;
1878             stream->level = 0;
1879             stream->blockLevel = -1;
1880             if (comp->flags & XML_STREAM_FROM_ROOT) {
1881                 if (comp->nbStep == 0) {
1882                     /* TODO: We have a "/." here? */
1883                     ret = 1;
1884                 } else {
1885                     if ((comp->nbStep == 1) &&
1886                         (comp->steps[0].nodeType == XML_STREAM_ANY_NODE) &&
1887                         (comp->steps[0].flags & XML_STREAM_STEP_DESC))
1888                     {
1889                         /*
1890                         * In the case of "//." the document node will match
1891                         * as well.
1892                         */
1893                         ret = 1;
1894                     } else if (comp->steps[0].flags & XML_STREAM_STEP_ROOT) {
1895                         /* TODO: Do we need this ? */
1896                         tmp = xmlStreamCtxtAddState(stream, 0, 0);
1897                         if (tmp < 0)
1898                             err++;
1899                     }
1900                 }
1901             }
1902             stream = stream->next;
1903             continue; /* while */
1904         }
1905
1906         /*
1907         * Fast check for ".".
1908         */
1909         if (comp->nbStep == 0) {
1910             /*
1911              * / and . are handled at the XPath node set creation
1912              * level by checking min depth
1913              */
1914             if (stream->flags & XML_PATTERN_XPATH) {
1915                 stream = stream->next;
1916                 continue; /* while */
1917             }
1918             /*
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.
1922             */
1923             if ((nodeType != XML_ATTRIBUTE_NODE) &&
1924                 (((stream->flags & XML_PATTERN_NOTPATTERN) == 0) ||
1925                 (stream->level == 0))) {
1926                     ret = 1;
1927             }
1928             stream->level++;
1929             goto stream_next;
1930         }
1931         if (stream->blockLevel != -1) {
1932             /*
1933             * Skip blocked expressions.
1934             */
1935             stream->level++;
1936             goto stream_next;
1937         }
1938
1939         if ((nodeType != XML_ELEMENT_NODE) &&
1940             (nodeType != XML_ATTRIBUTE_NODE) &&
1941             ((comp->flags & XML_STREAM_FINAL_IS_ANY_NODE) == 0)) {
1942             /*
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?
1946             */
1947             stream->level++;
1948             goto stream_next;
1949         }
1950
1951         /*
1952          * Check evolution of existing states
1953          */
1954         i = 0;
1955         m = stream->nbState;
1956         while (i < m) {
1957             if ((comp->flags & XML_STREAM_DESC) == 0) {
1958                 /*
1959                 * If there is no "//", then only the last
1960                 * added state is of interest.
1961                 */
1962                 stepNr = stream->states[2 * (stream->nbState -1)];
1963                 /*
1964                 * TODO: Security check, should not happen, remove it.
1965                 */
1966                 if (stream->states[(2 * (stream->nbState -1)) + 1] <
1967                     stream->level) {
1968                     return (-1);
1969                 }
1970                 desc = 0;
1971                 /* loop-stopper */
1972                 i = m;
1973             } else {
1974                 /*
1975                 * If there are "//", then we need to process every "//"
1976                 * occuring in the states, plus any other state for this
1977                 * level.
1978                 */
1979                 stepNr = stream->states[2 * i];
1980
1981                 /* TODO: should not happen anymore: dead states */
1982                 if (stepNr < 0)
1983                     goto next_state;
1984
1985                 tmp = stream->states[(2 * i) + 1];
1986
1987                 /* skip new states just added */
1988                 if (tmp > stream->level)
1989                     goto next_state;
1990
1991                 /* skip states at ancestor levels, except if "//" */
1992                 desc = comp->steps[stepNr].flags & XML_STREAM_STEP_DESC;
1993                 if ((tmp < stream->level) && (!desc))
1994                     goto next_state;
1995             }
1996             /*
1997             * Check for correct node-type.
1998             */
1999             step = comp->steps[stepNr];
2000             if (step.nodeType != nodeType) {
2001                 if (step.nodeType == XML_ATTRIBUTE_NODE) {
2002                     /*
2003                     * Block this expression for deeper evaluation.
2004                     */
2005                     if ((comp->flags & XML_STREAM_DESC) == 0)
2006                         stream->blockLevel = stream->level +1;
2007                     goto next_state;
2008                 } else if (step.nodeType != XML_STREAM_ANY_NODE)
2009                     goto next_state;
2010             }
2011             /*
2012             * Compare local/namespace-name.
2013             */
2014             match = 0;
2015             if (step.nodeType == XML_STREAM_ANY_NODE) {
2016                 match = 1;
2017             } else if (step.name == NULL) {
2018                 if (step.ns == NULL) {
2019                     /*
2020                     * This lets through all elements/attributes.
2021                     */
2022                     match = 1;
2023                 } else if (ns != NULL)
2024                     match = xmlStrEqual(step.ns, ns);
2025             } else if (((step.ns != NULL) == (ns != NULL)) &&
2026                 (name != NULL) &&
2027                 (step.name[0] == name[0]) &&
2028                 xmlStrEqual(step.name, name) &&
2029                 ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2030             {
2031                 match = 1;
2032             }
2033 #if 0
2034 /*
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 !
2038 */
2039             } else if (comp->dict) {
2040                 if (step.name == NULL) {
2041                     if (step.ns == NULL)
2042                         match = 1;
2043                     else
2044                         match = (step.ns == ns);
2045                 } else {
2046                     match = ((step.name == name) && (step.ns == ns));
2047                 }
2048 #endif /* if 0 ------------------------------------------------------- */
2049             if (match) {
2050                 final = step.flags & XML_STREAM_STEP_FINAL;
2051                 if (desc) {
2052                     if (final) {
2053                         ret = 1;
2054                     } else {
2055                         /* descending match create a new state */
2056                         xmlStreamCtxtAddState(stream, stepNr + 1,
2057                                               stream->level + 1);
2058                     }
2059                 } else {
2060                     if (final) {
2061                         ret = 1;
2062                     } else {
2063                         xmlStreamCtxtAddState(stream, stepNr + 1,
2064                                               stream->level + 1);
2065                     }
2066                 }
2067                 if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2068                     /*
2069                     * Check if we have a special case like "foo/bar//.", where
2070                     * "foo" is selected as well.
2071                     */
2072                     ret = 1;
2073                 }
2074             }
2075             if (((comp->flags & XML_STREAM_DESC) == 0) &&
2076                 ((! match) || final))  {
2077                 /*
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.
2081                 */
2082                 stream->blockLevel = stream->level +1;
2083             }
2084 next_state:
2085             i++;
2086         }
2087
2088         stream->level++;
2089
2090         /*
2091         * Re/enter the expression.
2092         * Don't reenter if it's an absolute expression like "/foo",
2093         *   except "//foo".
2094         */
2095         step = comp->steps[0];
2096         if (step.flags & XML_STREAM_STEP_ROOT)
2097             goto stream_next;
2098
2099         desc = step.flags & XML_STREAM_STEP_DESC;
2100         if (stream->flags & XML_PATTERN_NOTPATTERN) {
2101             /*
2102             * Re/enter the expression if it is a "descendant" one,
2103             * or if we are at the 1st level of evaluation.
2104             */
2105
2106             if (stream->level == 1) {
2107                 if (XML_STREAM_XS_IDC(stream)) {
2108                     /*
2109                     * XS-IDC: The missing "self::node()" will always
2110                     * match the first given node.
2111                     */
2112                     goto stream_next;
2113                 } else
2114                     goto compare;
2115             }
2116             /*
2117             * A "//" is always reentrant.
2118             */
2119             if (desc)
2120                 goto compare;
2121
2122             /*
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.
2126             */
2127             if ((stream->level == 2) && XML_STREAM_XS_IDC(stream))
2128                 goto compare;
2129
2130             goto stream_next;
2131         }
2132
2133 compare:
2134         /*
2135         * Check expected node-type.
2136         */
2137         if (step.nodeType != nodeType) {
2138             if (nodeType == XML_ATTRIBUTE_NODE)
2139                 goto stream_next;
2140             else if (step.nodeType != XML_STREAM_ANY_NODE)
2141                 goto stream_next;
2142         }
2143         /*
2144         * Compare local/namespace-name.
2145         */
2146         match = 0;
2147         if (step.nodeType == XML_STREAM_ANY_NODE) {
2148             match = 1;
2149         } else if (step.name == NULL) {
2150             if (step.ns == NULL) {
2151                 /*
2152                 * This lets through all elements/attributes.
2153                 */
2154                 match = 1;
2155             } else if (ns != NULL)
2156                 match = xmlStrEqual(step.ns, ns);
2157         } else if (((step.ns != NULL) == (ns != NULL)) &&
2158             (name != NULL) &&
2159             (step.name[0] == name[0]) &&
2160             xmlStrEqual(step.name, name) &&
2161             ((step.ns == ns) || xmlStrEqual(step.ns, ns)))
2162         {
2163             match = 1;
2164         }
2165         final = step.flags & XML_STREAM_STEP_FINAL;
2166         if (match) {
2167             if (final)
2168                 ret = 1;
2169             else
2170                 xmlStreamCtxtAddState(stream, 1, stream->level);
2171             if ((ret != 1) && (step.flags & XML_STREAM_STEP_IN_SET)) {
2172                 /*
2173                 * Check if we have a special case like "foo//.", where
2174                 * "foo" is selected as well.
2175                 */
2176                 ret = 1;
2177             }
2178         }
2179         if (((comp->flags & XML_STREAM_DESC) == 0) &&
2180             ((! match) || final))  {
2181             /*
2182             * Mark this expression as blocked for any evaluation at
2183             * deeper levels.
2184             */
2185             stream->blockLevel = stream->level;
2186         }
2187
2188 stream_next:
2189         stream = stream->next;
2190     } /* while stream != NULL */
2191
2192     if (err > 0)
2193         ret = -1;
2194 #ifdef DEBUG_STREAMING
2195     xmlDebugStreamCtxt(orig, ret);
2196 #endif
2197     return(ret);
2198 }
2199
2200 /**
2201  * xmlStreamPush:
2202  * @stream: the stream context
2203  * @name: the current name
2204  * @ns: the namespace name
2205  *
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.
2212  *
2213  * Returns: -1 in case of error, 1 if the current state in the stream is a
2214  *    match and 0 otherwise.
2215  */
2216 int
2217 xmlStreamPush(xmlStreamCtxtPtr stream,
2218               const xmlChar *name, const xmlChar *ns) {
2219     return (xmlStreamPushInternal(stream, name, ns, (int) XML_ELEMENT_NODE));
2220 }
2221
2222 /**
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
2228  *
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.
2237  *
2238  * Returns: -1 in case of error, 1 if the current state in the stream is a
2239  *    match and 0 otherwise.
2240  */
2241 int
2242 xmlStreamPushNode(xmlStreamCtxtPtr stream,
2243                   const xmlChar *name, const xmlChar *ns,
2244                   int nodeType)
2245 {
2246     return (xmlStreamPushInternal(stream, name, ns,
2247         nodeType));
2248 }
2249
2250 /**
2251 * xmlStreamPushAttr:
2252 * @stream: the stream context
2253 * @name: the current name
2254 * @ns: the namespace name
2255 *
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.
2262 *
2263 * Returns: -1 in case of error, 1 if the current state in the stream is a
2264 *    match and 0 otherwise.
2265 */
2266 int
2267 xmlStreamPushAttr(xmlStreamCtxtPtr stream,
2268                   const xmlChar *name, const xmlChar *ns) {
2269     return (xmlStreamPushInternal(stream, name, ns, (int) XML_ATTRIBUTE_NODE));
2270 }
2271
2272 /**
2273  * xmlStreamPop:
2274  * @stream: the stream context
2275  *
2276  * push one level from the stream.
2277  *
2278  * Returns: -1 in case of error, 0 otherwise.
2279  */
2280 int
2281 xmlStreamPop(xmlStreamCtxtPtr stream) {
2282     int i, lev;
2283
2284     if (stream == NULL)
2285         return(-1);
2286     while (stream != NULL) {
2287         /*
2288         * Reset block-level.
2289         */
2290         if (stream->blockLevel == stream->level)
2291             stream->blockLevel = -1;
2292
2293         /*
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)
2297          */
2298         if (stream->level)
2299             stream->level--;
2300         /*
2301          * Check evolution of existing states
2302          */
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)
2307                 stream->nbState--;
2308             if (lev <= stream->level)
2309                 break;
2310         }
2311         stream = stream->next;
2312     }
2313     return(0);
2314 }
2315
2316 /**
2317  * xmlStreamWantsAnyNode:
2318  * @streamCtxt: the stream context
2319  *
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.
2324  *
2325  * Returns: 1 in case of need of nodes of the above described types,
2326  *          0 otherwise. -1 on API errors.
2327  */
2328 int
2329 xmlStreamWantsAnyNode(xmlStreamCtxtPtr streamCtxt)
2330 {
2331     if (streamCtxt == NULL)
2332         return(-1);
2333     while (streamCtxt != NULL) {
2334         if (streamCtxt->comp->flags & XML_STREAM_FINAL_IS_ANY_NODE)
2335             return(1);
2336         streamCtxt = streamCtxt->next;
2337     }
2338     return(0);
2339 }
2340
2341 /************************************************************************
2342  *                                                                      *
2343  *                      The public interfaces                           *
2344  *                                                                      *
2345  ************************************************************************/
2346
2347 /**
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
2353  *
2354  * Compile a pattern.
2355  *
2356  * Returns the compiled form of the pattern or NULL in case of error
2357  */
2358 xmlPatternPtr
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;
2365     int type = 0;
2366     int streamable = 1;
2367
2368     if (pattern == NULL)
2369         return(NULL);
2370
2371     start = pattern;
2372     or = start;
2373     while (*or != 0) {
2374         tmp = NULL;
2375         while ((*or != 0) && (*or != '|')) or++;
2376         if (*or == 0)
2377             ctxt = xmlNewPatParserContext(start, dict, namespaces);
2378         else {
2379             tmp = xmlStrndup(start, or - start);
2380             if (tmp != NULL) {
2381                 ctxt = xmlNewPatParserContext(tmp, dict, namespaces);
2382             }
2383             or++;
2384         }
2385         if (ctxt == NULL) goto error;
2386         cur = xmlNewPattern();
2387         if (cur == NULL) goto error;
2388         /*
2389         * Assign string dict.
2390         */
2391         if (dict) {
2392             cur->dict = dict;
2393             xmlDictReference(dict);
2394         }
2395         if (ret == NULL)
2396             ret = cur;
2397         else {
2398             cur->next = ret->next;
2399             ret->next = cur;
2400         }
2401         cur->flags = flags;
2402         ctxt->comp = cur;
2403
2404         if (XML_STREAM_XS_IDC(cur))
2405             xmlCompileIDCXPathPath(ctxt);
2406         else
2407             xmlCompilePathPattern(ctxt);
2408         if (ctxt->error != 0)
2409             goto error;
2410         xmlFreePatParserContext(ctxt);
2411         ctxt = NULL;
2412
2413
2414         if (streamable) {
2415             if (type == 0) {
2416                 type = cur->flags & (PAT_FROM_ROOT | PAT_FROM_CUR);
2417             } else if (type == PAT_FROM_ROOT) {
2418                 if (cur->flags & PAT_FROM_CUR)
2419                     streamable = 0;
2420             } else if (type == PAT_FROM_CUR) {
2421                 if (cur->flags & PAT_FROM_ROOT)
2422                     streamable = 0;
2423             }
2424         }
2425         if (streamable)
2426             xmlStreamCompile(cur);
2427         if (xmlReversePattern(cur) < 0)
2428             goto error;
2429         if (tmp != NULL) {
2430             xmlFree(tmp);
2431             tmp = NULL;
2432         }
2433         start = or;
2434     }
2435     if (streamable == 0) {
2436         cur = ret;
2437         while (cur != NULL) {
2438             if (cur->stream != NULL) {
2439                 xmlFreeStreamComp(cur->stream);
2440                 cur->stream = NULL;
2441             }
2442             cur = cur->next;
2443         }
2444     }
2445
2446     return(ret);
2447 error:
2448     if (ctxt != NULL) xmlFreePatParserContext(ctxt);
2449     if (ret != NULL) xmlFreePattern(ret);
2450     if (tmp != NULL) xmlFree(tmp);
2451     return(NULL);
2452 }
2453
2454 /**
2455  * xmlPatternMatch:
2456  * @comp: the precompiled pattern
2457  * @node: a node
2458  *
2459  * Test whether the node matches the pattern
2460  *
2461  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
2462  */
2463 int
2464 xmlPatternMatch(xmlPatternPtr comp, xmlNodePtr node)
2465 {
2466     int ret = 0;
2467
2468     if ((comp == NULL) || (node == NULL))
2469         return(-1);
2470
2471     while (comp != NULL) {
2472         ret = xmlPatMatch(comp, node);
2473         if (ret != 0)
2474             return(ret);
2475         comp = comp->next;
2476     }
2477     return(ret);
2478 }
2479
2480 /**
2481  * xmlPatternGetStreamCtxt:
2482  * @comp: the precompiled pattern
2483  *
2484  * Get a streaming context for that pattern
2485  * Use xmlFreeStreamCtxt to free the context.
2486  *
2487  * Returns a pointer to the context or NULL in case of failure
2488  */
2489 xmlStreamCtxtPtr
2490 xmlPatternGetStreamCtxt(xmlPatternPtr comp)
2491 {
2492     xmlStreamCtxtPtr ret = NULL, cur;
2493
2494     if ((comp == NULL) || (comp->stream == NULL))
2495         return(NULL);
2496
2497     while (comp != NULL) {
2498         if (comp->stream == NULL)
2499             goto failed;
2500         cur = xmlNewStreamCtxt(comp->stream);
2501         if (cur == NULL)
2502             goto failed;
2503         if (ret == NULL)
2504             ret = cur;
2505         else {
2506             cur->next = ret->next;
2507             ret->next = cur;
2508         }
2509         cur->flags = comp->flags;
2510         comp = comp->next;
2511     }
2512     return(ret);
2513 failed:
2514     xmlFreeStreamCtxt(ret);
2515     return(NULL);
2516 }
2517
2518 /**
2519  * xmlPatternStreamable:
2520  * @comp: the precompiled pattern
2521  *
2522  * Check if the pattern is streamable i.e. xmlPatternGetStreamCtxt()
2523  * should work.
2524  *
2525  * Returns 1 if streamable, 0 if not and -1 in case of error.
2526  */
2527 int
2528 xmlPatternStreamable(xmlPatternPtr comp) {
2529     if (comp == NULL)
2530         return(-1);
2531     while (comp != NULL) {
2532         if (comp->stream == NULL)
2533             return(0);
2534         comp = comp->next;
2535     }
2536     return(1);
2537 }
2538
2539 /**
2540  * xmlPatternMaxDepth:
2541  * @comp: the precompiled pattern
2542  *
2543  * Check the maximum depth reachable by a pattern
2544  *
2545  * Returns -2 if no limit (using //), otherwise the depth,
2546  *         and -1 in case of error
2547  */
2548 int
2549 xmlPatternMaxDepth(xmlPatternPtr comp) {
2550     int ret = 0, i;
2551     if (comp == NULL)
2552         return(-1);
2553     while (comp != NULL) {
2554         if (comp->stream == NULL)
2555             return(-1);
2556         for (i = 0;i < comp->stream->nbStep;i++)
2557             if (comp->stream->steps[i].flags & XML_STREAM_STEP_DESC)
2558                 return(-2);
2559         if (comp->stream->nbStep > ret)
2560             ret = comp->stream->nbStep;
2561         comp = comp->next;
2562     }
2563     return(ret);
2564 }
2565
2566 /**
2567  * xmlPatternMinDepth:
2568  * @comp: the precompiled pattern
2569  *
2570  * Check the minimum depth reachable by a pattern, 0 mean the / or . are
2571  * part of the set.
2572  *
2573  * Returns -1 in case of error otherwise the depth,
2574  *
2575  */
2576 int
2577 xmlPatternMinDepth(xmlPatternPtr comp) {
2578     int ret = 12345678;
2579     if (comp == NULL)
2580         return(-1);
2581     while (comp != NULL) {
2582         if (comp->stream == NULL)
2583             return(-1);
2584         if (comp->stream->nbStep < ret)
2585             ret = comp->stream->nbStep;
2586         if (ret == 0)
2587             return(0);
2588         comp = comp->next;
2589     }
2590     return(ret);
2591 }
2592
2593 /**
2594  * xmlPatternFromRoot:
2595  * @comp: the precompiled pattern
2596  *
2597  * Check if the pattern must be looked at from the root.
2598  *
2599  * Returns 1 if true, 0 if false and -1 in case of error
2600  */
2601 int
2602 xmlPatternFromRoot(xmlPatternPtr comp) {
2603     if (comp == NULL)
2604         return(-1);
2605     while (comp != NULL) {
2606         if (comp->stream == NULL)
2607             return(-1);
2608         if (comp->flags & PAT_FROM_ROOT)
2609             return(1);
2610         comp = comp->next;
2611     }
2612     return(0);
2613
2614 }
2615
2616 #define bottom_pattern
2617 #include "elfgcchack.h"
2618 #endif /* LIBXML_PATTERN_ENABLED */