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