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