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