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