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