xref: /reactos/dll/3rdparty/libxslt/pattern.c (revision 12e94103)
1 /*
2  * pattern.c: Implemetation of the template match compilation and lookup
3  *
4  * Reference:
5  *   http://www.w3.org/TR/1999/REC-xslt-19991116
6  *
7  * See Copyright for the status of this software.
8  *
9  * daniel@veillard.com
10  */
11 
12 /*
13  * TODO: handle pathological cases like *[*[@a="b"]]
14  * TODO: detect [number] at compilation, optimize accordingly
15  */
16 
17 #include "precomp.h"
18 
19 #ifdef WITH_XSLT_DEBUG
20 #define WITH_XSLT_DEBUG_PATTERN
21 #endif
22 
23 /*
24  * Types are private:
25  */
26 
27 typedef enum {
28     XSLT_OP_END=0,
29     XSLT_OP_ROOT,
30     XSLT_OP_ELEM,
31     XSLT_OP_ATTR,
32     XSLT_OP_PARENT,
33     XSLT_OP_ANCESTOR,
34     XSLT_OP_ID,
35     XSLT_OP_KEY,
36     XSLT_OP_NS,
37     XSLT_OP_ALL,
38     XSLT_OP_PI,
39     XSLT_OP_COMMENT,
40     XSLT_OP_TEXT,
41     XSLT_OP_NODE,
42     XSLT_OP_PREDICATE
43 } xsltOp;
44 
45 typedef enum {
46     AXIS_CHILD=1,
47     AXIS_ATTRIBUTE
48 } xsltAxis;
49 
50 typedef struct _xsltStepState xsltStepState;
51 typedef xsltStepState *xsltStepStatePtr;
52 struct _xsltStepState {
53     int step;
54     xmlNodePtr node;
55 };
56 
57 typedef struct _xsltStepStates xsltStepStates;
58 typedef xsltStepStates *xsltStepStatesPtr;
59 struct _xsltStepStates {
60     int nbstates;
61     int maxstates;
62     xsltStepStatePtr states;
63 };
64 
65 typedef struct _xsltStepOp xsltStepOp;
66 typedef xsltStepOp *xsltStepOpPtr;
67 struct _xsltStepOp {
68     xsltOp op;
69     xmlChar *value;
70     xmlChar *value2;
71     xmlChar *value3;
72     xmlXPathCompExprPtr comp;
73     /*
74      * Optimisations for count
75      */
76     int        previousExtra;
77     int        indexExtra;
78     int        lenExtra;
79 };
80 
81 struct _xsltCompMatch {
82     struct _xsltCompMatch *next; /* siblings in the name hash */
83     float priority;              /* the priority */
84     const xmlChar *pattern;       /* the pattern */
85     const xmlChar *mode;         /* the mode */
86     const xmlChar *modeURI;      /* the mode URI */
87     xsltTemplatePtr template;    /* the associated template */
88     xmlNodePtr node;             /* the containing element */
89 
90     int direct;
91     /* TODO fix the statically allocated size steps[] */
92     int nbStep;
93     int maxStep;
94     xmlNsPtr *nsList;		/* the namespaces in scope */
95     int nsNr;			/* the number of namespaces in scope */
96     xsltStepOpPtr steps;        /* ops for computation */
97 };
98 
99 typedef struct _xsltParserContext xsltParserContext;
100 typedef xsltParserContext *xsltParserContextPtr;
101 struct _xsltParserContext {
102     xsltStylesheetPtr style;		/* the stylesheet */
103     xsltTransformContextPtr ctxt;	/* the transformation or NULL */
104     const xmlChar *cur;			/* the current char being parsed */
105     const xmlChar *base;		/* the full expression */
106     xmlDocPtr      doc;			/* the source document */
107     xmlNodePtr    elem;			/* the source element */
108     int error;				/* error code */
109     xsltCompMatchPtr comp;		/* the result */
110 };
111 
112 /************************************************************************
113  *									*
114  *			Type functions					*
115  *									*
116  ************************************************************************/
117 
118 /**
119  * xsltNewCompMatch:
120  *
121  * Create a new XSLT CompMatch
122  *
123  * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
124  */
125 static xsltCompMatchPtr
126 xsltNewCompMatch(void) {
127     xsltCompMatchPtr cur;
128 
129     cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
130     if (cur == NULL) {
131 	xsltTransformError(NULL, NULL, NULL,
132 		"xsltNewCompMatch : out of memory error\n");
133 	return(NULL);
134     }
135     memset(cur, 0, sizeof(xsltCompMatch));
136     cur->maxStep = 10;
137     cur->nbStep = 0;
138     cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) *
139                                             cur->maxStep);
140     if (cur->steps == NULL) {
141 	xsltTransformError(NULL, NULL, NULL,
142 		"xsltNewCompMatch : out of memory error\n");
143 	xmlFree(cur);
144 	return(NULL);
145     }
146     cur->nsNr = 0;
147     cur->nsList = NULL;
148     cur->direct = 0;
149     return(cur);
150 }
151 
152 /**
153  * xsltFreeCompMatch:
154  * @comp:  an XSLT comp
155  *
156  * Free up the memory allocated by @comp
157  */
158 static void
159 xsltFreeCompMatch(xsltCompMatchPtr comp) {
160     xsltStepOpPtr op;
161     int i;
162 
163     if (comp == NULL)
164 	return;
165     if (comp->pattern != NULL)
166 	xmlFree((xmlChar *)comp->pattern);
167     if (comp->nsList != NULL)
168 	xmlFree(comp->nsList);
169     for (i = 0;i < comp->nbStep;i++) {
170 	op = &comp->steps[i];
171 	if (op->value != NULL)
172 	    xmlFree(op->value);
173 	if (op->value2 != NULL)
174 	    xmlFree(op->value2);
175 	if (op->value3 != NULL)
176 	    xmlFree(op->value3);
177 	if (op->comp != NULL)
178 	    xmlXPathFreeCompExpr(op->comp);
179     }
180     xmlFree(comp->steps);
181     memset(comp, -1, sizeof(xsltCompMatch));
182     xmlFree(comp);
183 }
184 
185 /**
186  * xsltFreeCompMatchList:
187  * @comp:  an XSLT comp list
188  *
189  * Free up the memory allocated by all the elements of @comp
190  */
191 void
192 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
193     xsltCompMatchPtr cur;
194 
195     while (comp != NULL) {
196 	cur = comp;
197 	comp = comp->next;
198 	xsltFreeCompMatch(cur);
199     }
200 }
201 
202 /**
203  * xsltNormalizeCompSteps:
204  * @payload: pointer to template hash table entry
205  * @data: pointer to the stylesheet
206  * @name: template match name
207  *
208  * This is a hashtable scanner function to normalize the compiled
209  * steps of an imported stylesheet.
210  */
211 void xsltNormalizeCompSteps(void *payload,
212         void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
213     xsltCompMatchPtr comp = payload;
214     xsltStylesheetPtr style = data;
215     int ix;
216 
217     for (ix = 0; ix < comp->nbStep; ix++) {
218         comp->steps[ix].previousExtra += style->extrasNr;
219         comp->steps[ix].indexExtra += style->extrasNr;
220         comp->steps[ix].lenExtra += style->extrasNr;
221     }
222 }
223 
224 /**
225  * xsltNewParserContext:
226  * @style:  the stylesheet
227  * @ctxt:  the transformation context, if done at run-time
228  *
229  * Create a new XSLT ParserContext
230  *
231  * Returns the newly allocated xsltParserContextPtr or NULL in case of error
232  */
233 static xsltParserContextPtr
234 xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
235     xsltParserContextPtr cur;
236 
237     cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
238     if (cur == NULL) {
239 	xsltTransformError(NULL, NULL, NULL,
240 		"xsltNewParserContext : malloc failed\n");
241 	return(NULL);
242     }
243     memset(cur, 0, sizeof(xsltParserContext));
244     cur->style = style;
245     cur->ctxt = ctxt;
246     return(cur);
247 }
248 
249 /**
250  * xsltFreeParserContext:
251  * @ctxt:  an XSLT parser context
252  *
253  * Free up the memory allocated by @ctxt
254  */
255 static void
256 xsltFreeParserContext(xsltParserContextPtr ctxt) {
257     if (ctxt == NULL)
258 	return;
259     memset(ctxt, -1, sizeof(xsltParserContext));
260     xmlFree(ctxt);
261 }
262 
263 /**
264  * xsltCompMatchAdd:
265  * @comp:  the compiled match expression
266  * @op:  an op
267  * @value:  the first value
268  * @value2:  the second value
269  * @novar:  flag to set XML_XPATH_NOVAR
270  *
271  * Add an step to an XSLT Compiled Match
272  *
273  * Returns -1 in case of failure, 0 otherwise.
274  */
275 static int
276 xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
277                  xsltOp op, xmlChar * value, xmlChar * value2, int novar)
278 {
279     if (comp->nbStep >= comp->maxStep) {
280         xsltStepOpPtr tmp;
281 
282 	tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
283 	                                 sizeof(xsltStepOp));
284 	if (tmp == NULL) {
285 	    xsltGenericError(xsltGenericErrorContext,
286 	     "xsltCompMatchAdd: memory re-allocation failure.\n");
287 	    if (ctxt->style != NULL)
288 		ctxt->style->errors++;
289 	    if (value)
290 	        xmlFree(value);
291 	    if (value2)
292 	        xmlFree(value2);
293 	    return (-1);
294 	}
295         comp->maxStep *= 2;
296 	comp->steps = tmp;
297     }
298     comp->steps[comp->nbStep].op = op;
299     comp->steps[comp->nbStep].value = value;
300     comp->steps[comp->nbStep].value2 = value2;
301     comp->steps[comp->nbStep].value3 = NULL;
302     comp->steps[comp->nbStep].comp = NULL;
303     if (ctxt->ctxt != NULL) {
304 	comp->steps[comp->nbStep].previousExtra =
305 	    xsltAllocateExtraCtxt(ctxt->ctxt);
306 	comp->steps[comp->nbStep].indexExtra =
307 	    xsltAllocateExtraCtxt(ctxt->ctxt);
308 	comp->steps[comp->nbStep].lenExtra =
309 	    xsltAllocateExtraCtxt(ctxt->ctxt);
310     } else {
311 	comp->steps[comp->nbStep].previousExtra =
312 	    xsltAllocateExtra(ctxt->style);
313 	comp->steps[comp->nbStep].indexExtra =
314 	    xsltAllocateExtra(ctxt->style);
315 	comp->steps[comp->nbStep].lenExtra =
316 	    xsltAllocateExtra(ctxt->style);
317     }
318     if (op == XSLT_OP_PREDICATE) {
319 	xmlXPathContextPtr xctxt;
320 
321 	if (ctxt->style != NULL)
322 	    xctxt = xmlXPathNewContext(ctxt->style->doc);
323 	else
324 	    xctxt = xmlXPathNewContext(NULL);
325 #ifdef XML_XPATH_NOVAR
326 	if (novar != 0)
327 	    xctxt->flags = XML_XPATH_NOVAR;
328 #endif
329 	if (ctxt->style != NULL)
330 	    xctxt->dict = ctxt->style->dict;
331 	comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value);
332 	xmlXPathFreeContext(xctxt);
333 	if (comp->steps[comp->nbStep].comp == NULL) {
334 	    xsltTransformError(NULL, ctxt->style, ctxt->elem,
335 		    "Failed to compile predicate\n");
336 	    if (ctxt->style != NULL)
337 		ctxt->style->errors++;
338 	}
339     }
340     comp->nbStep++;
341     return (0);
342 }
343 
344 /**
345  * xsltSwapTopCompMatch:
346  * @comp:  the compiled match expression
347  *
348  * reverse the two top steps.
349  */
350 static void
351 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
352     int i;
353     int j = comp->nbStep - 1;
354 
355     if (j > 0) {
356 	register xmlChar *tmp;
357 	register xsltOp op;
358 	register xmlXPathCompExprPtr expr;
359 	register int t;
360 	i = j - 1;
361 	tmp = comp->steps[i].value;
362 	comp->steps[i].value = comp->steps[j].value;
363 	comp->steps[j].value = tmp;
364 	tmp = comp->steps[i].value2;
365 	comp->steps[i].value2 = comp->steps[j].value2;
366 	comp->steps[j].value2 = tmp;
367 	tmp = comp->steps[i].value3;
368 	comp->steps[i].value3 = comp->steps[j].value3;
369 	comp->steps[j].value3 = tmp;
370 	op = comp->steps[i].op;
371 	comp->steps[i].op = comp->steps[j].op;
372 	comp->steps[j].op = op;
373 	expr = comp->steps[i].comp;
374 	comp->steps[i].comp = comp->steps[j].comp;
375 	comp->steps[j].comp = expr;
376 	t = comp->steps[i].previousExtra;
377 	comp->steps[i].previousExtra = comp->steps[j].previousExtra;
378 	comp->steps[j].previousExtra = t;
379 	t = comp->steps[i].indexExtra;
380 	comp->steps[i].indexExtra = comp->steps[j].indexExtra;
381 	comp->steps[j].indexExtra = t;
382 	t = comp->steps[i].lenExtra;
383 	comp->steps[i].lenExtra = comp->steps[j].lenExtra;
384 	comp->steps[j].lenExtra = t;
385     }
386 }
387 
388 /**
389  * xsltReverseCompMatch:
390  * @ctxt: the parser context
391  * @comp:  the compiled match expression
392  *
393  * reverse all the stack of expressions
394  */
395 static void
396 xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) {
397     int i = 0;
398     int j = comp->nbStep - 1;
399 
400     while (j > i) {
401 	register xmlChar *tmp;
402 	register xsltOp op;
403 	register xmlXPathCompExprPtr expr;
404 	register int t;
405 
406 	tmp = comp->steps[i].value;
407 	comp->steps[i].value = comp->steps[j].value;
408 	comp->steps[j].value = tmp;
409 	tmp = comp->steps[i].value2;
410 	comp->steps[i].value2 = comp->steps[j].value2;
411 	comp->steps[j].value2 = tmp;
412 	tmp = comp->steps[i].value3;
413 	comp->steps[i].value3 = comp->steps[j].value3;
414 	comp->steps[j].value3 = tmp;
415 	op = comp->steps[i].op;
416 	comp->steps[i].op = comp->steps[j].op;
417 	comp->steps[j].op = op;
418 	expr = comp->steps[i].comp;
419 	comp->steps[i].comp = comp->steps[j].comp;
420 	comp->steps[j].comp = expr;
421 	t = comp->steps[i].previousExtra;
422 	comp->steps[i].previousExtra = comp->steps[j].previousExtra;
423 	comp->steps[j].previousExtra = t;
424 	t = comp->steps[i].indexExtra;
425 	comp->steps[i].indexExtra = comp->steps[j].indexExtra;
426 	comp->steps[j].indexExtra = t;
427 	t = comp->steps[i].lenExtra;
428 	comp->steps[i].lenExtra = comp->steps[j].lenExtra;
429 	comp->steps[j].lenExtra = t;
430 	j--;
431 	i++;
432     }
433     xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0);
434 
435     /*
436      * Detect consecutive XSLT_OP_PREDICATE and predicates on ops which
437      * haven't been optimized yet indicating a direct matching should be done.
438      */
439     for (i = 0;i < comp->nbStep - 1;i++) {
440         xsltOp op = comp->steps[i].op;
441 
442         if ((op != XSLT_OP_ELEM) &&
443             (op != XSLT_OP_ALL) &&
444 	    (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
445 
446 	    comp->direct = 1;
447 	    if (comp->pattern[0] != '/') {
448 		xmlChar *query;
449 
450 		query = xmlStrdup((const xmlChar *)"//");
451 		query = xmlStrcat(query, comp->pattern);
452 
453 		xmlFree((xmlChar *) comp->pattern);
454 		comp->pattern = query;
455 	    }
456 	    break;
457 	}
458     }
459 }
460 
461 /************************************************************************
462  *									*
463  *		The interpreter for the precompiled patterns		*
464  *									*
465  ************************************************************************/
466 
467 static int
468 xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
469                  int step, xmlNodePtr node) {
470     if ((states->states == NULL) || (states->maxstates <= 0)) {
471         states->maxstates = 4;
472 	states->nbstates = 0;
473 	states->states = xmlMalloc(4 * sizeof(xsltStepState));
474     }
475     else if (states->maxstates <= states->nbstates) {
476         xsltStepState *tmp;
477 
478 	tmp = (xsltStepStatePtr) xmlRealloc(states->states,
479 			       2 * states->maxstates * sizeof(xsltStepState));
480 	if (tmp == NULL) {
481 	    xsltGenericError(xsltGenericErrorContext,
482 	     "xsltPatPushState: memory re-allocation failure.\n");
483 	    ctxt->state = XSLT_STATE_STOPPED;
484 	    return(-1);
485 	}
486 	states->states = tmp;
487 	states->maxstates *= 2;
488     }
489     states->states[states->nbstates].step = step;
490     states->states[states->nbstates++].node = node;
491 #if 0
492     fprintf(stderr, "Push: %d, %s\n", step, node->name);
493 #endif
494     return(0);
495 }
496 
497 /**
498  * xsltTestCompMatchDirect:
499  * @ctxt:  a XSLT process context
500  * @comp: the precompiled pattern
501  * @node: a node
502  * @nsList: the namespaces in scope
503  * @nsNr: the number of namespaces in scope
504  *
505  * Test whether the node matches the pattern, do a direct evalutation
506  * and not a step by step evaluation.
507  *
508  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
509  */
510 static int
511 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
512 	                xmlNodePtr node, xmlNsPtr *nsList, int nsNr) {
513     xsltStepOpPtr sel = NULL;
514     xmlDocPtr prevdoc;
515     xmlDocPtr doc;
516     xmlXPathObjectPtr list;
517     int ix, j;
518     int nocache = 0;
519     int isRVT;
520 
521     doc = node->doc;
522     if (XSLT_IS_RES_TREE_FRAG(doc))
523 	isRVT = 1;
524     else
525 	isRVT = 0;
526     sel = &comp->steps[0]; /* store extra in first step arbitrarily */
527 
528     prevdoc = (xmlDocPtr)
529 	XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
530     ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
531     list = (xmlXPathObjectPtr)
532 	XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
533 
534     if ((list == NULL) || (prevdoc != doc)) {
535 	xmlXPathObjectPtr newlist;
536 	xmlNodePtr parent = node->parent;
537 	xmlDocPtr olddoc;
538 	xmlNodePtr oldnode;
539 	int oldNsNr, oldContextSize, oldProximityPosition;
540 	xmlNsPtr *oldNamespaces;
541 
542 	oldnode = ctxt->xpathCtxt->node;
543 	olddoc = ctxt->xpathCtxt->doc;
544 	oldNsNr = ctxt->xpathCtxt->nsNr;
545 	oldNamespaces = ctxt->xpathCtxt->namespaces;
546 	oldContextSize = ctxt->xpathCtxt->contextSize;
547 	oldProximityPosition = ctxt->xpathCtxt->proximityPosition;
548 	ctxt->xpathCtxt->node = node;
549 	ctxt->xpathCtxt->doc = doc;
550 	ctxt->xpathCtxt->namespaces = nsList;
551 	ctxt->xpathCtxt->nsNr = nsNr;
552 	newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
553 	ctxt->xpathCtxt->node = oldnode;
554 	ctxt->xpathCtxt->doc = olddoc;
555 	ctxt->xpathCtxt->namespaces = oldNamespaces;
556 	ctxt->xpathCtxt->nsNr = oldNsNr;
557 	ctxt->xpathCtxt->contextSize = oldContextSize;
558 	ctxt->xpathCtxt->proximityPosition = oldProximityPosition;
559 	if (newlist == NULL)
560 	    return(-1);
561 	if (newlist->type != XPATH_NODESET) {
562 	    xmlXPathFreeObject(newlist);
563 	    return(-1);
564 	}
565 	ix = 0;
566 
567 	if ((parent == NULL) || (node->doc == NULL) || isRVT)
568 	    nocache = 1;
569 
570 	if (nocache == 0) {
571 	    if (list != NULL)
572 		xmlXPathFreeObject(list);
573 	    list = newlist;
574 
575 	    XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
576 		(void *) list;
577 	    XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
578 		(void *) doc;
579 	    XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
580 		0;
581 	    XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
582 		(xmlFreeFunc) xmlXPathFreeObject;
583 	} else
584 	    list = newlist;
585     }
586     if ((list->nodesetval == NULL) ||
587 	(list->nodesetval->nodeNr <= 0)) {
588 	if (nocache == 1)
589 	    xmlXPathFreeObject(list);
590 	return(0);
591     }
592     /* TODO: store the index and use it for the scan */
593     if (ix == 0) {
594 	for (j = 0;j < list->nodesetval->nodeNr;j++) {
595 	    if (list->nodesetval->nodeTab[j] == node) {
596 		if (nocache == 1)
597 		    xmlXPathFreeObject(list);
598 		return(1);
599 	    }
600 	}
601     } else {
602     }
603     if (nocache == 1)
604 	xmlXPathFreeObject(list);
605     return(0);
606 }
607 
608 /**
609  * xsltTestPredicateMatch:
610  * @ctxt: a XSLT process context
611  * @comp: the precompiled pattern
612  * @node: a node
613  * @step: the predicate step
614  * @sel:  the previous step
615  *
616  * Test whether the node matches the predicate
617  *
618  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
619  */
620 static int
621 xsltTestPredicateMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
622                        xmlNodePtr node, xsltStepOpPtr step,
623                        xsltStepOpPtr sel) {
624     xmlNodePtr oldNode;
625     xmlDocPtr doc;
626     int oldCS, oldCP;
627     int pos = 0, len = 0;
628     int isRVT;
629     int match;
630 
631     if (step->value == NULL)
632         return(0);
633     if (step->comp == NULL)
634         return(0);
635 
636     doc = node->doc;
637     if (XSLT_IS_RES_TREE_FRAG(doc))
638         isRVT = 1;
639     else
640         isRVT = 0;
641 
642     /*
643      * Recompute contextSize and proximityPosition.
644      *
645      * TODO: Make this work for additional ops. Currently, only XSLT_OP_ELEM
646      * and XSLT_OP_ALL are supported.
647      */
648     oldCS = ctxt->xpathCtxt->contextSize;
649     oldCP = ctxt->xpathCtxt->proximityPosition;
650     if ((sel != NULL) &&
651         (sel->op == XSLT_OP_ELEM) &&
652         (sel->value != NULL) &&
653         (node->type == XML_ELEMENT_NODE) &&
654         (node->parent != NULL)) {
655         xmlNodePtr previous;
656         int nocache = 0;
657 
658         previous = (xmlNodePtr)
659             XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
660         if ((previous != NULL) &&
661             (previous->parent == node->parent)) {
662             /*
663              * just walk back to adjust the index
664              */
665             int indx = 0;
666             xmlNodePtr sibling = node;
667 
668             while (sibling != NULL) {
669                 if (sibling == previous)
670                     break;
671                 if ((sibling->type == XML_ELEMENT_NODE) &&
672                     (previous->name != NULL) &&
673                     (sibling->name != NULL) &&
674                     (previous->name[0] == sibling->name[0]) &&
675                     (xmlStrEqual(previous->name, sibling->name)))
676                 {
677                     if ((sel->value2 == NULL) ||
678                         ((sibling->ns != NULL) &&
679                          (xmlStrEqual(sel->value2, sibling->ns->href))))
680                         indx++;
681                 }
682                 sibling = sibling->prev;
683             }
684             if (sibling == NULL) {
685                 /* hum going backward in document order ... */
686                 indx = 0;
687                 sibling = node;
688                 while (sibling != NULL) {
689                     if (sibling == previous)
690                         break;
691                     if ((sibling->type == XML_ELEMENT_NODE) &&
692                         (previous->name != NULL) &&
693                         (sibling->name != NULL) &&
694                         (previous->name[0] == sibling->name[0]) &&
695                         (xmlStrEqual(previous->name, sibling->name)))
696                     {
697                         if ((sel->value2 == NULL) ||
698                             ((sibling->ns != NULL) &&
699                             (xmlStrEqual(sel->value2,
700                             sibling->ns->href))))
701                         {
702                             indx--;
703                         }
704                     }
705                     sibling = sibling->next;
706                 }
707             }
708             if (sibling != NULL) {
709                 pos = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) + indx;
710                 /*
711                  * If the node is in a Value Tree we need to
712                  * save len, but cannot cache the node!
713                  * (bugs 153137 and 158840)
714                  */
715                 if (node->doc != NULL) {
716                     len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival);
717                     if (!isRVT) {
718                         XSLT_RUNTIME_EXTRA(ctxt,
719                             sel->previousExtra, ptr) = node;
720                         XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
721                     }
722                 }
723             } else
724                 pos = 0;
725         } else {
726             /*
727              * recompute the index
728              */
729             xmlNodePtr parent = node->parent;
730             xmlNodePtr siblings = NULL;
731 
732             if (parent) siblings = parent->children;
733 
734             while (siblings != NULL) {
735                 if (siblings->type == XML_ELEMENT_NODE) {
736                     if (siblings == node) {
737                         len++;
738                         pos = len;
739                     } else if ((node->name != NULL) &&
740                                (siblings->name != NULL) &&
741                         (node->name[0] == siblings->name[0]) &&
742                         (xmlStrEqual(node->name, siblings->name))) {
743                         if ((sel->value2 == NULL) ||
744                             ((siblings->ns != NULL) &&
745                              (xmlStrEqual(sel->value2, siblings->ns->href))))
746                             len++;
747                     }
748                 }
749                 siblings = siblings->next;
750             }
751             if ((parent == NULL) || (node->doc == NULL))
752                 nocache = 1;
753             else {
754                 while (parent->parent != NULL)
755                     parent = parent->parent;
756                 if (((parent->type != XML_DOCUMENT_NODE) &&
757                      (parent->type != XML_HTML_DOCUMENT_NODE)) ||
758                      (parent != (xmlNodePtr) node->doc))
759                     nocache = 1;
760             }
761         }
762         if (pos != 0) {
763             ctxt->xpathCtxt->contextSize = len;
764             ctxt->xpathCtxt->proximityPosition = pos;
765             /*
766              * If the node is in a Value Tree we cannot
767              * cache it !
768              */
769             if ((!isRVT) && (node->doc != NULL) &&
770                 (nocache == 0)) {
771                 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
772                 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
773                 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len;
774             }
775         }
776     } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
777                (node->type == XML_ELEMENT_NODE)) {
778         xmlNodePtr previous;
779         int nocache = 0;
780 
781         previous = (xmlNodePtr)
782             XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
783         if ((previous != NULL) &&
784             (previous->parent == node->parent)) {
785             /*
786              * just walk back to adjust the index
787              */
788             int indx = 0;
789             xmlNodePtr sibling = node;
790 
791             while (sibling != NULL) {
792                 if (sibling == previous)
793                     break;
794                 if (sibling->type == XML_ELEMENT_NODE)
795                     indx++;
796                 sibling = sibling->prev;
797             }
798             if (sibling == NULL) {
799                 /* hum going backward in document order ... */
800                 indx = 0;
801                 sibling = node;
802                 while (sibling != NULL) {
803                     if (sibling == previous)
804                         break;
805                     if (sibling->type == XML_ELEMENT_NODE)
806                         indx--;
807                     sibling = sibling->next;
808                 }
809             }
810             if (sibling != NULL) {
811                 pos = XSLT_RUNTIME_EXTRA(ctxt,
812                     sel->indexExtra, ival) + indx;
813                 /*
814                  * If the node is in a Value Tree we cannot
815                  * cache it !
816                  */
817                 if ((node->doc != NULL) && !isRVT) {
818                     len = XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival);
819                     XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
820                     XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
821                 }
822             } else
823                 pos = 0;
824         } else {
825             /*
826              * recompute the index
827              */
828             xmlNodePtr parent = node->parent;
829             xmlNodePtr siblings = NULL;
830 
831             if (parent) siblings = parent->children;
832 
833             while (siblings != NULL) {
834                 if (siblings->type == XML_ELEMENT_NODE) {
835                     len++;
836                     if (siblings == node) {
837                         pos = len;
838                     }
839                 }
840                 siblings = siblings->next;
841             }
842             if ((parent == NULL) || (node->doc == NULL))
843                 nocache = 1;
844             else {
845                 while (parent->parent != NULL)
846                     parent = parent->parent;
847                 if (((parent->type != XML_DOCUMENT_NODE) &&
848                      (parent->type != XML_HTML_DOCUMENT_NODE)) ||
849                      (parent != (xmlNodePtr) node->doc))
850                     nocache = 1;
851             }
852         }
853         if (pos != 0) {
854             ctxt->xpathCtxt->contextSize = len;
855             ctxt->xpathCtxt->proximityPosition = pos;
856             /*
857              * If the node is in a Value Tree we cannot
858              * cache it !
859              */
860             if ((node->doc != NULL) && (nocache == 0) && !isRVT) {
861                 XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) = node;
862                 XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) = pos;
863                 XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) = len;
864             }
865         }
866     }
867 
868     oldNode = ctxt->node;
869     ctxt->node = node;
870 
871     match = xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList, comp->nsNr);
872 
873     if (pos != 0) {
874         ctxt->xpathCtxt->contextSize = oldCS;
875         ctxt->xpathCtxt->proximityPosition = oldCP;
876     }
877     ctxt->node = oldNode;
878 
879     return match;
880 }
881 
882 /**
883  * xsltTestCompMatch:
884  * @ctxt:  a XSLT process context
885  * @comp: the precompiled pattern
886  * @node: a node
887  * @mode:  the mode name or NULL
888  * @modeURI:  the mode URI or NULL
889  *
890  * Test whether the node matches the pattern
891  *
892  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
893  */
894 static int
895 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
896 	          xmlNodePtr matchNode, const xmlChar *mode,
897 		  const xmlChar *modeURI) {
898     int i;
899     int found = 0;
900     xmlNodePtr node = matchNode;
901     xmlNodePtr oldInst;
902     xsltStepOpPtr step, sel = NULL;
903     xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
904 
905     if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
906 	xsltTransformError(ctxt, NULL, node,
907 		"xsltTestCompMatch: null arg\n");
908         return(-1);
909     }
910     if (mode != NULL) {
911 	if (comp->mode == NULL)
912 	    return(0);
913 	/*
914 	 * both mode strings must be interned on the stylesheet dictionary
915 	 */
916 	if (comp->mode != mode)
917 	    return(0);
918     } else {
919 	if (comp->mode != NULL)
920 	    return(0);
921     }
922     if (modeURI != NULL) {
923 	if (comp->modeURI == NULL)
924 	    return(0);
925 	/*
926 	 * both modeURI strings must be interned on the stylesheet dictionary
927 	 */
928 	if (comp->modeURI != modeURI)
929 	    return(0);
930     } else {
931 	if (comp->modeURI != NULL)
932 	    return(0);
933     }
934 
935     /* Some XPath functions rely on inst being set correctly. */
936     oldInst = ctxt->inst;
937     ctxt->inst = comp->node;
938 
939     i = 0;
940 restart:
941     for (;i < comp->nbStep;i++) {
942 	step = &comp->steps[i];
943 	if (step->op != XSLT_OP_PREDICATE)
944 	    sel = step;
945 	switch (step->op) {
946             case XSLT_OP_END:
947 		goto found;
948             case XSLT_OP_ROOT:
949 		if ((node->type == XML_DOCUMENT_NODE) ||
950 #ifdef LIBXML_DOCB_ENABLED
951 		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
952 #endif
953 		    (node->type == XML_HTML_DOCUMENT_NODE))
954 		    continue;
955 		if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
956 		    continue;
957 		goto rollback;
958             case XSLT_OP_ELEM:
959 		if (node->type != XML_ELEMENT_NODE)
960 		    goto rollback;
961 		if (step->value == NULL)
962 		    continue;
963 		if (step->value[0] != node->name[0])
964 		    goto rollback;
965 		if (!xmlStrEqual(step->value, node->name))
966 		    goto rollback;
967 
968 		/* Namespace test */
969 		if (node->ns == NULL) {
970 		    if (step->value2 != NULL)
971 			goto rollback;
972 		} else if (node->ns->href != NULL) {
973 		    if (step->value2 == NULL)
974 			goto rollback;
975 		    if (!xmlStrEqual(step->value2, node->ns->href))
976 			goto rollback;
977 		}
978 		continue;
979             case XSLT_OP_ATTR:
980 		if (node->type != XML_ATTRIBUTE_NODE)
981 		    goto rollback;
982 		if (step->value != NULL) {
983 		    if (step->value[0] != node->name[0])
984 			goto rollback;
985 		    if (!xmlStrEqual(step->value, node->name))
986 			goto rollback;
987 		}
988 		/* Namespace test */
989 		if (node->ns == NULL) {
990 		    if (step->value2 != NULL)
991 			goto rollback;
992 		} else if (step->value2 != NULL) {
993 		    if (!xmlStrEqual(step->value2, node->ns->href))
994 			goto rollback;
995 		}
996 		continue;
997             case XSLT_OP_PARENT:
998 		if ((node->type == XML_DOCUMENT_NODE) ||
999 		    (node->type == XML_HTML_DOCUMENT_NODE) ||
1000 #ifdef LIBXML_DOCB_ENABLED
1001 		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
1002 #endif
1003 		    (node->type == XML_NAMESPACE_DECL))
1004 		    goto rollback;
1005 		node = node->parent;
1006 		if (node == NULL)
1007 		    goto rollback;
1008 		if (step->value == NULL)
1009 		    continue;
1010 		if (step->value[0] != node->name[0])
1011 		    goto rollback;
1012 		if (!xmlStrEqual(step->value, node->name))
1013 		    goto rollback;
1014 		/* Namespace test */
1015 		if (node->ns == NULL) {
1016 		    if (step->value2 != NULL)
1017 			goto rollback;
1018 		} else if (node->ns->href != NULL) {
1019 		    if (step->value2 == NULL)
1020 			goto rollback;
1021 		    if (!xmlStrEqual(step->value2, node->ns->href))
1022 			goto rollback;
1023 		}
1024 		continue;
1025             case XSLT_OP_ANCESTOR:
1026 		/* TODO: implement coalescing of ANCESTOR/NODE ops */
1027 		if (step->value == NULL) {
1028 		    step = &comp->steps[i+1];
1029 		    if (step->op == XSLT_OP_ROOT)
1030 			goto found;
1031 		    /* added NS, ID and KEY as a result of bug 168208 */
1032 		    if ((step->op != XSLT_OP_ELEM) &&
1033 			(step->op != XSLT_OP_ALL) &&
1034 			(step->op != XSLT_OP_NS) &&
1035 			(step->op != XSLT_OP_ID) &&
1036 			(step->op != XSLT_OP_KEY))
1037 			goto rollback;
1038 		}
1039 		if (node == NULL)
1040 		    goto rollback;
1041 		if ((node->type == XML_DOCUMENT_NODE) ||
1042 		    (node->type == XML_HTML_DOCUMENT_NODE) ||
1043 #ifdef LIBXML_DOCB_ENABLED
1044 		    (node->type == XML_DOCB_DOCUMENT_NODE) ||
1045 #endif
1046 		    (node->type == XML_NAMESPACE_DECL))
1047 		    goto rollback;
1048 		node = node->parent;
1049 		if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
1050 		    xsltPatPushState(ctxt, &states, i, node);
1051 		    continue;
1052 		}
1053 		i++;
1054 		if (step->value == NULL) {
1055 		    xsltPatPushState(ctxt, &states, i - 1, node);
1056 		    continue;
1057 		}
1058 		while (node != NULL) {
1059 		    if ((node->type == XML_ELEMENT_NODE) &&
1060 			(step->value[0] == node->name[0]) &&
1061 			(xmlStrEqual(step->value, node->name))) {
1062 			/* Namespace test */
1063 			if (node->ns == NULL) {
1064 			    if (step->value2 == NULL)
1065 				break;
1066 			} else if (node->ns->href != NULL) {
1067 			    if ((step->value2 != NULL) &&
1068 			        (xmlStrEqual(step->value2, node->ns->href)))
1069 				break;
1070 			}
1071 		    }
1072 		    node = node->parent;
1073 		}
1074 		if (node == NULL)
1075 		    goto rollback;
1076 		xsltPatPushState(ctxt, &states, i - 1, node);
1077 		continue;
1078             case XSLT_OP_ID: {
1079 		/* TODO Handle IDs decently, must be done differently */
1080 		xmlAttrPtr id;
1081 
1082 		if (node->type != XML_ELEMENT_NODE)
1083 		    goto rollback;
1084 
1085 		id = xmlGetID(node->doc, step->value);
1086 		if ((id == NULL) || (id->parent != node))
1087 		    goto rollback;
1088 		break;
1089 	    }
1090             case XSLT_OP_KEY: {
1091 		xmlNodeSetPtr list;
1092 		int indx;
1093 
1094 		list = xsltGetKey(ctxt, step->value,
1095 			          step->value3, step->value2);
1096 		if (list == NULL)
1097 		    goto rollback;
1098 		for (indx = 0;indx < list->nodeNr;indx++)
1099 		    if (list->nodeTab[indx] == node)
1100 			break;
1101 		if (indx >= list->nodeNr)
1102 		    goto rollback;
1103 		break;
1104 	    }
1105             case XSLT_OP_NS:
1106 		if (node->type != XML_ELEMENT_NODE)
1107 		    goto rollback;
1108 		if (node->ns == NULL) {
1109 		    if (step->value != NULL)
1110 			goto rollback;
1111 		} else if (node->ns->href != NULL) {
1112 		    if (step->value == NULL)
1113 			goto rollback;
1114 		    if (!xmlStrEqual(step->value, node->ns->href))
1115 			goto rollback;
1116 		}
1117 		break;
1118             case XSLT_OP_ALL:
1119 		if (node->type != XML_ELEMENT_NODE)
1120 		    goto rollback;
1121 		break;
1122 	    case XSLT_OP_PREDICATE: {
1123 		/*
1124 		 * When there is cascading XSLT_OP_PREDICATE or a predicate
1125 		 * after an op which hasn't been optimized yet, then use a
1126 		 * direct computation approach. It's not done directly
1127 		 * at the beginning of the routine to filter out as much
1128 		 * as possible this costly computation.
1129 		 */
1130 		if (comp->direct) {
1131 		    found = xsltTestCompMatchDirect(ctxt, comp, matchNode,
1132 						    comp->nsList, comp->nsNr);
1133                     goto exit;
1134 		}
1135 
1136 		if (!xsltTestPredicateMatch(ctxt, comp, node, step, sel))
1137 		    goto rollback;
1138 
1139 		break;
1140 	    }
1141             case XSLT_OP_PI:
1142 		if (node->type != XML_PI_NODE)
1143 		    goto rollback;
1144 		if (step->value != NULL) {
1145 		    if (!xmlStrEqual(step->value, node->name))
1146 			goto rollback;
1147 		}
1148 		break;
1149             case XSLT_OP_COMMENT:
1150 		if (node->type != XML_COMMENT_NODE)
1151 		    goto rollback;
1152 		break;
1153             case XSLT_OP_TEXT:
1154 		if ((node->type != XML_TEXT_NODE) &&
1155 		    (node->type != XML_CDATA_SECTION_NODE))
1156 		    goto rollback;
1157 		break;
1158             case XSLT_OP_NODE:
1159 		switch (node->type) {
1160 		    case XML_ELEMENT_NODE:
1161 		    case XML_CDATA_SECTION_NODE:
1162 		    case XML_PI_NODE:
1163 		    case XML_COMMENT_NODE:
1164 		    case XML_TEXT_NODE:
1165 			break;
1166 		    default:
1167 			goto rollback;
1168 		}
1169 		break;
1170 	}
1171     }
1172 found:
1173     found = 1;
1174 exit:
1175     ctxt->inst = oldInst;
1176     if (states.states != NULL) {
1177         /* Free the rollback states */
1178 	xmlFree(states.states);
1179     }
1180     return found;
1181 rollback:
1182     /* got an error try to rollback */
1183     if (states.states == NULL || states.nbstates <= 0) {
1184         found = 0;
1185 	goto exit;
1186     }
1187     states.nbstates--;
1188     i = states.states[states.nbstates].step;
1189     node = states.states[states.nbstates].node;
1190 #if 0
1191     fprintf(stderr, "Pop: %d, %s\n", i, node->name);
1192 #endif
1193     goto restart;
1194 }
1195 
1196 /**
1197  * xsltTestCompMatchList:
1198  * @ctxt:  a XSLT process context
1199  * @node: a node
1200  * @comp: the precompiled pattern list
1201  *
1202  * Test whether the node matches one of the patterns in the list
1203  *
1204  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1205  */
1206 int
1207 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
1208 	              xsltCompMatchPtr comp) {
1209     int ret;
1210 
1211     if ((ctxt == NULL) || (node == NULL))
1212 	return(-1);
1213     while (comp != NULL) {
1214 	ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
1215 	if (ret == 1)
1216 	    return(1);
1217 	comp = comp->next;
1218     }
1219     return(0);
1220 }
1221 
1222 /************************************************************************
1223  *									*
1224  *			Dedicated parser for templates			*
1225  *									*
1226  ************************************************************************/
1227 
1228 #define CUR (*ctxt->cur)
1229 #define SKIP(val) ctxt->cur += (val)
1230 #define NXT(val) ctxt->cur[(val)]
1231 #define CUR_PTR ctxt->cur
1232 
1233 #define SKIP_BLANKS							\
1234     while (IS_BLANK_CH(CUR)) NEXT
1235 
1236 #define CURRENT (*ctxt->cur)
1237 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
1238 
1239 
1240 #define PUSH(op, val, val2, novar)						\
1241     if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1242 
1243 #define SWAP()						\
1244     xsltSwapTopCompMatch(ctxt->comp);
1245 
1246 #define XSLT_ERROR(X)							\
1247     { xsltError(ctxt, __FILE__, __LINE__, X);			\
1248       ctxt->error = (X); return; }
1249 
1250 #define XSLT_ERROR0(X)							\
1251     { xsltError(ctxt, __FILE__, __LINE__, X);			\
1252       ctxt->error = (X); return(0); }
1253 
1254 /**
1255  * xsltScanLiteral:
1256  * @ctxt:  the XPath Parser context
1257  *
1258  * Parse an XPath Litteral:
1259  *
1260  * [29] Literal ::= '"' [^"]* '"'
1261  *                | "'" [^']* "'"
1262  *
1263  * Returns the Literal parsed or NULL
1264  */
1265 
1266 static xmlChar *
1267 xsltScanLiteral(xsltParserContextPtr ctxt) {
1268     const xmlChar *q, *cur;
1269     xmlChar *ret = NULL;
1270     int val, len;
1271 
1272     SKIP_BLANKS;
1273     if (CUR == '"') {
1274         NEXT;
1275 	cur = q = CUR_PTR;
1276 	val = xmlStringCurrentChar(NULL, cur, &len);
1277 	while ((IS_CHAR(val)) && (val != '"')) {
1278 	    cur += len;
1279 	    val = xmlStringCurrentChar(NULL, cur, &len);
1280 	}
1281 	if (!IS_CHAR(val)) {
1282 	    ctxt->error = 1;
1283 	    return(NULL);
1284 	} else {
1285 	    ret = xmlStrndup(q, cur - q);
1286         }
1287 	cur += len;
1288 	CUR_PTR = cur;
1289     } else if (CUR == '\'') {
1290         NEXT;
1291 	cur = q = CUR_PTR;
1292 	val = xmlStringCurrentChar(NULL, cur, &len);
1293 	while ((IS_CHAR(val)) && (val != '\'')) {
1294 	    cur += len;
1295 	    val = xmlStringCurrentChar(NULL, cur, &len);
1296 	}
1297 	if (!IS_CHAR(val)) {
1298 	    ctxt->error = 1;
1299 	    return(NULL);
1300 	} else {
1301 	    ret = xmlStrndup(q, cur - q);
1302         }
1303 	cur += len;
1304 	CUR_PTR = cur;
1305     } else {
1306 	/* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1307 	ctxt->error = 1;
1308 	return(NULL);
1309     }
1310     return(ret);
1311 }
1312 
1313 /**
1314  * xsltScanNCName:
1315  * @ctxt:  the XPath Parser context
1316  *
1317  * Parses a non qualified name
1318  *
1319  * Returns the Name parsed or NULL
1320  */
1321 
1322 static xmlChar *
1323 xsltScanNCName(xsltParserContextPtr ctxt) {
1324     const xmlChar *q, *cur;
1325     xmlChar *ret = NULL;
1326     int val, len;
1327 
1328     SKIP_BLANKS;
1329 
1330     cur = q = CUR_PTR;
1331     val = xmlStringCurrentChar(NULL, cur, &len);
1332     if (!IS_LETTER(val) && (val != '_'))
1333 	return(NULL);
1334 
1335     while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
1336            (val == '.') || (val == '-') ||
1337 	   (val == '_') ||
1338 	   (IS_COMBINING(val)) ||
1339 	   (IS_EXTENDER(val))) {
1340 	cur += len;
1341 	val = xmlStringCurrentChar(NULL, cur, &len);
1342     }
1343     ret = xmlStrndup(q, cur - q);
1344     CUR_PTR = cur;
1345     return(ret);
1346 }
1347 
1348 /*
1349  * xsltCompileIdKeyPattern:
1350  * @ctxt:  the compilation context
1351  * @name:  a preparsed name
1352  * @aid:  whether id/key are allowed there
1353  * @novar:  flag to prohibit xslt var
1354  *
1355  * Compile the XSLT LocationIdKeyPattern
1356  * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1357  *                    | 'key' '(' Literal ',' Literal ')'
1358  *
1359  * also handle NodeType and PI from:
1360  *
1361  * [7]  NodeTest ::= NameTest
1362  *                 | NodeType '(' ')'
1363  *                 | 'processing-instruction' '(' Literal ')'
1364  */
1365 static void
1366 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
1367 		int aid, int novar, xsltAxis axis) {
1368     xmlChar *lit = NULL;
1369     xmlChar *lit2 = NULL;
1370 
1371     if (CUR != '(') {
1372 	xsltTransformError(NULL, NULL, NULL,
1373 		"xsltCompileIdKeyPattern : ( expected\n");
1374 	ctxt->error = 1;
1375 	return;
1376     }
1377     if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
1378 	if (axis != 0) {
1379 	    xsltTransformError(NULL, NULL, NULL,
1380 		    "xsltCompileIdKeyPattern : NodeTest expected\n");
1381 	    ctxt->error = 1;
1382 	    return;
1383 	}
1384 	NEXT;
1385 	SKIP_BLANKS;
1386         lit = xsltScanLiteral(ctxt);
1387 	if (ctxt->error) {
1388 	    xsltTransformError(NULL, NULL, NULL,
1389 		    "xsltCompileIdKeyPattern : Literal expected\n");
1390 	    return;
1391 	}
1392 	SKIP_BLANKS;
1393 	if (CUR != ')') {
1394 	    xsltTransformError(NULL, NULL, NULL,
1395 		    "xsltCompileIdKeyPattern : ) expected\n");
1396 	    xmlFree(lit);
1397 	    ctxt->error = 1;
1398 	    return;
1399 	}
1400 	NEXT;
1401 	PUSH(XSLT_OP_ID, lit, NULL, novar);
1402 	lit = NULL;
1403     } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
1404 	if (axis != 0) {
1405 	    xsltTransformError(NULL, NULL, NULL,
1406 		    "xsltCompileIdKeyPattern : NodeTest expected\n");
1407 	    ctxt->error = 1;
1408 	    return;
1409 	}
1410 	NEXT;
1411 	SKIP_BLANKS;
1412         lit = xsltScanLiteral(ctxt);
1413 	if (ctxt->error) {
1414 	    xsltTransformError(NULL, NULL, NULL,
1415 		    "xsltCompileIdKeyPattern : Literal expected\n");
1416 	    return;
1417 	}
1418 	SKIP_BLANKS;
1419 	if (CUR != ',') {
1420 	    xsltTransformError(NULL, NULL, NULL,
1421 		    "xsltCompileIdKeyPattern : , expected\n");
1422 	    xmlFree(lit);
1423 	    ctxt->error = 1;
1424 	    return;
1425 	}
1426 	NEXT;
1427 	SKIP_BLANKS;
1428         lit2 = xsltScanLiteral(ctxt);
1429 	if (ctxt->error) {
1430 	    xsltTransformError(NULL, NULL, NULL,
1431 		    "xsltCompileIdKeyPattern : Literal expected\n");
1432 	    xmlFree(lit);
1433 	    return;
1434 	}
1435 	SKIP_BLANKS;
1436 	if (CUR != ')') {
1437 	    xsltTransformError(NULL, NULL, NULL,
1438 		    "xsltCompileIdKeyPattern : ) expected\n");
1439 	    xmlFree(lit);
1440 	    xmlFree(lit2);
1441 	    ctxt->error = 1;
1442 	    return;
1443 	}
1444 	NEXT;
1445 	/* URGENT TODO: support namespace in keys */
1446 	PUSH(XSLT_OP_KEY, lit, lit2, novar);
1447 	lit = NULL;
1448 	lit2 = NULL;
1449     } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
1450 	NEXT;
1451 	SKIP_BLANKS;
1452 	if (CUR != ')') {
1453 	    lit = xsltScanLiteral(ctxt);
1454 	    if (ctxt->error) {
1455 		xsltTransformError(NULL, NULL, NULL,
1456 			"xsltCompileIdKeyPattern : Literal expected\n");
1457 		return;
1458 	    }
1459 	    SKIP_BLANKS;
1460 	    if (CUR != ')') {
1461 		xsltTransformError(NULL, NULL, NULL,
1462 			"xsltCompileIdKeyPattern : ) expected\n");
1463 		ctxt->error = 1;
1464                 xmlFree(lit);
1465 		return;
1466 	    }
1467 	}
1468 	NEXT;
1469 	PUSH(XSLT_OP_PI, lit, NULL, novar);
1470 	lit = NULL;
1471     } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
1472 	NEXT;
1473 	SKIP_BLANKS;
1474 	if (CUR != ')') {
1475 	    xsltTransformError(NULL, NULL, NULL,
1476 		    "xsltCompileIdKeyPattern : ) expected\n");
1477 	    ctxt->error = 1;
1478 	    return;
1479 	}
1480 	NEXT;
1481 	PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
1482     } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
1483 	NEXT;
1484 	SKIP_BLANKS;
1485 	if (CUR != ')') {
1486 	    xsltTransformError(NULL, NULL, NULL,
1487 		    "xsltCompileIdKeyPattern : ) expected\n");
1488 	    ctxt->error = 1;
1489 	    return;
1490 	}
1491 	NEXT;
1492 	PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
1493     } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
1494 	NEXT;
1495 	SKIP_BLANKS;
1496 	if (CUR != ')') {
1497 	    xsltTransformError(NULL, NULL, NULL,
1498 		    "xsltCompileIdKeyPattern : ) expected\n");
1499 	    ctxt->error = 1;
1500 	    return;
1501 	}
1502 	NEXT;
1503 	if (axis == AXIS_ATTRIBUTE) {
1504 	    PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1505 	}
1506 	else {
1507 	    PUSH(XSLT_OP_NODE, NULL, NULL, novar);
1508 	}
1509     } else if (aid) {
1510 	xsltTransformError(NULL, NULL, NULL,
1511 	    "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1512 	ctxt->error = 1;
1513 	return;
1514     } else {
1515 	xsltTransformError(NULL, NULL, NULL,
1516 	    "xsltCompileIdKeyPattern : node type\n");
1517 	ctxt->error = 1;
1518 	return;
1519     }
1520 error:
1521     return;
1522 }
1523 
1524 /**
1525  * xsltCompileStepPattern:
1526  * @ctxt:  the compilation context
1527  * @token:  a posible precompiled name
1528  * @novar: flag to prohibit xslt variables from pattern
1529  *
1530  * Compile the XSLT StepPattern and generates a precompiled
1531  * form suitable for fast matching.
1532  *
1533  * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
1534  * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1535  *                                     | ('child' | 'attribute') '::'
1536  * from XPath
1537  * [7]  NodeTest ::= NameTest
1538  *                 | NodeType '(' ')'
1539  *                 | 'processing-instruction' '(' Literal ')'
1540  * [8] Predicate ::= '[' PredicateExpr ']'
1541  * [9] PredicateExpr ::= Expr
1542  * [13] AbbreviatedAxisSpecifier ::= '@'?
1543  * [37] NameTest ::= '*' | NCName ':' '*' | QName
1544  */
1545 
1546 static void
1547 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1548     xmlChar *name = NULL;
1549     const xmlChar *URI = NULL;
1550     xmlChar *URL = NULL;
1551     int level;
1552     xsltAxis axis = 0;
1553 
1554     SKIP_BLANKS;
1555     if ((token == NULL) && (CUR == '@')) {
1556 	NEXT;
1557         axis = AXIS_ATTRIBUTE;
1558     }
1559 parse_node_test:
1560     if (token == NULL)
1561 	token = xsltScanNCName(ctxt);
1562     if (token == NULL) {
1563 	if (CUR == '*') {
1564 	    NEXT;
1565 	    if (axis == AXIS_ATTRIBUTE) {
1566                 PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1567             }
1568             else {
1569                 PUSH(XSLT_OP_ALL, NULL, NULL, novar);
1570             }
1571 	    goto parse_predicate;
1572 	} else {
1573 	    xsltTransformError(NULL, NULL, NULL,
1574 		    "xsltCompileStepPattern : Name expected\n");
1575 	    ctxt->error = 1;
1576 	    goto error;
1577 	}
1578     }
1579 
1580 
1581     SKIP_BLANKS;
1582     if (CUR == '(') {
1583 	xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
1584 	xmlFree(token);
1585 	token = NULL;
1586 	if (ctxt->error)
1587 	    goto error;
1588     } else if (CUR == ':') {
1589 	NEXT;
1590 	if (CUR != ':') {
1591 	    xmlChar *prefix = token;
1592 	    xmlNsPtr ns;
1593 
1594 	    /*
1595 	     * This is a namespace match
1596 	     */
1597 	    token = xsltScanNCName(ctxt);
1598 	    ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1599 	    if (ns == NULL) {
1600 		xsltTransformError(NULL, NULL, NULL,
1601 	    "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1602 				 prefix);
1603 		xmlFree(prefix);
1604 		prefix=NULL;
1605 		ctxt->error = 1;
1606 		goto error;
1607 	    } else {
1608 		URL = xmlStrdup(ns->href);
1609 	    }
1610 	    xmlFree(prefix);
1611 	    prefix=NULL;
1612 	    if (token == NULL) {
1613 		if (CUR == '*') {
1614 		    NEXT;
1615                     if (axis == AXIS_ATTRIBUTE) {
1616                         PUSH(XSLT_OP_ATTR, NULL, URL, novar);
1617 			URL = NULL;
1618                     }
1619                     else {
1620                         PUSH(XSLT_OP_NS, URL, NULL, novar);
1621 			URL = NULL;
1622                     }
1623 		} else {
1624 		    xsltTransformError(NULL, NULL, NULL,
1625 			    "xsltCompileStepPattern : Name expected\n");
1626 		    ctxt->error = 1;
1627                     xmlFree(URL);
1628 		    goto error;
1629 		}
1630 	    } else {
1631                 if (axis == AXIS_ATTRIBUTE) {
1632                     PUSH(XSLT_OP_ATTR, token, URL, novar);
1633 		    token = NULL;
1634 		    URL = NULL;
1635                 }
1636                 else {
1637                     PUSH(XSLT_OP_ELEM, token, URL, novar);
1638 		    token = NULL;
1639 		    URL = NULL;
1640                 }
1641 	    }
1642 	} else {
1643 	    if (axis != 0) {
1644 		xsltTransformError(NULL, NULL, NULL,
1645 		    "xsltCompileStepPattern : NodeTest expected\n");
1646 		ctxt->error = 1;
1647 		goto error;
1648 	    }
1649 	    NEXT;
1650 	    if (xmlStrEqual(token, (const xmlChar *) "child")) {
1651 	        axis = AXIS_CHILD;
1652 	    } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1653 	        axis = AXIS_ATTRIBUTE;
1654 	    } else {
1655 		xsltTransformError(NULL, NULL, NULL,
1656 		    "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1657 		ctxt->error = 1;
1658 		goto error;
1659 	    }
1660 	    xmlFree(token);
1661 	    token = NULL;
1662             SKIP_BLANKS;
1663             token = xsltScanNCName(ctxt);
1664 	    goto parse_node_test;
1665 	}
1666     } else {
1667 	URI = xsltGetQNameURI(ctxt->elem, &token);
1668 	if (token == NULL) {
1669 	    ctxt->error = 1;
1670 	    goto error;
1671 	}
1672 	if (URI != NULL)
1673 	    URL = xmlStrdup(URI);
1674         if (axis == AXIS_ATTRIBUTE) {
1675             PUSH(XSLT_OP_ATTR, token, URL, novar);
1676 	    token = NULL;
1677 	    URL = NULL;
1678         }
1679         else {
1680             PUSH(XSLT_OP_ELEM, token, URL, novar);
1681 	    token = NULL;
1682 	    URL = NULL;
1683         }
1684     }
1685 parse_predicate:
1686     SKIP_BLANKS;
1687     level = 0;
1688     while (CUR == '[') {
1689 	const xmlChar *q;
1690 	xmlChar *ret = NULL;
1691 
1692 	level++;
1693 	NEXT;
1694 	q = CUR_PTR;
1695 	while (CUR != 0) {
1696 	    /* Skip over nested predicates */
1697 	    if (CUR == '[')
1698 		level++;
1699 	    else if (CUR == ']') {
1700 		level--;
1701 		if (level == 0)
1702 		    break;
1703 	    } else if (CUR == '"') {
1704 		NEXT;
1705 		while ((CUR != 0) && (CUR != '"'))
1706 		    NEXT;
1707 	    } else if (CUR == '\'') {
1708 		NEXT;
1709 		while ((CUR != 0) && (CUR != '\''))
1710 		    NEXT;
1711 	    }
1712 	    NEXT;
1713 	}
1714 	if (CUR == 0) {
1715 	    xsltTransformError(NULL, NULL, NULL,
1716 		    "xsltCompileStepPattern : ']' expected\n");
1717 	    ctxt->error = 1;
1718 	    return;
1719         }
1720 	ret = xmlStrndup(q, CUR_PTR - q);
1721 	PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
1722 	ret = NULL;
1723 	/* push the predicate lower than local test */
1724 	SWAP();
1725 	NEXT;
1726 	SKIP_BLANKS;
1727     }
1728     return;
1729 error:
1730     if (token != NULL)
1731 	xmlFree(token);
1732     if (name != NULL)
1733 	xmlFree(name);
1734 }
1735 
1736 /**
1737  * xsltCompileRelativePathPattern:
1738  * @comp:  the compilation context
1739  * @token:  a posible precompiled name
1740  * @novar:  flag to prohibit xslt variables
1741  *
1742  * Compile the XSLT RelativePathPattern and generates a precompiled
1743  * form suitable for fast matching.
1744  *
1745  * [4] RelativePathPattern ::= StepPattern
1746  *                           | RelativePathPattern '/' StepPattern
1747  *                           | RelativePathPattern '//' StepPattern
1748  */
1749 static void
1750 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1751     xsltCompileStepPattern(ctxt, token, novar);
1752     if (ctxt->error)
1753 	goto error;
1754     SKIP_BLANKS;
1755     while ((CUR != 0) && (CUR != '|')) {
1756 	if ((CUR == '/') && (NXT(1) == '/')) {
1757 	    PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1758 	    NEXT;
1759 	    NEXT;
1760 	    SKIP_BLANKS;
1761 	    xsltCompileStepPattern(ctxt, NULL, novar);
1762 	} else if (CUR == '/') {
1763 	    PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1764 	    NEXT;
1765 	    SKIP_BLANKS;
1766 	    if ((CUR != 0) && (CUR != '|')) {
1767 		xsltCompileRelativePathPattern(ctxt, NULL, novar);
1768 	    }
1769 	} else {
1770 	    ctxt->error = 1;
1771 	}
1772 	if (ctxt->error)
1773 	    goto error;
1774 	SKIP_BLANKS;
1775     }
1776 error:
1777     return;
1778 }
1779 
1780 /**
1781  * xsltCompileLocationPathPattern:
1782  * @ctxt:  the compilation context
1783  * @novar:  flag to prohibit xslt variables
1784  *
1785  * Compile the XSLT LocationPathPattern and generates a precompiled
1786  * form suitable for fast matching.
1787  *
1788  * [2] LocationPathPattern ::= '/' RelativePathPattern?
1789  *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
1790  *                           | '//'? RelativePathPattern
1791  */
1792 static void
1793 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
1794     SKIP_BLANKS;
1795     if ((CUR == '/') && (NXT(1) == '/')) {
1796 	/*
1797 	 * since we reverse the query
1798 	 * a leading // can be safely ignored
1799 	 */
1800 	NEXT;
1801 	NEXT;
1802 	ctxt->comp->priority = 0.5;	/* '//' means not 0 priority */
1803 	xsltCompileRelativePathPattern(ctxt, NULL, novar);
1804     } else if (CUR == '/') {
1805 	/*
1806 	 * We need to find root as the parent
1807 	 */
1808 	NEXT;
1809 	SKIP_BLANKS;
1810 	PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
1811 	if ((CUR != 0) && (CUR != '|')) {
1812 	    PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1813 	    xsltCompileRelativePathPattern(ctxt, NULL, novar);
1814 	}
1815     } else if (CUR == '*') {
1816 	xsltCompileRelativePathPattern(ctxt, NULL, novar);
1817     } else if (CUR == '@') {
1818 	xsltCompileRelativePathPattern(ctxt, NULL, novar);
1819     } else {
1820 	xmlChar *name;
1821 	name = xsltScanNCName(ctxt);
1822 	if (name == NULL) {
1823 	    xsltTransformError(NULL, NULL, NULL,
1824 		    "xsltCompileLocationPathPattern : Name expected\n");
1825 	    ctxt->error = 1;
1826 	    return;
1827 	}
1828 	SKIP_BLANKS;
1829 	if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
1830 	    xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
1831 	    xmlFree(name);
1832 	    name = NULL;
1833 	    if ((CUR == '/') && (NXT(1) == '/')) {
1834 		PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1835 		NEXT;
1836 		NEXT;
1837 		SKIP_BLANKS;
1838 		xsltCompileRelativePathPattern(ctxt, NULL, novar);
1839 	    } else if (CUR == '/') {
1840 		PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1841 		NEXT;
1842 		SKIP_BLANKS;
1843 		xsltCompileRelativePathPattern(ctxt, NULL, novar);
1844 	    }
1845 	    return;
1846 	}
1847 	xsltCompileRelativePathPattern(ctxt, name, novar);
1848     }
1849 error:
1850     return;
1851 }
1852 
1853 /**
1854  * xsltCompilePatternInternal:
1855  * @pattern: an XSLT pattern
1856  * @doc:  the containing document
1857  * @node:  the containing element
1858  * @style:  the stylesheet
1859  * @runtime:  the transformation context, if done at run-time
1860  * @novar:  flag to prohibit xslt variables
1861  *
1862  * Compile the XSLT pattern and generates a list of precompiled form suitable
1863  * for fast matching.
1864  *
1865  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1866  *
1867  * Returns the generated pattern list or NULL in case of failure
1868  */
1869 
1870 static xsltCompMatchPtr
1871 xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc,
1872 	           xmlNodePtr node, xsltStylesheetPtr style,
1873 		   xsltTransformContextPtr runtime, int novar) {
1874     xsltParserContextPtr ctxt = NULL;
1875     xsltCompMatchPtr element, first = NULL, previous = NULL;
1876     int current, start, end, level, j;
1877 
1878     if (pattern == NULL) {
1879 	xsltTransformError(NULL, NULL, node,
1880 			 "xsltCompilePattern : NULL pattern\n");
1881 	return(NULL);
1882     }
1883 
1884     ctxt = xsltNewParserContext(style, runtime);
1885     if (ctxt == NULL)
1886 	return(NULL);
1887     ctxt->doc = doc;
1888     ctxt->elem = node;
1889     current = end = 0;
1890     while (pattern[current] != 0) {
1891 	start = current;
1892 	while (IS_BLANK_CH(pattern[current]))
1893 	    current++;
1894 	end = current;
1895 	level = 0;
1896 	while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
1897 	    if (pattern[end] == '[')
1898 		level++;
1899 	    else if (pattern[end] == ']')
1900 		level--;
1901 	    else if (pattern[end] == '\'') {
1902 		end++;
1903 		while ((pattern[end] != 0) && (pattern[end] != '\''))
1904 		    end++;
1905 	    } else if (pattern[end] == '"') {
1906 		end++;
1907 		while ((pattern[end] != 0) && (pattern[end] != '"'))
1908 		    end++;
1909 	    }
1910 	    if (pattern[end] == 0)
1911 	        break;
1912 	    end++;
1913 	}
1914 	if (current == end) {
1915 	    xsltTransformError(NULL, NULL, node,
1916 			     "xsltCompilePattern : NULL pattern\n");
1917 	    goto error;
1918 	}
1919 	element = xsltNewCompMatch();
1920 	if (element == NULL) {
1921 	    goto error;
1922 	}
1923 	if (first == NULL)
1924 	    first = element;
1925 	else if (previous != NULL)
1926 	    previous->next = element;
1927 	previous = element;
1928 
1929 	ctxt->comp = element;
1930 	ctxt->base = xmlStrndup(&pattern[start], end - start);
1931 	if (ctxt->base == NULL)
1932 	    goto error;
1933 	ctxt->cur = &(ctxt->base)[current - start];
1934 	element->pattern = ctxt->base;
1935         element->node = node;
1936 	element->nsList = xmlGetNsList(doc, node);
1937 	j = 0;
1938 	if (element->nsList != NULL) {
1939 	    while (element->nsList[j] != NULL)
1940 		j++;
1941 	}
1942 	element->nsNr = j;
1943 
1944 
1945 #ifdef WITH_XSLT_DEBUG_PATTERN
1946 	xsltGenericDebug(xsltGenericDebugContext,
1947 			 "xsltCompilePattern : parsing '%s'\n",
1948 			 element->pattern);
1949 #endif
1950 	/*
1951 	 Preset default priority to be zero.
1952 	 This may be changed by xsltCompileLocationPathPattern.
1953 	 */
1954 	element->priority = 0;
1955 	xsltCompileLocationPathPattern(ctxt, novar);
1956 	if (ctxt->error) {
1957 	    xsltTransformError(NULL, style, node,
1958 			     "xsltCompilePattern : failed to compile '%s'\n",
1959 			     element->pattern);
1960 	    if (style != NULL) style->errors++;
1961 	    goto error;
1962 	}
1963 
1964 	/*
1965 	 * Reverse for faster interpretation.
1966 	 */
1967 	xsltReverseCompMatch(ctxt, element);
1968 
1969 	/*
1970 	 * Set-up the priority
1971 	 */
1972 	if (element->priority == 0) {	/* if not yet determined */
1973 	    if (((element->steps[0].op == XSLT_OP_ELEM) ||
1974 		 (element->steps[0].op == XSLT_OP_ATTR) ||
1975 		 (element->steps[0].op == XSLT_OP_PI)) &&
1976 		(element->steps[0].value != NULL) &&
1977 		(element->steps[1].op == XSLT_OP_END)) {
1978 		;	/* previously preset */
1979 	    } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1980 		       (element->steps[0].value2 != NULL) &&
1981 		       (element->steps[1].op == XSLT_OP_END)) {
1982 			element->priority = -0.25;
1983 	    } else if ((element->steps[0].op == XSLT_OP_NS) &&
1984 		       (element->steps[0].value != NULL) &&
1985 		       (element->steps[1].op == XSLT_OP_END)) {
1986 			element->priority = -0.25;
1987 	    } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1988 		       (element->steps[0].value == NULL) &&
1989 		       (element->steps[0].value2 == NULL) &&
1990 		       (element->steps[1].op == XSLT_OP_END)) {
1991 			element->priority = -0.5;
1992 	    } else if (((element->steps[0].op == XSLT_OP_PI) ||
1993 		       (element->steps[0].op == XSLT_OP_TEXT) ||
1994 		       (element->steps[0].op == XSLT_OP_ALL) ||
1995 		       (element->steps[0].op == XSLT_OP_NODE) ||
1996 		       (element->steps[0].op == XSLT_OP_COMMENT)) &&
1997 		       (element->steps[1].op == XSLT_OP_END)) {
1998 			element->priority = -0.5;
1999 	    } else {
2000 		element->priority = 0.5;
2001 	    }
2002 	}
2003 #ifdef WITH_XSLT_DEBUG_PATTERN
2004 	xsltGenericDebug(xsltGenericDebugContext,
2005 		     "xsltCompilePattern : parsed %s, default priority %f\n",
2006 			 element->pattern, element->priority);
2007 #endif
2008 	if (pattern[end] == '|')
2009 	    end++;
2010 	current = end;
2011     }
2012     if (end == 0) {
2013 	xsltTransformError(NULL, style, node,
2014 			 "xsltCompilePattern : NULL pattern\n");
2015 	if (style != NULL) style->errors++;
2016 	goto error;
2017     }
2018 
2019     xsltFreeParserContext(ctxt);
2020     return(first);
2021 
2022 error:
2023     if (ctxt != NULL)
2024 	xsltFreeParserContext(ctxt);
2025     if (first != NULL)
2026 	xsltFreeCompMatchList(first);
2027     return(NULL);
2028 }
2029 
2030 /**
2031  * xsltCompilePattern:
2032  * @pattern: an XSLT pattern
2033  * @doc:  the containing document
2034  * @node:  the containing element
2035  * @style:  the stylesheet
2036  * @runtime:  the transformation context, if done at run-time
2037  *
2038  * Compile the XSLT pattern and generates a list of precompiled form suitable
2039  * for fast matching.
2040  *
2041  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
2042  *
2043  * Returns the generated pattern list or NULL in case of failure
2044  */
2045 
2046 xsltCompMatchPtr
2047 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
2048 	           xmlNodePtr node, xsltStylesheetPtr style,
2049 		   xsltTransformContextPtr runtime) {
2050     return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0));
2051 }
2052 
2053 /************************************************************************
2054  *									*
2055  *			Module interfaces				*
2056  *									*
2057  ************************************************************************/
2058 
2059 /**
2060  * xsltAddTemplate:
2061  * @style: an XSLT stylesheet
2062  * @cur: an XSLT template
2063  * @mode:  the mode name or NULL
2064  * @modeURI:  the mode URI or NULL
2065  *
2066  * Register the XSLT pattern associated to @cur
2067  *
2068  * Returns -1 in case of error, 0 otherwise
2069  */
2070 int
2071 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
2072 	        const xmlChar *mode, const xmlChar *modeURI) {
2073     xsltCompMatchPtr pat, list, next;
2074     /*
2075      * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2076      *  avoids gcc 'type-punned pointer' warning.
2077      */
2078     void **top = NULL;
2079     const xmlChar *name = NULL;
2080     float priority;              /* the priority */
2081 
2082     if ((style == NULL) || (cur == NULL))
2083 	return(-1);
2084 
2085     /* Register named template */
2086     if (cur->name != NULL) {
2087         if (style->namedTemplates == NULL) {
2088             style->namedTemplates = xmlHashCreate(10);
2089             if (style->namedTemplates == NULL)
2090                 return(-1);
2091         }
2092         else {
2093             void *dup = xmlHashLookup2(style->namedTemplates, cur->name,
2094                                        cur->nameURI);
2095             if (dup != NULL) {
2096                 xsltTransformError(NULL, style, cur->elem,
2097                                    "xsl:template: error duplicate name '%s'\n",
2098                                    cur->name);
2099                 style->errors++;
2100                 return(-1);
2101             }
2102         }
2103 
2104         xmlHashAddEntry2(style->namedTemplates, cur->name, cur->nameURI, cur);
2105     }
2106 
2107     if (cur->match == NULL)
2108 	return(0);
2109 
2110     priority = cur->priority;
2111     pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem,
2112 		    style, NULL, 1);
2113     if (pat == NULL)
2114 	return(-1);
2115     while (pat) {
2116 	next = pat->next;
2117 	pat->next = NULL;
2118 	name = NULL;
2119 
2120 	pat->template = cur;
2121 	if (mode != NULL)
2122 	    pat->mode = xmlDictLookup(style->dict, mode, -1);
2123 	if (modeURI != NULL)
2124 	    pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
2125 	if (priority != XSLT_PAT_NO_PRIORITY)
2126 	    pat->priority = priority;
2127 
2128 	/*
2129 	 * insert it in the hash table list corresponding to its lookup name
2130 	 */
2131 	switch (pat->steps[0].op) {
2132         case XSLT_OP_ATTR:
2133 	    if (pat->steps[0].value != NULL)
2134 		name = pat->steps[0].value;
2135 	    else
2136 		top = &(style->attrMatch);
2137 	    break;
2138         case XSLT_OP_PARENT:
2139         case XSLT_OP_ANCESTOR:
2140 	    top = &(style->elemMatch);
2141 	    break;
2142         case XSLT_OP_ROOT:
2143 	    top = &(style->rootMatch);
2144 	    break;
2145         case XSLT_OP_KEY:
2146 	    top = &(style->keyMatch);
2147 	    break;
2148         case XSLT_OP_ID:
2149 	    /* TODO optimize ID !!! */
2150         case XSLT_OP_NS:
2151         case XSLT_OP_ALL:
2152 	    top = &(style->elemMatch);
2153 	    break;
2154         case XSLT_OP_END:
2155 	case XSLT_OP_PREDICATE:
2156 	    xsltTransformError(NULL, style, NULL,
2157 			     "xsltAddTemplate: invalid compiled pattern\n");
2158 	    xsltFreeCompMatch(pat);
2159 	    return(-1);
2160 	    /*
2161 	     * TODO: some flags at the top level about type based patterns
2162 	     *       would be faster than inclusion in the hash table.
2163 	     */
2164 	case XSLT_OP_PI:
2165 	    if (pat->steps[0].value != NULL)
2166 		name = pat->steps[0].value;
2167 	    else
2168 		top = &(style->piMatch);
2169 	    break;
2170 	case XSLT_OP_COMMENT:
2171 	    top = &(style->commentMatch);
2172 	    break;
2173 	case XSLT_OP_TEXT:
2174 	    top = &(style->textMatch);
2175 	    break;
2176         case XSLT_OP_ELEM:
2177 	case XSLT_OP_NODE:
2178 	    if (pat->steps[0].value != NULL)
2179 		name = pat->steps[0].value;
2180 	    else
2181 		top = &(style->elemMatch);
2182 	    break;
2183 	}
2184 	if (name != NULL) {
2185 	    if (style->templatesHash == NULL) {
2186 		style->templatesHash = xmlHashCreate(1024);
2187 		if (style->templatesHash == NULL) {
2188 		    xsltFreeCompMatch(pat);
2189 		    return(-1);
2190 		}
2191 		xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
2192 	    } else {
2193 		list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
2194 							 name, mode, modeURI);
2195 		if (list == NULL) {
2196 		    xmlHashAddEntry3(style->templatesHash, name,
2197 				     mode, modeURI, pat);
2198 		} else {
2199 		    /*
2200 		     * Note '<=' since one must choose among the matching
2201 		     * template rules that are left, the one that occurs
2202 		     * last in the stylesheet
2203 		     */
2204 		    if (list->priority <= pat->priority) {
2205 			pat->next = list;
2206 			xmlHashUpdateEntry3(style->templatesHash, name,
2207 					    mode, modeURI, pat, NULL);
2208 		    } else {
2209 			while (list->next != NULL) {
2210 			    if (list->next->priority <= pat->priority)
2211 				break;
2212 			    list = list->next;
2213 			}
2214 			pat->next = list->next;
2215 			list->next = pat;
2216 		    }
2217 		}
2218 	    }
2219 	} else if (top != NULL) {
2220 	    list = *top;
2221 	    if (list == NULL) {
2222 		*top = pat;
2223 		pat->next = NULL;
2224 	    } else if (list->priority <= pat->priority) {
2225 		pat->next = list;
2226 		*top = pat;
2227 	    } else {
2228 		while (list->next != NULL) {
2229 		    if (list->next->priority <= pat->priority)
2230 			break;
2231 		    list = list->next;
2232 		}
2233 		pat->next = list->next;
2234 		list->next = pat;
2235 	    }
2236 	} else {
2237 	    xsltTransformError(NULL, style, NULL,
2238 			     "xsltAddTemplate: invalid compiled pattern\n");
2239 	    xsltFreeCompMatch(pat);
2240 	    return(-1);
2241 	}
2242 #ifdef WITH_XSLT_DEBUG_PATTERN
2243 	if (mode)
2244 	    xsltGenericDebug(xsltGenericDebugContext,
2245 			 "added pattern : '%s' mode '%s' priority %f\n",
2246 			     pat->pattern, pat->mode, pat->priority);
2247 	else
2248 	    xsltGenericDebug(xsltGenericDebugContext,
2249 			 "added pattern : '%s' priority %f\n",
2250 			     pat->pattern, pat->priority);
2251 #endif
2252 
2253 	pat = next;
2254     }
2255     return(0);
2256 }
2257 
2258 static int
2259 xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
2260 {
2261     if ((ctxt == NULL) || (contextNode == NULL)) {
2262 	xsltTransformError(ctxt, NULL, ctxt->inst,
2263 	    "Internal error in xsltComputeAllKeys(): "
2264 	    "Bad arguments.\n");
2265 	return(-1);
2266     }
2267 
2268     if (ctxt->document == NULL) {
2269 	/*
2270 	* The document info will only be NULL if we have a RTF.
2271 	*/
2272 	if (contextNode->doc->_private != NULL)
2273 	    goto doc_info_mismatch;
2274 	/*
2275 	* On-demand creation of the document info (needed for keys).
2276 	*/
2277 	ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
2278 	if (ctxt->document == NULL)
2279 	    return(-1);
2280     }
2281     return xsltInitAllDocKeys(ctxt);
2282 
2283 doc_info_mismatch:
2284     xsltTransformError(ctxt, NULL, ctxt->inst,
2285 	"Internal error in xsltComputeAllKeys(): "
2286 	"The context's document info doesn't match the "
2287 	"document info of the current result tree.\n");
2288     ctxt->state = XSLT_STATE_STOPPED;
2289     return(-1);
2290 }
2291 
2292 /**
2293  * xsltGetTemplate:
2294  * @ctxt:  a XSLT process context
2295  * @node:  the node being processed
2296  * @style:  the current style
2297  *
2298  * Finds the template applying to this node, if @style is non-NULL
2299  * it means one needs to look for the next imported template in scope.
2300  *
2301  * Returns the xsltTemplatePtr or NULL if not found
2302  */
2303 xsltTemplatePtr
2304 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2305 	        xsltStylesheetPtr style)
2306 {
2307     xsltStylesheetPtr curstyle;
2308     xsltTemplatePtr ret = NULL;
2309     const xmlChar *name = NULL;
2310     xsltCompMatchPtr list = NULL;
2311     float priority;
2312     int keyed = 0;
2313 
2314     if ((ctxt == NULL) || (node == NULL))
2315 	return(NULL);
2316 
2317     if (style == NULL) {
2318 	curstyle = ctxt->style;
2319     } else {
2320 	curstyle = xsltNextImport(style);
2321     }
2322 
2323     while ((curstyle != NULL) && (curstyle != style)) {
2324 	priority = XSLT_PAT_NO_PRIORITY;
2325 	/* TODO : handle IDs/keys here ! */
2326 	if (curstyle->templatesHash != NULL) {
2327 	    /*
2328 	     * Use the top name as selector
2329 	     */
2330 	    switch (node->type) {
2331 		case XML_ELEMENT_NODE:
2332 		    if (node->name[0] == ' ')
2333 			break;
2334 		case XML_ATTRIBUTE_NODE:
2335 		case XML_PI_NODE:
2336 		    name = node->name;
2337 		    break;
2338 		case XML_DOCUMENT_NODE:
2339 		case XML_HTML_DOCUMENT_NODE:
2340 		case XML_TEXT_NODE:
2341 		case XML_CDATA_SECTION_NODE:
2342 		case XML_COMMENT_NODE:
2343 		case XML_ENTITY_REF_NODE:
2344 		case XML_ENTITY_NODE:
2345 		case XML_DOCUMENT_TYPE_NODE:
2346 		case XML_DOCUMENT_FRAG_NODE:
2347 		case XML_NOTATION_NODE:
2348 		case XML_DTD_NODE:
2349 		case XML_ELEMENT_DECL:
2350 		case XML_ATTRIBUTE_DECL:
2351 		case XML_ENTITY_DECL:
2352 		case XML_NAMESPACE_DECL:
2353 		case XML_XINCLUDE_START:
2354 		case XML_XINCLUDE_END:
2355 		    break;
2356 		default:
2357 		    return(NULL);
2358 
2359 	    }
2360 	}
2361 	if (name != NULL) {
2362 	    /*
2363 	     * find the list of applicable expressions based on the name
2364 	     */
2365 	    list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
2366 					     name, ctxt->mode, ctxt->modeURI);
2367 	} else
2368 	    list = NULL;
2369 	while (list != NULL) {
2370 	    if (xsltTestCompMatch(ctxt, list, node,
2371 			          ctxt->mode, ctxt->modeURI)) {
2372 		ret = list->template;
2373 		priority = list->priority;
2374 		break;
2375 	    }
2376 	    list = list->next;
2377 	}
2378 	list = NULL;
2379 
2380 	/*
2381 	 * find alternate generic matches
2382 	 */
2383 	switch (node->type) {
2384 	    case XML_ELEMENT_NODE:
2385 		if (node->name[0] == ' ')
2386 		    list = curstyle->rootMatch;
2387 		else
2388 		    list = curstyle->elemMatch;
2389 		if (node->psvi != NULL) keyed = 1;
2390 		break;
2391 	    case XML_ATTRIBUTE_NODE: {
2392 	        xmlAttrPtr attr;
2393 
2394 		list = curstyle->attrMatch;
2395 		attr = (xmlAttrPtr) node;
2396 		if (attr->psvi != NULL) keyed = 1;
2397 		break;
2398 	    }
2399 	    case XML_PI_NODE:
2400 		list = curstyle->piMatch;
2401 		if (node->psvi != NULL) keyed = 1;
2402 		break;
2403 	    case XML_DOCUMENT_NODE:
2404 	    case XML_HTML_DOCUMENT_NODE: {
2405 	        xmlDocPtr doc;
2406 
2407 		list = curstyle->rootMatch;
2408 		doc = (xmlDocPtr) node;
2409 		if (doc->psvi != NULL) keyed = 1;
2410 		break;
2411 	    }
2412 	    case XML_TEXT_NODE:
2413 	    case XML_CDATA_SECTION_NODE:
2414 		list = curstyle->textMatch;
2415 		if (node->psvi != NULL) keyed = 1;
2416 		break;
2417 	    case XML_COMMENT_NODE:
2418 		list = curstyle->commentMatch;
2419 		if (node->psvi != NULL) keyed = 1;
2420 		break;
2421 	    case XML_ENTITY_REF_NODE:
2422 	    case XML_ENTITY_NODE:
2423 	    case XML_DOCUMENT_TYPE_NODE:
2424 	    case XML_DOCUMENT_FRAG_NODE:
2425 	    case XML_NOTATION_NODE:
2426 	    case XML_DTD_NODE:
2427 	    case XML_ELEMENT_DECL:
2428 	    case XML_ATTRIBUTE_DECL:
2429 	    case XML_ENTITY_DECL:
2430 	    case XML_NAMESPACE_DECL:
2431 	    case XML_XINCLUDE_START:
2432 	    case XML_XINCLUDE_END:
2433 		break;
2434 	    default:
2435 		break;
2436 	}
2437 	while ((list != NULL) &&
2438 	       ((ret == NULL)  || (list->priority > priority))) {
2439 	    if (xsltTestCompMatch(ctxt, list, node,
2440 			          ctxt->mode, ctxt->modeURI)) {
2441 		ret = list->template;
2442 		priority = list->priority;
2443 		break;
2444 	    }
2445 	    list = list->next;
2446 	}
2447 	/*
2448 	 * Some of the tests for elements can also apply to documents
2449 	 */
2450 	if ((node->type == XML_DOCUMENT_NODE) ||
2451 	    (node->type == XML_HTML_DOCUMENT_NODE) ||
2452 	    (node->type == XML_TEXT_NODE)) {
2453 	    list = curstyle->elemMatch;
2454 	    while ((list != NULL) &&
2455 		   ((ret == NULL)  || (list->priority > priority))) {
2456 		if (xsltTestCompMatch(ctxt, list, node,
2457 				      ctxt->mode, ctxt->modeURI)) {
2458 		    ret = list->template;
2459 		    priority = list->priority;
2460 		    break;
2461 		}
2462 		list = list->next;
2463 	    }
2464 	} else if ((node->type == XML_PI_NODE) ||
2465 		   (node->type == XML_COMMENT_NODE)) {
2466 	    list = curstyle->elemMatch;
2467 	    while ((list != NULL) &&
2468 		   ((ret == NULL)  || (list->priority > priority))) {
2469 		if (xsltTestCompMatch(ctxt, list, node,
2470 				      ctxt->mode, ctxt->modeURI)) {
2471 		    ret = list->template;
2472 		    priority = list->priority;
2473 		    break;
2474 		}
2475 		list = list->next;
2476 	    }
2477 	}
2478 
2479 keyed_match:
2480 	if (keyed) {
2481 	    list = curstyle->keyMatch;
2482 	    while ((list != NULL) &&
2483 		   ((ret == NULL)  || (list->priority > priority))) {
2484 		if (xsltTestCompMatch(ctxt, list, node,
2485 				      ctxt->mode, ctxt->modeURI)) {
2486 		    ret = list->template;
2487 		    priority = list->priority;
2488 		    break;
2489 		}
2490 		list = list->next;
2491 	    }
2492 	}
2493 	else if (ctxt->hasTemplKeyPatterns &&
2494 	    ((ctxt->document == NULL) ||
2495 	     (ctxt->document->nbKeysComputed < ctxt->nbKeys)))
2496 	{
2497 	    /*
2498 	    * Compute all remaining keys for this document.
2499 	    *
2500 	    * REVISIT TODO: I think this could be further optimized.
2501 	    */
2502 	    if (xsltComputeAllKeys(ctxt, node) == -1)
2503 		goto error;
2504 
2505 	    switch (node->type) {
2506 		case XML_ELEMENT_NODE:
2507 		    if (node->psvi != NULL) keyed = 1;
2508 		    break;
2509 		case XML_ATTRIBUTE_NODE:
2510 		    if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1;
2511 		    break;
2512 		case XML_TEXT_NODE:
2513 		case XML_CDATA_SECTION_NODE:
2514 		case XML_COMMENT_NODE:
2515 		case XML_PI_NODE:
2516 		    if (node->psvi != NULL) keyed = 1;
2517 		    break;
2518 		case XML_DOCUMENT_NODE:
2519 		case XML_HTML_DOCUMENT_NODE:
2520 		    if (((xmlDocPtr) node)->psvi != NULL) keyed = 1;
2521 		    break;
2522 		default:
2523 		    break;
2524 	    }
2525 	    if (keyed)
2526 		goto keyed_match;
2527 	}
2528 	if (ret != NULL)
2529 	    return(ret);
2530 
2531 	/*
2532 	 * Cycle on next curstylesheet import.
2533 	 */
2534 	curstyle = xsltNextImport(curstyle);
2535     }
2536 
2537 error:
2538     return(NULL);
2539 }
2540 
2541 /**
2542  * xsltCleanupTemplates:
2543  * @style: an XSLT stylesheet
2544  *
2545  * Cleanup the state of the templates used by the stylesheet and
2546  * the ones it imports.
2547  */
2548 void
2549 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
2550 }
2551 
2552 /**
2553  * xsltFreeTemplateHashes:
2554  * @style: an XSLT stylesheet
2555  *
2556  * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2557  */
2558 void
2559 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
2560     if (style->templatesHash != NULL)
2561 	xmlHashFree((xmlHashTablePtr) style->templatesHash,
2562 		    (xmlHashDeallocator) xsltFreeCompMatchList);
2563     if (style->rootMatch != NULL)
2564         xsltFreeCompMatchList(style->rootMatch);
2565     if (style->keyMatch != NULL)
2566         xsltFreeCompMatchList(style->keyMatch);
2567     if (style->elemMatch != NULL)
2568         xsltFreeCompMatchList(style->elemMatch);
2569     if (style->attrMatch != NULL)
2570         xsltFreeCompMatchList(style->attrMatch);
2571     if (style->parentMatch != NULL)
2572         xsltFreeCompMatchList(style->parentMatch);
2573     if (style->textMatch != NULL)
2574         xsltFreeCompMatchList(style->textMatch);
2575     if (style->piMatch != NULL)
2576         xsltFreeCompMatchList(style->piMatch);
2577     if (style->commentMatch != NULL)
2578         xsltFreeCompMatchList(style->commentMatch);
2579     if (style->namedTemplates != NULL)
2580         xmlHashFree(style->namedTemplates, NULL);
2581 }
2582 
2583