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