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