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