xref: /reactos/sdk/lib/3rdparty/libxml2/pattern.c (revision 4561998a)
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 */
2619