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