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