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