try to fix #153137 but without a test case it's shooting in the dark.
[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 ATTRIBUTE_UNUSED) {
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 = xsltXPathCompile(ctxt->style, 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                                 (node->doc->name != NULL) &&
775                                 (node->doc->name[0] != ' ')) {
776                                 len = (int)
777                                     XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra);
778                                 XSLT_RUNTIME_EXTRA(ctxt,
779                                         select->previousExtra) = node;
780                                 XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
781                                     (void *) pos;
782                             }
783                             index = pos;
784                         } else
785                             pos = 0;
786                     } else {
787                         /*
788                          * recompute the index
789                          */
790                         xmlNodePtr siblings = node->parent->children;
791                         xmlNodePtr parent = node->parent;
792
793                         while (siblings != NULL) {
794                             if (siblings->type == XML_ELEMENT_NODE) {
795                                 if (siblings == node) {
796                                     len++;
797                                     pos = len;
798                                 } else if ((node->name != NULL) &&
799                                            (siblings->name != NULL) &&
800                                     (node->name[0] == siblings->name[0]) &&
801                                     (xmlStrEqual(node->name, siblings->name))) {
802                                     if ((select->value2 == NULL) ||
803                                         ((siblings->ns != NULL) &&
804                                          (xmlStrEqual(select->value2,
805                                                       siblings->ns->href))))
806                                         len++;
807                                 }
808                             }
809                             siblings = siblings->next;
810                         }
811                         if ((parent == NULL) || (node->doc == NULL))
812                             nocache = 1;
813                         else {
814                             while (parent->parent != NULL)
815                                 parent = parent->parent;
816                             if (((parent->type != XML_DOCUMENT_NODE) &&
817                                  (parent->type != XML_HTML_DOCUMENT_NODE)) ||
818                                  (parent != (xmlNodePtr) node->doc))
819                                 nocache = 1;
820                         }
821                     }
822                     if (pos != 0) {
823                         ctxt->xpathCtxt->contextSize = len;
824                         ctxt->xpathCtxt->proximityPosition = pos;
825                         /*
826                          * If the node is in a Value Tree we cannot
827                          * cache it !
828                          */
829                         if ((node->doc != NULL) && (nocache == 0)) {
830                             XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra) =
831                                 node;
832                             XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
833                                 (void *) pos;
834                             XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra) =
835                                 (void *) len;
836                         }
837                     }
838                 } else if ((select != NULL) && (select->op == XSLT_OP_ALL) &&
839                            (node->type == XML_ELEMENT_NODE)) {
840                     xmlNodePtr previous;
841                     int index, nocache = 0;
842
843                     previous = (xmlNodePtr)
844                         XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra);
845                     index = (int)
846                         XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra);
847                     if ((previous != NULL) &&
848                         (previous->parent == node->parent)) {
849                         /*
850                          * just walk back to adjust the index
851                          */
852                         int indx = 0;
853                         xmlNodePtr sibling = node;
854
855                         while (sibling != NULL) {
856                             if (sibling == previous)
857                                 break;
858                             if (sibling->type == XML_ELEMENT_NODE)
859                                 indx++;
860                             sibling = sibling->prev;
861                         }
862                         if (sibling == NULL) {
863                             /* hum going backward in document order ... */
864                             indx = 0;
865                             sibling = node;
866                             while (sibling != NULL) {
867                                 if (sibling == previous)
868                                     break;
869                                 if (sibling->type == XML_ELEMENT_NODE)
870                                     indx--;
871                                 sibling = sibling->next;
872                             }
873                         }
874                         if (sibling != NULL) {
875                             pos = index + indx;
876                             /*
877                              * If the node is in a Value Tree we cannot
878                              * cache it !
879                              */
880                             if ((node->doc != NULL) &&
881                                 (node->doc->name != NULL) &&
882                                 (node->doc->name[0] != ' ')) {
883                                 len = (int)
884                                     XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra);
885                                 XSLT_RUNTIME_EXTRA(ctxt,
886                                         select->previousExtra) = node;
887                                 XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
888                                     (void *) pos;
889                             }
890                         } else
891                             pos = 0;
892                     } else {
893                         /*
894                          * recompute the index
895                          */
896                         xmlNodePtr siblings = node->parent->children;
897                         xmlNodePtr parent = node->parent;
898
899                         while (siblings != NULL) {
900                             if (siblings->type == XML_ELEMENT_NODE) {
901                                 len++;
902                                 if (siblings == node) {
903                                     pos = len;
904                                 }
905                             }
906                             siblings = siblings->next;
907                         }
908                         if ((parent == NULL) || (node->doc == NULL))
909                             nocache = 1;
910                         else {
911                             while (parent->parent != NULL)
912                                 parent = parent->parent;
913                             if (((parent->type != XML_DOCUMENT_NODE) &&
914                                  (parent->type != XML_HTML_DOCUMENT_NODE)) ||
915                                  (parent != (xmlNodePtr) node->doc))
916                                 nocache = 1;
917                         }
918                     }
919                     if (pos != 0) {
920                         ctxt->xpathCtxt->contextSize = len;
921                         ctxt->xpathCtxt->proximityPosition = pos;
922                         /*
923                          * If the node is in a Value Tree we cannot
924                          * cache it !
925                          */
926                         if ((node->doc != NULL) && (nocache == 0)) {
927                             XSLT_RUNTIME_EXTRA(ctxt, select->previousExtra) =
928                                 node;
929                             XSLT_RUNTIME_EXTRA(ctxt, select->indexExtra) =
930                                 (void *) pos;
931                             XSLT_RUNTIME_EXTRA(ctxt, select->lenExtra) =
932                                 (void *) len;
933                         }
934                     }
935                 }
936                 oldNode = ctxt->node;
937                 ctxt->node = node;
938
939                 if (step->value == NULL)
940                     goto wrong_index;
941                 if (step->comp == NULL)
942                     goto wrong_index;
943
944                 if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList,
945                                             comp->nsNr))
946                     goto wrong_index;
947
948                 if (pos != 0) {
949                     ctxt->xpathCtxt->contextSize = oldCS;
950                     ctxt->xpathCtxt->proximityPosition = oldCP;
951                 }
952                 ctxt->node = oldNode;
953                 break;
954 wrong_index:
955                 if (pos != 0) {
956                     ctxt->xpathCtxt->contextSize = oldCS;
957                     ctxt->xpathCtxt->proximityPosition = oldCP;
958                 }
959                 ctxt->node = oldNode;
960                 return(0);
961             }
962             case XSLT_OP_PI:
963                 if (node->type != XML_PI_NODE)
964                     return(0);
965                 if (step->value != NULL) {
966                     if (!xmlStrEqual(step->value, node->name))
967                         return(0);
968                 }
969                 break;
970             case XSLT_OP_COMMENT:
971                 if (node->type != XML_COMMENT_NODE)
972                     return(0);
973                 break;
974             case XSLT_OP_TEXT:
975                 if ((node->type != XML_TEXT_NODE) &&
976                     (node->type != XML_CDATA_SECTION_NODE))
977                     return(0);
978                 break;
979             case XSLT_OP_NODE:
980                 switch (node->type) {
981                     case XML_ELEMENT_NODE:
982                     case XML_CDATA_SECTION_NODE:
983                     case XML_PI_NODE:
984                     case XML_COMMENT_NODE:
985                     case XML_TEXT_NODE:
986                         break;
987                     default:
988                         return(0);
989                 }
990                 break;
991         }
992     }
993     return(1);
994 }
995
996 /**
997  * xsltTestCompMatchList:
998  * @ctxt:  a XSLT process context
999  * @node: a node
1000  * @comp: the precompiled pattern list
1001  *
1002  * Test wether the node matches one of the patterns in the list
1003  *
1004  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1005  */
1006 int
1007 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
1008                       xsltCompMatchPtr comp) {
1009     int ret;
1010
1011     if ((ctxt == NULL) || (node == NULL))
1012         return(-1);
1013     while (comp != NULL) {
1014         ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
1015         if (ret == 1)
1016             return(1);
1017         comp = comp->next;
1018     }
1019     return(0);
1020 }
1021
1022 /************************************************************************
1023  *                                                                      *
1024  *                      Dedicated parser for templates                  *
1025  *                                                                      *
1026  ************************************************************************/
1027
1028 #define CUR (*ctxt->cur)
1029 #define SKIP(val) ctxt->cur += (val)
1030 #define NXT(val) ctxt->cur[(val)]
1031 #define CUR_PTR ctxt->cur
1032
1033 #define SKIP_BLANKS                                                     \
1034     while (IS_BLANK_CH(CUR)) NEXT
1035
1036 #define CURRENT (*ctxt->cur)
1037 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
1038
1039
1040 #define PUSH(op, val, val2)                                             \
1041     if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2))) goto error;
1042
1043 #define SWAP()                                          \
1044     xsltSwapTopCompMatch(ctxt->comp);
1045
1046 #define XSLT_ERROR(X)                                                   \
1047     { xsltError(ctxt, __FILE__, __LINE__, X);                   \
1048       ctxt->error = (X); return; }
1049
1050 #define XSLT_ERROR0(X)                                                  \
1051     { xsltError(ctxt, __FILE__, __LINE__, X);                   \
1052       ctxt->error = (X); return(0); }
1053
1054 /**
1055  * xsltScanLiteral:
1056  * @ctxt:  the XPath Parser context
1057  *
1058  * Parse an XPath Litteral:
1059  *
1060  * [29] Literal ::= '"' [^"]* '"'
1061  *                | "'" [^']* "'"
1062  *
1063  * Returns the Literal parsed or NULL
1064  */
1065
1066 static xmlChar *
1067 xsltScanLiteral(xsltParserContextPtr ctxt) {
1068     const xmlChar *q, *cur;
1069     xmlChar *ret = NULL;
1070     int val, len;
1071
1072     SKIP_BLANKS;
1073     if (CUR == '"') {
1074         NEXT;
1075         cur = q = CUR_PTR;
1076         val = xmlStringCurrentChar(NULL, cur, &len);
1077         while ((IS_CHAR(val)) && (val != '"')) {
1078             cur += len;
1079             val = xmlStringCurrentChar(NULL, cur, &len);
1080         }
1081         if (!IS_CHAR(val)) {
1082             ctxt->error = 1;
1083             return(NULL);
1084         } else {
1085             ret = xmlStrndup(q, cur - q);
1086         }
1087         cur += len;
1088         CUR_PTR = cur;
1089     } else if (CUR == '\'') {
1090         NEXT;
1091         cur = q = CUR_PTR;
1092         val = xmlStringCurrentChar(NULL, cur, &len);
1093         while ((IS_CHAR(val)) && (val != '\'')) {
1094             cur += len;
1095             val = xmlStringCurrentChar(NULL, cur, &len);
1096         }
1097         if (!IS_CHAR(val)) {
1098             ctxt->error = 1;
1099             return(NULL);
1100         } else {
1101             ret = xmlStrndup(q, cur - q);
1102         }
1103         cur += len;
1104         CUR_PTR = cur;
1105     } else {
1106         /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1107         ctxt->error = 1;
1108         return(NULL);
1109     }
1110     return(ret);
1111 }
1112
1113 /**
1114  * xsltScanName:
1115  * @ctxt:  the XPath Parser context
1116  *
1117  * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | 
1118  *                  CombiningChar | Extender
1119  *
1120  * [5] Name ::= (Letter | '_' | ':') (NameChar)*
1121  *
1122  * [6] Names ::= Name (S Name)*
1123  *
1124  * Returns the Name parsed or NULL
1125  */
1126
1127 static xmlChar *
1128 xsltScanName(xsltParserContextPtr ctxt) {
1129     const xmlChar *q, *cur;
1130     xmlChar *ret = NULL;
1131     int val, len;
1132
1133     SKIP_BLANKS;
1134
1135     cur = q = CUR_PTR;
1136     val = xmlStringCurrentChar(NULL, cur, &len);
1137     if (!IS_LETTER(val) && (val != '_') && (val != ':'))
1138         return(NULL);
1139
1140     while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
1141            (val == '.') || (val == '-') ||
1142            (val == '_') || 
1143            (IS_COMBINING(val)) ||
1144            (IS_EXTENDER(val))) {
1145         cur += len;
1146         val = xmlStringCurrentChar(NULL, cur, &len);
1147     }
1148     ret = xmlStrndup(q, cur - q);
1149     CUR_PTR = cur;
1150     return(ret);
1151 }
1152
1153 /**
1154  * xsltScanNCName:
1155  * @ctxt:  the XPath Parser context
1156  *
1157  * Parses a non qualified name
1158  *
1159  * Returns the Name parsed or NULL
1160  */
1161
1162 static xmlChar *
1163 xsltScanNCName(xsltParserContextPtr ctxt) {
1164     const xmlChar *q, *cur;
1165     xmlChar *ret = NULL;
1166     int val, len;
1167
1168     SKIP_BLANKS;
1169
1170     cur = q = CUR_PTR;
1171     val = xmlStringCurrentChar(NULL, cur, &len);
1172     if (!IS_LETTER(val) && (val != '_'))
1173         return(NULL);
1174
1175     while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
1176            (val == '.') || (val == '-') ||
1177            (val == '_') ||
1178            (IS_COMBINING(val)) ||
1179            (IS_EXTENDER(val))) {
1180         cur += len;
1181         val = xmlStringCurrentChar(NULL, cur, &len);
1182     }
1183     ret = xmlStrndup(q, cur - q);
1184     CUR_PTR = cur;
1185     return(ret);
1186 }
1187
1188 /**
1189  * xsltScanQName:
1190  * @ctxt:  the XPath Parser context
1191  * @prefix:  the place to store the prefix
1192  *
1193  * Parse a qualified name
1194  *
1195  * Returns the Name parsed or NULL
1196  */
1197
1198 static xmlChar *
1199 xsltScanQName(xsltParserContextPtr ctxt, xmlChar **prefix) {
1200     xmlChar *ret = NULL;
1201
1202     *prefix = NULL;
1203     ret = xsltScanNCName(ctxt);
1204     if (CUR == ':') {
1205         *prefix = ret;
1206         NEXT;
1207         ret = xsltScanNCName(ctxt);
1208     }
1209     return(ret);
1210 }
1211
1212 /*
1213  * xsltCompileIdKeyPattern:
1214  * @ctxt:  the compilation context
1215  * @name:  a preparsed name
1216  * @aid:  whether id/key are allowed there
1217  *
1218  * Compile the XSLT LocationIdKeyPattern
1219  * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1220  *                    | 'key' '(' Literal ',' Literal ')'
1221  *
1222  * also handle NodeType and PI from:
1223  *
1224  * [7]  NodeTest ::= NameTest
1225  *                 | NodeType '(' ')'
1226  *                 | 'processing-instruction' '(' Literal ')'
1227  */
1228 static void
1229 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
1230     xmlChar *lit = NULL;
1231     xmlChar *lit2 = NULL;
1232
1233     if (CUR != '(') {
1234         xsltTransformError(NULL, NULL, NULL,
1235                 "xsltCompileIdKeyPattern : ( expected\n");
1236         ctxt->error = 1;
1237         return;
1238     }
1239     if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
1240         NEXT;
1241         SKIP_BLANKS;
1242         lit = xsltScanLiteral(ctxt);
1243         if (ctxt->error)
1244             return;
1245         SKIP_BLANKS;
1246         if (CUR != ')') {
1247             xsltTransformError(NULL, NULL, NULL,
1248                     "xsltCompileIdKeyPattern : ) expected\n");
1249             ctxt->error = 1;
1250             return;
1251         }
1252         NEXT;
1253         PUSH(XSLT_OP_ID, lit, NULL);
1254     } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
1255         NEXT;
1256         SKIP_BLANKS;
1257         lit = xsltScanLiteral(ctxt);
1258         if (ctxt->error)
1259             return;
1260         SKIP_BLANKS;
1261         if (CUR != ',') {
1262             xsltTransformError(NULL, NULL, NULL,
1263                     "xsltCompileIdKeyPattern : , expected\n");
1264             ctxt->error = 1;
1265             return;
1266         }
1267         NEXT;
1268         SKIP_BLANKS;
1269         lit2 = xsltScanLiteral(ctxt);
1270         if (ctxt->error)
1271             return;
1272         SKIP_BLANKS;
1273         if (CUR != ')') {
1274             xsltTransformError(NULL, NULL, NULL,
1275                     "xsltCompileIdKeyPattern : ) expected\n");
1276             ctxt->error = 1;
1277             return;
1278         }
1279         NEXT;
1280         /* TODO: support namespace in keys */
1281         PUSH(XSLT_OP_KEY, lit, lit2);
1282     } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
1283         NEXT;
1284         SKIP_BLANKS;
1285         if (CUR != ')') {
1286             lit = xsltScanLiteral(ctxt);
1287             if (ctxt->error)
1288                 return;
1289             SKIP_BLANKS;
1290             if (CUR != ')') {
1291                 xsltTransformError(NULL, NULL, NULL,
1292                         "xsltCompileIdKeyPattern : ) expected\n");
1293                 ctxt->error = 1;
1294                 return;
1295             }
1296         }
1297         NEXT;
1298         PUSH(XSLT_OP_PI, lit, NULL);
1299     } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
1300         NEXT;
1301         SKIP_BLANKS;
1302         if (CUR != ')') {
1303             xsltTransformError(NULL, NULL, NULL,
1304                     "xsltCompileIdKeyPattern : ) expected\n");
1305             ctxt->error = 1;
1306             return;
1307         }
1308         NEXT;
1309         PUSH(XSLT_OP_TEXT, NULL, NULL);
1310     } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
1311         NEXT;
1312         SKIP_BLANKS;
1313         if (CUR != ')') {
1314             xsltTransformError(NULL, NULL, NULL,
1315                     "xsltCompileIdKeyPattern : ) expected\n");
1316             ctxt->error = 1;
1317             return;
1318         }
1319         NEXT;
1320         PUSH(XSLT_OP_COMMENT, NULL, NULL);
1321     } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
1322         NEXT;
1323         SKIP_BLANKS;
1324         if (CUR != ')') {
1325             xsltTransformError(NULL, NULL, NULL,
1326                     "xsltCompileIdKeyPattern : ) expected\n");
1327             ctxt->error = 1;
1328             return;
1329         }
1330         NEXT;
1331         PUSH(XSLT_OP_NODE, NULL, NULL);
1332     } else if (aid) {
1333         xsltTransformError(NULL, NULL, NULL,
1334             "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1335         ctxt->error = 1;
1336         return;
1337     } else {
1338         xsltTransformError(NULL, NULL, NULL,
1339             "xsltCompileIdKeyPattern : node type\n");
1340         ctxt->error = 1;
1341         return;
1342     }
1343 error:
1344     if (name != NULL)
1345         xmlFree(name);
1346 }
1347
1348 /**
1349  * xsltCompileStepPattern:
1350  * @ctxt:  the compilation context
1351  * @token:  a posible precompiled name
1352  *
1353  * Compile the XSLT StepPattern and generates a precompiled
1354  * form suitable for fast matching.
1355  *
1356  * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* 
1357  * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1358  *                                     | ('child' | 'attribute') '::'
1359  * from XPath
1360  * [7]  NodeTest ::= NameTest
1361  *                 | NodeType '(' ')'
1362  *                 | 'processing-instruction' '(' Literal ')'
1363  * [8] Predicate ::= '[' PredicateExpr ']'
1364  * [9] PredicateExpr ::= Expr
1365  * [13] AbbreviatedAxisSpecifier ::= '@'?
1366  * [37] NameTest ::= '*' | NCName ':' '*' | QName
1367  */
1368
1369 static void
1370 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
1371     xmlChar *name = NULL;
1372     const xmlChar *URI = NULL;
1373     xmlChar *URL = NULL;
1374     int level;
1375
1376     SKIP_BLANKS;
1377     if ((token == NULL) && (CUR == '@')) {
1378         xmlChar *prefix = NULL;
1379
1380         NEXT;
1381         if (CUR == '*') {
1382             NEXT;
1383             PUSH(XSLT_OP_ATTR, NULL, NULL);
1384             goto parse_predicate;
1385         }
1386         token = xsltScanQName(ctxt, &prefix);
1387         if (prefix != NULL) {
1388             xmlNsPtr ns;
1389
1390             ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1391             if (ns == NULL) {
1392                 xsltTransformError(NULL, NULL, NULL,
1393                 "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1394                                  prefix);
1395             } else {
1396                 URL = xmlStrdup(ns->href);
1397             }
1398             xmlFree(prefix);
1399         }
1400         if (token == NULL) {
1401             if (CUR == '*') {
1402                 NEXT;
1403                 PUSH(XSLT_OP_ATTR, NULL, URL);
1404                 return;
1405             }
1406             xsltTransformError(NULL, NULL, NULL,
1407                     "xsltCompileStepPattern : Name expected\n");
1408             ctxt->error = 1;
1409             goto error;
1410         }
1411         PUSH(XSLT_OP_ATTR, token, URL);
1412         goto parse_predicate;
1413     }
1414     if (token == NULL)
1415         token = xsltScanName(ctxt);
1416     if (token == NULL) {
1417         if (CUR == '*') {
1418             NEXT;
1419             PUSH(XSLT_OP_ALL, token, NULL);
1420             goto parse_predicate;
1421         } else {
1422             xsltTransformError(NULL, NULL, NULL,
1423                     "xsltCompileStepPattern : Name expected\n");
1424             ctxt->error = 1;
1425             goto error;
1426         }
1427     }
1428
1429
1430     SKIP_BLANKS;
1431     if (CUR == '(') {
1432         xsltCompileIdKeyPattern(ctxt, token, 0);
1433         if (ctxt->error)
1434             goto error;
1435     } else if (CUR == ':') {
1436         NEXT;
1437         if (CUR != ':') {
1438             xmlChar *prefix = token;
1439             xmlNsPtr ns;
1440
1441             /*
1442              * This is a namespace match
1443              */
1444             token = xsltScanName(ctxt);
1445             ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1446             if (ns == NULL) {
1447                 xsltTransformError(NULL, NULL, NULL,
1448             "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1449                                  prefix);
1450                 ctxt->error = 1;
1451                 goto error;
1452             } else {
1453                 URL = xmlStrdup(ns->href);
1454             }
1455             xmlFree(prefix);
1456             if (token == NULL) {
1457                 if (CUR == '*') {
1458                     NEXT;
1459                     PUSH(XSLT_OP_NS, URL, NULL);
1460                 } else {
1461                     xsltTransformError(NULL, NULL, NULL,
1462                             "xsltCompileStepPattern : Name expected\n");
1463                     ctxt->error = 1;
1464                     goto error;
1465                 }
1466             } else {
1467                 PUSH(XSLT_OP_ELEM, token, URL);
1468             }
1469         } else {
1470             NEXT;
1471             if (xmlStrEqual(token, (const xmlChar *) "child")) {
1472                 xmlFree(token);
1473                 token = xsltScanName(ctxt);
1474                 if (token == NULL) {
1475                     if (CUR == '*') {
1476                         NEXT;
1477                         PUSH(XSLT_OP_ALL, token, NULL);
1478                         goto parse_predicate;
1479                     } else {
1480                         xsltTransformError(NULL, NULL, NULL,
1481                             "xsltCompileStepPattern : QName expected\n");
1482                         ctxt->error = 1;
1483                         goto error;
1484                     }
1485                 }
1486                 URI = xsltGetQNameURI(ctxt->elem, &token);
1487                 if (token == NULL) {
1488                     ctxt->error = 1;
1489                     goto error;
1490                 } else {
1491                     name = xmlStrdup(token);
1492                     if (URI != NULL)
1493                         URL = xmlStrdup(URI);
1494                 }
1495                 PUSH(XSLT_OP_CHILD, name, URL);
1496             } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1497                 xmlFree(token);
1498                 token = xsltScanName(ctxt);
1499                 if (token == NULL) {
1500                     xsltTransformError(NULL, NULL, NULL,
1501                             "xsltCompileStepPattern : QName expected\n");
1502                     ctxt->error = 1;
1503                     goto error;
1504                 }
1505                 URI = xsltGetQNameURI(ctxt->elem, &token);
1506                 if (token == NULL) {
1507                     ctxt->error = 1;
1508                     goto error;
1509                 } else {
1510                     name = xmlStrdup(token);
1511                     if (URI != NULL)
1512                         URL = xmlStrdup(URI);
1513                 }
1514                 PUSH(XSLT_OP_ATTR, name, URL);
1515             } else {
1516                 xsltTransformError(NULL, NULL, NULL,
1517                     "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1518                 ctxt->error = 1;
1519                 goto error;
1520             }
1521             xmlFree(token);
1522         }
1523     } else if (CUR == '*') {
1524         NEXT;
1525         PUSH(XSLT_OP_ALL, token, NULL);
1526     } else {
1527         URI = xsltGetQNameURI(ctxt->elem, &token);
1528         if (token == NULL) {
1529             ctxt->error = 1;
1530             goto error;
1531         }
1532         if (URI != NULL)
1533             URL = xmlStrdup(URI);
1534         PUSH(XSLT_OP_ELEM, token, URL);
1535     }
1536 parse_predicate:
1537     SKIP_BLANKS;
1538     level = 0;
1539     while (CUR == '[') {
1540         const xmlChar *q;
1541         xmlChar *ret = NULL;
1542
1543         level++;
1544         NEXT;
1545         q = CUR_PTR;
1546         while (CUR != 0) {
1547             /* Skip over nested predicates */
1548             if (CUR == '[')
1549                 level++;
1550             else if (CUR == ']') {
1551                 level--;
1552                 if (level == 0)
1553                     break;
1554             } else if (CUR == '"') {
1555                 NEXT;
1556                 while ((CUR != 0) && (CUR != '"'))
1557                     NEXT;
1558             } else if (CUR == '\'') {
1559                 NEXT;
1560                 while ((CUR != 0) && (CUR != '\''))
1561                     NEXT;
1562             }
1563             NEXT;
1564         }
1565         if (CUR == 0) {
1566             xsltTransformError(NULL, NULL, NULL,
1567                     "xsltCompileStepPattern : ']' expected\n");
1568             ctxt->error = 1;
1569             return;
1570         }
1571         ret = xmlStrndup(q, CUR_PTR - q);
1572         PUSH(XSLT_OP_PREDICATE, ret, NULL);
1573         /* push the predicate lower than local test */
1574         SWAP();
1575         NEXT;
1576         SKIP_BLANKS;
1577     }
1578     return;
1579 error:
1580     if (token != NULL)
1581         xmlFree(token);
1582     if (name != NULL)
1583         xmlFree(name);
1584 }
1585
1586 /**
1587  * xsltCompileRelativePathPattern:
1588  * @comp:  the compilation context
1589  * @token:  a posible precompiled name
1590  *
1591  * Compile the XSLT RelativePathPattern and generates a precompiled
1592  * form suitable for fast matching.
1593  *
1594  * [4] RelativePathPattern ::= StepPattern
1595  *                           | RelativePathPattern '/' StepPattern
1596  *                           | RelativePathPattern '//' StepPattern
1597  */
1598 static void
1599 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
1600     xsltCompileStepPattern(ctxt, token);
1601     if (ctxt->error)
1602         goto error;
1603     SKIP_BLANKS;
1604     while ((CUR != 0) && (CUR != '|')) {
1605         if ((CUR == '/') && (NXT(1) == '/')) {
1606             PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1607             NEXT;
1608             NEXT;
1609             SKIP_BLANKS;
1610             xsltCompileStepPattern(ctxt, NULL);
1611         } else if (CUR == '/') {
1612             PUSH(XSLT_OP_PARENT, NULL, NULL);
1613             NEXT;
1614             SKIP_BLANKS;
1615             if ((CUR != 0) || (CUR == '|')) {
1616                 xsltCompileRelativePathPattern(ctxt, NULL);
1617             }
1618         } else {
1619             ctxt->error = 1;
1620         }
1621         if (ctxt->error)
1622             goto error;
1623         SKIP_BLANKS;
1624     }
1625 error:
1626     return;
1627 }
1628
1629 /**
1630  * xsltCompileLocationPathPattern:
1631  * @ctxt:  the compilation context
1632  *
1633  * Compile the XSLT LocationPathPattern and generates a precompiled
1634  * form suitable for fast matching.
1635  *
1636  * [2] LocationPathPattern ::= '/' RelativePathPattern?
1637  *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
1638  *                           | '//'? RelativePathPattern
1639  */
1640 static void
1641 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
1642     SKIP_BLANKS;
1643     if ((CUR == '/') && (NXT(1) == '/')) {
1644         /*
1645          * since we reverse the query
1646          * a leading // can be safely ignored
1647          */
1648         NEXT;
1649         NEXT;
1650         ctxt->comp->priority = 0.5;     /* '//' means not 0 priority */
1651         xsltCompileRelativePathPattern(ctxt, NULL);
1652     } else if (CUR == '/') {
1653         /*
1654          * We need to find root as the parent
1655          */
1656         NEXT;
1657         SKIP_BLANKS;
1658         PUSH(XSLT_OP_ROOT, NULL, NULL);
1659         if ((CUR != 0) || (CUR == '|')) {
1660             PUSH(XSLT_OP_PARENT, NULL, NULL);
1661             xsltCompileRelativePathPattern(ctxt, NULL);
1662         }
1663     } else if (CUR == '*') {
1664         xsltCompileRelativePathPattern(ctxt, NULL);
1665     } else if (CUR == '@') {
1666         xsltCompileRelativePathPattern(ctxt, NULL);
1667     } else {
1668         xmlChar *name;
1669         name = xsltScanName(ctxt);
1670         if (name == NULL) {
1671             xsltTransformError(NULL, NULL, NULL,
1672                     "xsltCompileLocationPathPattern : Name expected\n");
1673             ctxt->error = 1;
1674             return;
1675         }
1676         SKIP_BLANKS;
1677         if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
1678             xsltCompileIdKeyPattern(ctxt, name, 1);
1679             if ((CUR == '/') && (NXT(1) == '/')) {
1680                 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1681                 NEXT;
1682                 NEXT;
1683                 SKIP_BLANKS;
1684                 xsltCompileRelativePathPattern(ctxt, NULL);
1685             } else if (CUR == '/') {
1686                 PUSH(XSLT_OP_PARENT, NULL, NULL);
1687                 NEXT;
1688                 SKIP_BLANKS;
1689                 xsltCompileRelativePathPattern(ctxt, NULL);
1690             }
1691             return;
1692         }
1693         xsltCompileRelativePathPattern(ctxt, name);
1694     }
1695 error:
1696     return;
1697 }
1698
1699 /**
1700  * xsltCompilePattern:
1701  * @pattern: an XSLT pattern
1702  * @doc:  the containing document
1703  * @node:  the containing element
1704  * @style:  the stylesheet
1705  * @runtime:  the transformation context, if done at run-time
1706  *
1707  * Compile the XSLT pattern and generates a list of precompiled form suitable
1708  * for fast matching.
1709  *
1710  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1711  *
1712  * Returns the generated pattern list or NULL in case of failure
1713  */
1714
1715 xsltCompMatchPtr
1716 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
1717                    xmlNodePtr node, xsltStylesheetPtr style,
1718                    xsltTransformContextPtr runtime) {
1719     xsltParserContextPtr ctxt = NULL;
1720     xsltCompMatchPtr element, first = NULL, previous = NULL;
1721     int current, start, end, level, j;
1722
1723     if (pattern == NULL) {
1724         xsltTransformError(NULL, NULL, node,
1725                          "xsltCompilePattern : NULL pattern\n");
1726         return(NULL);
1727     }
1728
1729     ctxt = xsltNewParserContext(style, runtime);
1730     if (ctxt == NULL)
1731         return(NULL);
1732     ctxt->doc = doc;
1733     ctxt->elem = node;
1734     current = end = 0;
1735     while (pattern[current] != 0) {
1736         start = current;
1737         while (IS_BLANK_CH(pattern[current]))
1738             current++;
1739         end = current;
1740         level = 0;
1741         while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
1742             if (pattern[end] == '[')
1743                 level++;
1744             else if (pattern[end] == ']')
1745                 level--;
1746             else if (pattern[end] == '\'') {
1747                 end++;
1748                 while ((pattern[end] != 0) && (pattern[end] != '\''))
1749                     end++;
1750             } else if (pattern[end] == '"') {
1751                 end++;
1752                 while ((pattern[end] != 0) && (pattern[end] != '"'))
1753                     end++;
1754             }
1755             end++;
1756         }
1757         if (current == end) {
1758             xsltTransformError(NULL, NULL, node,
1759                              "xsltCompilePattern : NULL pattern\n");
1760             goto error;
1761         }
1762         element = xsltNewCompMatch();
1763         if (element == NULL) {
1764             goto error;
1765         }
1766         if (first == NULL)
1767             first = element;
1768         else if (previous != NULL)
1769             previous->next = element;
1770         previous = element;
1771
1772         ctxt->comp = element;
1773         ctxt->base = xmlStrndup(&pattern[start], end - start);
1774         if (ctxt->base == NULL)
1775             goto error;
1776         ctxt->cur = &(ctxt->base)[current - start];
1777         element->pattern = ctxt->base;
1778         element->nsList = xmlGetNsList(doc, node);
1779         j = 0;
1780         if (element->nsList != NULL) {
1781             while (element->nsList[j] != NULL)
1782                 j++;
1783         }
1784         element->nsNr = j;
1785
1786
1787 #ifdef WITH_XSLT_DEBUG_PATTERN
1788         xsltGenericDebug(xsltGenericDebugContext,
1789                          "xsltCompilePattern : parsing '%s'\n",
1790                          element->pattern);
1791 #endif
1792         /*
1793          Preset default priority to be zero.
1794          This may be changed by xsltCompileLocationPathPattern.
1795          */
1796         element->priority = 0;
1797         xsltCompileLocationPathPattern(ctxt);
1798         if (ctxt->error) {
1799             xsltTransformError(NULL, style, node,
1800                              "xsltCompilePattern : failed to compile '%s'\n",
1801                              element->pattern);
1802             if (style != NULL) style->errors++;
1803             goto error;
1804         }
1805
1806         /*
1807          * Reverse for faster interpretation.
1808          */
1809         xsltReverseCompMatch(element);
1810
1811         /*
1812          * Set-up the priority
1813          */
1814         if (element->priority == 0) {   /* if not yet determined */
1815             if (((element->steps[0].op == XSLT_OP_ELEM) ||
1816                  (element->steps[0].op == XSLT_OP_ATTR) ||
1817                  (element->steps[0].op == XSLT_OP_PI)) &&
1818                 (element->steps[0].value != NULL) &&
1819                 (element->steps[1].op == XSLT_OP_END)) {
1820                 ;       /* previously preset */
1821             } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1822                        (element->steps[0].value2 != NULL) &&
1823                        (element->steps[1].op == XSLT_OP_END)) {
1824                         element->priority = -0.25;
1825             } else if ((element->steps[0].op == XSLT_OP_NS) &&
1826                        (element->steps[0].value != NULL) &&
1827                        (element->steps[1].op == XSLT_OP_END)) {
1828                         element->priority = -0.25;
1829             } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1830                        (element->steps[0].value == NULL) &&
1831                        (element->steps[0].value2 == NULL) &&
1832                        (element->steps[1].op == XSLT_OP_END)) {
1833                         element->priority = -0.5;
1834             } else if (((element->steps[0].op == XSLT_OP_PI) ||
1835                        (element->steps[0].op == XSLT_OP_TEXT) ||
1836                        (element->steps[0].op == XSLT_OP_ALL) ||
1837                        (element->steps[0].op == XSLT_OP_NODE) ||
1838                        (element->steps[0].op == XSLT_OP_COMMENT)) &&
1839                        (element->steps[1].op == XSLT_OP_END)) {
1840                         element->priority = -0.5;
1841             } else {
1842                 element->priority = 0.5;
1843             }
1844         }
1845 #ifdef WITH_XSLT_DEBUG_PATTERN
1846         xsltGenericDebug(xsltGenericDebugContext,
1847                      "xsltCompilePattern : parsed %s, default priority %f\n",
1848                          element->pattern, element->priority);
1849 #endif
1850         if (pattern[end] == '|')
1851             end++;
1852         current = end;
1853     }
1854     if (end == 0) {
1855         xsltTransformError(NULL, style, node,
1856                          "xsltCompilePattern : NULL pattern\n");
1857         if (style != NULL) style->errors++;
1858         goto error;
1859     }
1860
1861     xsltFreeParserContext(ctxt);
1862     return(first);
1863
1864 error:
1865     if (ctxt != NULL)
1866         xsltFreeParserContext(ctxt);
1867     if (first != NULL)
1868         xsltFreeCompMatchList(first);
1869     return(NULL);
1870 }
1871
1872 /************************************************************************
1873  *                                                                      *
1874  *                      Module interfaces                               *
1875  *                                                                      *
1876  ************************************************************************/
1877
1878 /**
1879  * xsltAddTemplate:
1880  * @style: an XSLT stylesheet
1881  * @cur: an XSLT template
1882  * @mode:  the mode name or NULL
1883  * @modeURI:  the mode URI or NULL
1884  *
1885  * Register the XSLT pattern associated to @cur
1886  *
1887  * Returns -1 in case of error, 0 otherwise
1888  */
1889 int
1890 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
1891                 const xmlChar *mode, const xmlChar *modeURI) {
1892     xsltCompMatchPtr pat, list, *top = NULL, next;
1893     const xmlChar *name = NULL;
1894     float priority;              /* the priority */
1895
1896     if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
1897         return(-1);
1898
1899     priority = cur->priority;
1900     pat = xsltCompilePattern(cur->match, style->doc, cur->elem, style, NULL);
1901     while (pat) {
1902         next = pat->next;
1903         pat->next = NULL;
1904         name = NULL;
1905         
1906         pat->template = cur;
1907         if (mode != NULL)
1908             pat->mode = xmlStrdup(mode);
1909         if (modeURI != NULL)
1910             pat->modeURI = xmlStrdup(modeURI);
1911         if (priority != XSLT_PAT_NO_PRIORITY)
1912             pat->priority = priority;
1913
1914         /*
1915          * insert it in the hash table list corresponding to its lookup name
1916          */
1917         switch (pat->steps[0].op) {
1918         case XSLT_OP_ATTR:
1919             if (pat->steps[0].value != NULL)
1920                 name = pat->steps[0].value;
1921             else
1922                 top = (xsltCompMatchPtr *) &(style->attrMatch);
1923             break;
1924         case XSLT_OP_CHILD:
1925         case XSLT_OP_PARENT:
1926         case XSLT_OP_ANCESTOR:
1927             top = (xsltCompMatchPtr *) &(style->elemMatch);
1928             break;
1929         case XSLT_OP_ROOT:
1930             top = (xsltCompMatchPtr *) &(style->rootMatch);
1931             break;
1932         case XSLT_OP_KEY:
1933             top = (xsltCompMatchPtr *) &(style->keyMatch);
1934             break;
1935         case XSLT_OP_ID:
1936             /* TODO optimize ID !!! */
1937         case XSLT_OP_NS:
1938         case XSLT_OP_ALL:
1939             top = (xsltCompMatchPtr *) &(style->elemMatch);
1940             break;
1941         case XSLT_OP_END:
1942         case XSLT_OP_PREDICATE:
1943             xsltTransformError(NULL, style, NULL,
1944                              "xsltAddTemplate: invalid compiled pattern\n");
1945             xsltFreeCompMatch(pat);
1946             return(-1);
1947             /*
1948              * TODO: some flags at the top level about type based patterns
1949              *       would be faster than inclusion in the hash table.
1950              */
1951         case XSLT_OP_PI:
1952             if (pat->steps[0].value != NULL)
1953                 name = pat->steps[0].value;
1954             else
1955                 top = (xsltCompMatchPtr *) &(style->piMatch);
1956             break;
1957         case XSLT_OP_COMMENT:
1958             top = (xsltCompMatchPtr *) &(style->commentMatch);
1959             break;
1960         case XSLT_OP_TEXT:
1961             top = (xsltCompMatchPtr *) &(style->textMatch);
1962             break;
1963         case XSLT_OP_ELEM:
1964         case XSLT_OP_NODE:
1965             if (pat->steps[0].value != NULL)
1966                 name = pat->steps[0].value;
1967             else
1968                 top = (xsltCompMatchPtr *) &(style->elemMatch);
1969             break;
1970         }
1971         if (name != NULL) {
1972             if (style->templatesHash == NULL) {
1973                 style->templatesHash = xmlHashCreate(1024);
1974                 if (style->templatesHash == NULL) {
1975                     xsltFreeCompMatch(pat);
1976                     return(-1);
1977                 }
1978                 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
1979             } else {
1980                 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
1981                                                          name, mode, modeURI);
1982                 if (list == NULL) {
1983                     xmlHashAddEntry3(style->templatesHash, name,
1984                                      mode, modeURI, pat);
1985                 } else {
1986                     /*
1987                      * Note '<=' since one must choose among the matching
1988                      * template rules that are left, the one that occurs
1989                      * last in the stylesheet
1990                      */
1991                     if (list->priority <= pat->priority) {
1992                         pat->next = list;
1993                         xmlHashUpdateEntry3(style->templatesHash, name,
1994                                             mode, modeURI, pat, NULL);
1995                     } else {
1996                         while (list->next != NULL) {
1997                             if (list->next->priority <= pat->priority)
1998                                 break;
1999                             list = list->next;
2000                         }
2001                         pat->next = list->next;
2002                         list->next = pat;
2003                     }
2004                 }
2005             }
2006         } else if (top != NULL) {
2007             list = *top;
2008             if (list == NULL) {
2009                 *top = pat;
2010                 pat->next = NULL;
2011             } else if (list->priority <= pat->priority) {
2012                 pat->next = list;
2013                 *top = pat;
2014             } else {
2015                 while (list->next != NULL) {
2016                     if (list->next->priority <= pat->priority)
2017                         break;
2018                     list = list->next;
2019                 }
2020                 pat->next = list->next;
2021                 list->next = pat;
2022             }
2023         } else {
2024             xsltTransformError(NULL, style, NULL,
2025                              "xsltAddTemplate: invalid compiled pattern\n");
2026             xsltFreeCompMatch(pat);
2027             return(-1);
2028         }
2029 #ifdef WITH_XSLT_DEBUG_PATTERN
2030         if (mode)
2031             xsltGenericDebug(xsltGenericDebugContext,
2032                          "added pattern : '%s' mode '%s' priority %f\n",
2033                              pat->pattern, pat->mode, pat->priority);
2034         else
2035             xsltGenericDebug(xsltGenericDebugContext,
2036                          "added pattern : '%s' priority %f\n",
2037                              pat->pattern, pat->priority);
2038 #endif
2039
2040         pat = next;
2041     }
2042     return(0);
2043 }
2044
2045 /**
2046  * xsltGetTemplate:
2047  * @ctxt:  a XSLT process context
2048  * @node:  the node being processed
2049  * @style:  the current style
2050  *
2051  * Finds the template applying to this node, if @style is non-NULL
2052  * it means one needs to look for the next imported template in scope.
2053  *
2054  * Returns the xsltTemplatePtr or NULL if not found
2055  */
2056 xsltTemplatePtr
2057 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2058                 xsltStylesheetPtr style) {
2059     xsltStylesheetPtr curstyle;
2060     xsltTemplatePtr ret = NULL;
2061     const xmlChar *name = NULL;
2062     xsltCompMatchPtr list = NULL;
2063     float priority;
2064     int keyed = 0;
2065
2066     if ((ctxt == NULL) || (node == NULL))
2067         return(NULL);
2068
2069     if (style == NULL) {
2070         curstyle = ctxt->style;
2071     } else {
2072         curstyle = xsltNextImport(style);
2073     }
2074
2075     while ((curstyle != NULL) && (curstyle != style)) {
2076         priority = XSLT_PAT_NO_PRIORITY;
2077         /* TODO : handle IDs/keys here ! */
2078         if (curstyle->templatesHash != NULL) {
2079             /*
2080              * Use the top name as selector
2081              */
2082             switch (node->type) {
2083                 case XML_ELEMENT_NODE:
2084                     if (node->name[0] == ' ')
2085                         break;
2086                 case XML_ATTRIBUTE_NODE:
2087                 case XML_PI_NODE:
2088                     name = node->name;
2089                     break;
2090                 case XML_DOCUMENT_NODE:
2091                 case XML_HTML_DOCUMENT_NODE:
2092                 case XML_TEXT_NODE:
2093                 case XML_CDATA_SECTION_NODE:
2094                 case XML_COMMENT_NODE:
2095                 case XML_ENTITY_REF_NODE:
2096                 case XML_ENTITY_NODE:
2097                 case XML_DOCUMENT_TYPE_NODE:
2098                 case XML_DOCUMENT_FRAG_NODE:
2099                 case XML_NOTATION_NODE:
2100                 case XML_DTD_NODE:
2101                 case XML_ELEMENT_DECL:
2102                 case XML_ATTRIBUTE_DECL:
2103                 case XML_ENTITY_DECL:
2104                 case XML_NAMESPACE_DECL:
2105                 case XML_XINCLUDE_START:
2106                 case XML_XINCLUDE_END:
2107                     break;
2108                 default:
2109                     return(NULL);
2110
2111             }
2112         }
2113         if (name != NULL) {
2114             /*
2115              * find the list of appliable expressions based on the name
2116              */
2117             list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
2118                                              name, ctxt->mode, ctxt->modeURI);
2119         } else
2120             list = NULL;
2121         while (list != NULL) {
2122             if (xsltTestCompMatch(ctxt, list, node,
2123                                   ctxt->mode, ctxt->modeURI)) {
2124                 ret = list->template;
2125                 priority = list->priority;
2126                 break;
2127             }
2128             list = list->next;
2129         }
2130         list = NULL;
2131
2132         /*
2133          * find alternate generic matches
2134          */
2135         switch (node->type) {
2136             case XML_ELEMENT_NODE:
2137                 if (node->name[0] == ' ')
2138                     list = curstyle->rootMatch;
2139                 else
2140                     list = curstyle->elemMatch;
2141                 if (node->psvi != NULL) keyed = 1;
2142                 break;
2143             case XML_ATTRIBUTE_NODE: {
2144                 xmlAttrPtr attr;
2145
2146                 list = curstyle->attrMatch;
2147                 attr = (xmlAttrPtr) node;
2148                 if (attr->psvi != NULL) keyed = 1;
2149                 break;
2150             }
2151             case XML_PI_NODE:
2152                 list = curstyle->piMatch;
2153                 if (node->psvi != NULL) keyed = 1;
2154                 break;
2155             case XML_DOCUMENT_NODE:
2156             case XML_HTML_DOCUMENT_NODE: {
2157                 xmlDocPtr doc;
2158
2159                 list = curstyle->rootMatch;
2160                 doc = (xmlDocPtr) node;
2161                 if (doc->psvi != NULL) keyed = 1;
2162                 break;
2163             }
2164             case XML_TEXT_NODE:
2165             case XML_CDATA_SECTION_NODE:
2166                 list = curstyle->textMatch;
2167                 if (node->psvi != NULL) keyed = 1;
2168                 break;
2169             case XML_COMMENT_NODE:
2170                 list = curstyle->commentMatch;
2171                 if (node->psvi != NULL) keyed = 1;
2172                 break;
2173             case XML_ENTITY_REF_NODE:
2174             case XML_ENTITY_NODE:
2175             case XML_DOCUMENT_TYPE_NODE:
2176             case XML_DOCUMENT_FRAG_NODE:
2177             case XML_NOTATION_NODE:
2178             case XML_DTD_NODE:
2179             case XML_ELEMENT_DECL:
2180             case XML_ATTRIBUTE_DECL:
2181             case XML_ENTITY_DECL:
2182             case XML_NAMESPACE_DECL:
2183             case XML_XINCLUDE_START:
2184             case XML_XINCLUDE_END:
2185                 break;
2186             default:
2187                 break;
2188         }
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          * Some of the tests for elements can also apply to documents
2201          */
2202         if ((node->type == XML_DOCUMENT_NODE) ||
2203             (node->type == XML_HTML_DOCUMENT_NODE) ||
2204             (node->type == XML_TEXT_NODE)) {
2205             list = curstyle->elemMatch;
2206             while ((list != NULL) &&
2207                    ((ret == NULL)  || (list->priority > priority))) {
2208                 if (xsltTestCompMatch(ctxt, list, node,
2209                                       ctxt->mode, ctxt->modeURI)) {
2210                     ret = list->template;
2211                     priority = list->priority;
2212                     break;
2213                 }
2214                 list = list->next;
2215             }
2216         } else if ((node->type == XML_PI_NODE) ||
2217                    (node->type == XML_COMMENT_NODE)) {
2218             list = curstyle->elemMatch;
2219             while ((list != NULL) &&
2220                    ((ret == NULL)  || (list->priority > priority))) {
2221                 if (xsltTestCompMatch(ctxt, list, node,
2222                                       ctxt->mode, ctxt->modeURI)) {
2223                     ret = list->template;
2224                     priority = list->priority;
2225                     break;
2226                 }
2227                 list = list->next;
2228             }
2229         }
2230
2231         if (keyed) {
2232             list = curstyle->keyMatch;
2233             while ((list != NULL) &&
2234                    ((ret == NULL)  || (list->priority > priority))) {
2235                 if (xsltTestCompMatch(ctxt, list, node,
2236                                       ctxt->mode, ctxt->modeURI)) {
2237                     ret = list->template;
2238                     priority = list->priority;
2239                     break;
2240                 }
2241                 list = list->next;
2242             }
2243         }
2244         if (ret != NULL)
2245             return(ret);
2246
2247         /*
2248          * Cycle on next curstylesheet import.
2249          */
2250         curstyle = xsltNextImport(curstyle);
2251     }
2252     return(NULL);
2253 }
2254
2255 /**
2256  * xsltCleanupTemplates:
2257  * @style: an XSLT stylesheet
2258  *
2259  * Cleanup the state of the templates used by the stylesheet and
2260  * the ones it imports.
2261  */
2262 void
2263 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
2264 }
2265
2266 /**
2267  * xsltFreeTemplateHashes:
2268  * @style: an XSLT stylesheet
2269  *
2270  * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2271  */
2272 void
2273 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
2274     if (style->templatesHash != NULL)
2275         xmlHashFree((xmlHashTablePtr) style->templatesHash,
2276                     (xmlHashDeallocator) xsltFreeCompMatchList);
2277     if (style->rootMatch != NULL)
2278         xsltFreeCompMatchList(style->rootMatch);
2279     if (style->keyMatch != NULL)
2280         xsltFreeCompMatchList(style->keyMatch);
2281     if (style->elemMatch != NULL)
2282         xsltFreeCompMatchList(style->elemMatch);
2283     if (style->attrMatch != NULL)
2284         xsltFreeCompMatchList(style->attrMatch);
2285     if (style->parentMatch != NULL)
2286         xsltFreeCompMatchList(style->parentMatch);
2287     if (style->textMatch != NULL)
2288         xsltFreeCompMatchList(style->textMatch);
2289     if (style->piMatch != NULL)
2290         xsltFreeCompMatchList(style->piMatch);
2291     if (style->commentMatch != NULL)
2292         xsltFreeCompMatchList(style->commentMatch);
2293 }
2294