This should speed up and correct a few problems:
[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@imag.fr
10  */
11
12 /*
13  * TODO: handle pathological cases like *[*[@a="b"]]
14  * TODO: detect [number] at compilation, optimize accordingly
15  */
16
17 #include "xsltconfig.h"
18
19 #include <string.h>
20
21 #include <libxml/xmlmemory.h>
22 #include <libxml/tree.h>
23 #include <libxml/valid.h>
24 #include <libxml/hash.h>
25 #include <libxml/xmlerror.h>
26 #include <libxml/parserInternals.h>
27 #include "xslt.h"
28 #include "xsltInternals.h"
29 #include "xsltutils.h"
30 #include "imports.h"
31 #include "templates.h"
32 #include "keys.h"
33 #include "pattern.h"
34
35 #define DEBUG_PATTERN
36
37 /*
38  * Types are private:
39  */
40
41 typedef enum {
42     XSLT_OP_END=0,
43     XSLT_OP_ROOT,
44     XSLT_OP_ELEM,
45     XSLT_OP_CHILD,
46     XSLT_OP_ATTR,
47     XSLT_OP_PARENT,
48     XSLT_OP_ANCESTOR,
49     XSLT_OP_ID,
50     XSLT_OP_KEY,
51     XSLT_OP_NS,
52     XSLT_OP_ALL,
53     XSLT_OP_PI,
54     XSLT_OP_COMMENT,
55     XSLT_OP_TEXT,
56     XSLT_OP_NODE,
57     XSLT_OP_PREDICATE
58 } xsltOp;
59
60
61 typedef struct _xsltStepOp xsltStepOp;
62 typedef xsltStepOp *xsltStepOpPtr;
63 struct _xsltStepOp {
64     xsltOp op;
65     xmlChar *value;
66     xmlChar *value2;
67     xmlChar *value3;
68     /*
69      * Optimisations for count
70      */
71     xmlNodePtr previous;
72     int        index;
73     int        len;
74 };
75
76 struct _xsltCompMatch {
77     struct _xsltCompMatch *next; /* siblings in the name hash */
78     float priority;              /* the priority */
79     const xmlChar *mode;         /* the mode */
80     const xmlChar *modeURI;      /* the mode URI */
81     xsltTemplatePtr template;    /* the associated template */
82
83     /* TODO fix the statically allocated size steps[] */
84     int nbStep;
85     int maxStep;
86     xsltStepOp steps[20];        /* ops for computation */
87 };
88
89 typedef struct _xsltParserContext xsltParserContext;
90 typedef xsltParserContext *xsltParserContextPtr;
91 struct _xsltParserContext {
92     const xmlChar *cur;                 /* the current char being parsed */
93     const xmlChar *base;                /* the full expression */
94     xmlDocPtr      doc;                 /* the source document */
95     xmlNodePtr    elem;                 /* the source element */
96     int error;                          /* error code */
97     xsltCompMatchPtr comp;              /* the result */
98 };
99
100 /************************************************************************
101  *                                                                      *
102  *                      Type functions                                  *
103  *                                                                      *
104  ************************************************************************/
105
106 /**
107  * xsltNewCompMatch:
108  *
109  * Create a new XSLT CompMatch
110  *
111  * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
112  */
113 xsltCompMatchPtr
114 xsltNewCompMatch(void) {
115     xsltCompMatchPtr cur;
116
117     cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
118     if (cur == NULL) {
119         xsltGenericError(xsltGenericErrorContext,
120                 "xsltNewCompMatch : malloc failed\n");
121         return(NULL);
122     }
123     memset(cur, 0, sizeof(xsltCompMatch));
124     cur->maxStep = 20;
125     return(cur);
126 }
127
128 /**
129  * xsltFreeCompMatch:
130  * @comp:  an XSLT comp
131  *
132  * Free up the memory allocated by @comp
133  */
134 void
135 xsltFreeCompMatch(xsltCompMatchPtr comp) {
136     xsltStepOpPtr op;
137     int i;
138
139     if (comp == NULL)
140         return;
141     if (comp->mode != NULL)
142         xmlFree((xmlChar *)comp->mode);
143     if (comp->modeURI != NULL)
144         xmlFree((xmlChar *)comp->modeURI);
145     for (i = 0;i < comp->nbStep;i++) {
146         op = &comp->steps[i];
147         if (op->value != NULL)
148             xmlFree(op->value);
149         if (op->value2 != NULL)
150             xmlFree(op->value2);
151         if (op->value3 != NULL)
152             xmlFree(op->value3);
153     }
154     memset(comp, -1, sizeof(xsltCompMatch));
155     xmlFree(comp);
156 }
157
158 /**
159  * xsltFreeCompMatchList:
160  * @comp:  an XSLT comp list
161  *
162  * Free up the memory allocated by all the elements of @comp
163  */
164 void
165 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
166     xsltCompMatchPtr cur;
167
168     while (comp != NULL) {
169         cur = comp;
170         comp = comp->next;
171         xsltFreeCompMatch(cur);
172     }
173 }
174
175 /**
176  * xsltNewParserContext:
177  *
178  * Create a new XSLT ParserContext
179  *
180  * Returns the newly allocated xsltParserContextPtr or NULL in case of error
181  */
182 xsltParserContextPtr
183 xsltNewParserContext(void) {
184     xsltParserContextPtr cur;
185
186     cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
187     if (cur == NULL) {
188         xsltGenericError(xsltGenericErrorContext,
189                 "xsltNewParserContext : malloc failed\n");
190         return(NULL);
191     }
192     memset(cur, 0, sizeof(xsltParserContext));
193     return(cur);
194 }
195
196 /**
197  * xsltFreeParserContext:
198  * @ctxt:  an XSLT parser context
199  *
200  * Free up the memory allocated by @ctxt
201  */
202 void
203 xsltFreeParserContext(xsltParserContextPtr ctxt) {
204     if (ctxt == NULL)
205         return;
206     memset(ctxt, -1, sizeof(xsltParserContext));
207     xmlFree(ctxt);
208 }
209
210 /**
211  * xsltCompMatchAdd:
212  * @comp:  the compiled match expression
213  * @op:  an op
214  * @value:  the first value
215  * @value2:  the second value
216  *
217  * Add an step to an XSLT Compiled Match
218  *
219  * Returns -1 in case of failure, 0 otherwise.
220  */
221 int
222 xsltCompMatchAdd(xsltCompMatchPtr comp, xsltOp op, xmlChar *value,
223                    xmlChar *value2) {
224     if (comp->nbStep >= 20) {
225         xsltGenericError(xsltGenericErrorContext,
226                 "xsltCompMatchAddOp: overflow\n");
227         return(-1);
228     }
229     comp->steps[comp->nbStep].op = op;
230     comp->steps[comp->nbStep].value = value;
231     comp->steps[comp->nbStep].value2 = value2;
232     comp->nbStep++;
233     return(0);
234 }
235
236 /**
237  * xsltSwapTopCompMatch:
238  * @comp:  the compiled match expression
239  *
240  * reverse the two top steps.
241  */
242 void
243 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
244     int i;
245     int j = comp->nbStep - 1;
246
247     if (j > 0) {
248         register xmlChar *tmp;
249         register xsltOp op;
250         i = j - 1;
251         tmp = comp->steps[i].value;
252         comp->steps[i].value = comp->steps[j].value;
253         comp->steps[j].value = tmp;
254         tmp = comp->steps[i].value2;
255         comp->steps[i].value2 = comp->steps[j].value2;
256         comp->steps[j].value2 = tmp;
257         op = comp->steps[i].op;
258         comp->steps[i].op = comp->steps[j].op;
259         comp->steps[j].op = op;
260     }
261 }
262
263 /**
264  * xsltReverseCompMatch:
265  * @comp:  the compiled match expression
266  *
267  * reverse all the stack of expressions
268  */
269 void
270 xsltReverseCompMatch(xsltCompMatchPtr comp) {
271     int i = 0;
272     int j = comp->nbStep - 1;
273
274     while (j > i) {
275         register xmlChar *tmp;
276         register xsltOp op;
277         tmp = comp->steps[i].value;
278         comp->steps[i].value = comp->steps[j].value;
279         comp->steps[j].value = tmp;
280         tmp = comp->steps[i].value2;
281         comp->steps[i].value2 = comp->steps[j].value2;
282         comp->steps[j].value2 = tmp;
283         op = comp->steps[i].op;
284         comp->steps[i].op = comp->steps[j].op;
285         comp->steps[j].op = op;
286         j--;
287         i++;
288     }
289     comp->steps[comp->nbStep++].op = XSLT_OP_END;
290 }
291
292 /************************************************************************
293  *                                                                      *
294  *              The interpreter for the precompiled patterns            *
295  *                                                                      *
296  ************************************************************************/
297
298 /**
299  * xsltTestCompMatch:
300  * @ctxt:  a XSLT process context
301  * @comp: the precompiled pattern
302  * @node: a node
303  * @mode:  the mode name or NULL
304  * @modeURI:  the mode URI or NULL
305  *
306  * Test wether the node matches the pattern
307  *
308  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
309  */
310 int
311 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
312                   xmlNodePtr node, const xmlChar *mode,
313                   const xmlChar *modeURI) {
314     int i;
315     xsltStepOpPtr step, select = NULL;
316
317     if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
318         xsltGenericError(xsltGenericErrorContext,
319                 "xsltTestCompMatch: null arg\n");
320         return(-1);
321     }
322     if (mode != NULL) {
323         if (comp->mode == NULL)
324             return(0);
325         if ((comp->mode != mode) && (!xmlStrEqual(comp->mode, mode)))
326             return(0);
327     } else {
328         if (comp->mode != NULL)
329             return(0);
330     }
331     if (modeURI != NULL) {
332         if (comp->modeURI == NULL)
333             return(0);
334         if ((comp->modeURI != modeURI) &&
335             (!xmlStrEqual(comp->modeURI, modeURI)))
336             return(0);
337     } else {
338         if (comp->modeURI != NULL)
339             return(0);
340     }
341     for (i = 0;i < comp->nbStep;i++) {
342         step = &comp->steps[i];
343         if (step->op != XSLT_OP_PREDICATE)
344             select = step;
345         switch (step->op) {
346             case XSLT_OP_END:
347                 return(1);
348             case XSLT_OP_ROOT:
349                 if ((node->type == XML_DOCUMENT_NODE) ||
350                     (node->type == XML_HTML_DOCUMENT_NODE))
351                     continue;
352                 return(0);
353             case XSLT_OP_ELEM:
354                 if (node->type != XML_ELEMENT_NODE)
355                     return(0);
356                 if (step->value == NULL)
357                     continue;
358                 if (!xmlStrEqual(step->value, node->name))
359                     return(0);
360
361                 /* Namespace test */
362                 if (node->ns == NULL) {
363                     if (step->value2 != NULL)
364                         return(0);
365                 } else if (node->ns->href != NULL) {
366                     if (step->value2 == NULL)
367                         return(0);
368                     if (!xmlStrEqual(step->value2, node->ns->href))
369                         return(0);
370                 }
371                 continue;
372             case XSLT_OP_CHILD:
373                 TODO /* Handle OP_CHILD */
374                 return(0);
375             case XSLT_OP_ATTR:
376                 if (node->type != XML_ATTRIBUTE_NODE)
377                     return(0);
378                 if (step->value == NULL)
379                     continue;
380                 if (!xmlStrEqual(step->value, node->name))
381                     return(0);
382
383                 /* Namespace test */
384                 if (node->ns == NULL) {
385                     if (step->value2 != NULL)
386                         return(0);
387                 } else if (node->ns->href != NULL) {
388                     if (step->value2 == NULL)
389                         return(0);
390                     if (!xmlStrEqual(step->value2, node->ns->href))
391                         return(0);
392                 }
393                 continue;
394             case XSLT_OP_PARENT:
395                 node = node->parent;
396                 if (node == NULL)
397                     return(0);
398                 if (step->value == NULL)
399                     continue;
400                 if (!xmlStrEqual(step->value, node->name))
401                     return(0);
402                 /* Namespace test */
403                 if (node->ns == NULL) {
404                     if (step->value2 != NULL)
405                         return(0);
406                 } else if (node->ns->href != NULL) {
407                     if (step->value2 == NULL)
408                         return(0);
409                     if (!xmlStrEqual(step->value2, node->ns->href))
410                         return(0);
411                 }
412                 continue;
413             case XSLT_OP_ANCESTOR:
414                 /* TODO: implement coalescing of ANCESTOR/NODE ops */
415                 if (step->value == NULL) {
416                     i++;
417                     step = &comp->steps[i];
418                     if (step->op == XSLT_OP_ROOT)
419                         return(1);
420                     if (step->op != XSLT_OP_ELEM)
421                         return(0);
422                     if (step->value == NULL)
423                         return(-1);
424                 }
425                 if (node == NULL)
426                     return(0);
427                 node = node->parent;
428                 while (node != NULL) {
429                     if (node == NULL)
430                         return(0);
431                     if (xmlStrEqual(step->value, node->name)) {
432                         /* Namespace test */
433                         if (node->ns == NULL) {
434                             if (step->value2 == NULL)
435                                 break;
436                         } else if (node->ns->href != NULL) {
437                             if ((step->value2 != NULL) &&
438                                 (xmlStrEqual(step->value2, node->ns->href)))
439                                 break;
440                         }
441                     }
442                     node = node->parent;
443                 }
444                 if (node == NULL)
445                     return(0);
446                 continue;
447             case XSLT_OP_ID: {
448                 /* TODO Handle IDs decently, must be done differently */
449                 xmlAttrPtr id;
450
451                 id = xmlGetID(node->doc, step->value);
452                 if ((id == NULL) || (id->parent != node))
453                     return(0);
454                 break;
455             }
456             case XSLT_OP_KEY: {
457                 xmlNodeSetPtr list;
458                 int i;
459
460                 list = xsltGetKey(ctxt, step->value,
461                                   step->value3, step->value2);
462                 if (list == NULL)
463                     return(0);
464                 for (i = 0;i < list->nodeNr;i++)
465                     if (list->nodeTab[i] == node)
466                         break;
467                 if (i >= list->nodeNr)
468                     return(0);
469                 break;
470             }
471             case XSLT_OP_NS:
472                 /* Namespace test */
473                 if (node->ns == NULL) {
474                     if (step->value != NULL)
475                         return(0);
476                 } else if (node->ns->href != NULL) {
477                     if (step->value == NULL)
478                         return(0);
479                     if (!xmlStrEqual(step->value, node->ns->href))
480                         return(0);
481                 }
482                 break;
483             case XSLT_OP_ALL:
484                 switch (node->type) {
485                     case XML_DOCUMENT_NODE:
486                     case XML_HTML_DOCUMENT_NODE:
487                     case XML_ELEMENT_NODE:
488                         break;
489                     default:
490                         return(0);
491                 }
492                 break;
493             case XSLT_OP_PREDICATE: {
494                 xmlNodePtr oldNode;
495                 int oldCS, oldCP;
496                 int pos = 0, len = 0;
497                 /*
498                  * Depending on the last selection, one may need to
499                  * recompute contextSize and proximityPosition.
500                  */
501                 oldCS = ctxt->xpathCtxt->contextSize;
502                 oldCP = ctxt->xpathCtxt->proximityPosition;
503                 if ((select != NULL) &&
504                     (select->op == XSLT_OP_ELEM) &&
505                     (select->value != NULL) &&
506                     (node->type == XML_ELEMENT_NODE) &&
507                     (node->parent != NULL)) {
508
509                     if ((select->previous != NULL) &&
510                         (select->previous->parent == node->parent)) {
511                         /*
512                          * just walk back to adjust the index
513                          */
514                         int i = 0;
515                         xmlNodePtr sibling = node;
516
517                         while (sibling != NULL) {
518                             if (sibling == select->previous)
519                                 break;
520                             if (xmlStrEqual(node->name, sibling->name)) {
521                                 if ((select->value2 == NULL) ||
522                                     ((sibling->ns != NULL) &&
523                                      (xmlStrEqual(select->value2,
524                                                   sibling->ns->href))))
525                                     i++;
526                             }
527                             sibling = sibling->prev;
528                         }
529                         if (sibling == NULL) {
530                             /* hum going backward in document order ... */
531                             i = 0;
532                             sibling = node;
533                             while (sibling != NULL) {
534                                 if (sibling == select->previous)
535                                     break;
536                                 if ((select->value2 == NULL) ||
537                                     ((sibling->ns != NULL) &&
538                                      (xmlStrEqual(select->value2,
539                                                   sibling->ns->href))))
540                                     i--;
541                                 sibling = sibling->next;
542                             }
543                         }
544                         if (sibling != NULL) {
545                             pos = select->index + i;
546                             len = select->len;
547                             select->previous = node;
548                             select->index = pos;
549                         } else
550                             pos = 0;
551                     } else {
552                         /*
553                          * recompute the index
554                          */
555                         xmlNodePtr siblings = node->parent->children;
556
557                         while (siblings != NULL) {
558                             if (siblings->type == XML_ELEMENT_NODE) {
559                                 if (siblings == node) {
560                                     len++;
561                                     pos = len;
562                                 } else if (xmlStrEqual(node->name,
563                                            siblings->name)) {
564                                     if ((select->value2 == NULL) ||
565                                         ((siblings->ns != NULL) &&
566                                          (xmlStrEqual(select->value2,
567                                                       siblings->ns->href))))
568                                         len++;
569                                 }
570                             }
571                             siblings = siblings->next;
572                         }
573                     }
574                     if (pos != 0) {
575                         ctxt->xpathCtxt->contextSize = len;
576                         ctxt->xpathCtxt->proximityPosition = pos;
577                         select->previous = node;
578                         select->index = pos;
579                         select->len = len;
580                     }
581                 } else if ((select != NULL) && (select->op == XSLT_OP_ALL)) {
582                     if ((select->previous != NULL) &&
583                         (select->previous->parent == node->parent)) {
584                         /*
585                          * just walk back to adjust the index
586                          */
587                         int i = 0;
588                         xmlNodePtr sibling = node;
589
590                         while (sibling != NULL) {
591                             if (sibling == select->previous)
592                                 break;
593                             if (sibling->type == XML_ELEMENT_NODE)
594                                 i++;
595                             sibling = sibling->prev;
596                         }
597                         if (sibling == NULL) {
598                             /* hum going backward in document order ... */
599                             i = 0;
600                             sibling = node;
601                             while (sibling != NULL) {
602                                 if (sibling == select->previous)
603                                     break;
604                                 if (sibling->type == XML_ELEMENT_NODE)
605                                     i--;
606                                 sibling = sibling->next;
607                             }
608                         }
609                         if (sibling != NULL) {
610                             pos = select->index + i;
611                             len = select->len;
612                             select->previous = node;
613                             select->index = pos;
614                         } else
615                             pos = 0;
616                     } else {
617                         /*
618                          * recompute the index
619                          */
620                         xmlNodePtr siblings = node->parent->children;
621
622                         while (siblings != NULL) {
623                             if (siblings->type == XML_ELEMENT_NODE) {
624                                 len++;
625                                 if (siblings == node) {
626                                     pos = len;
627                                 }
628                             }
629                             siblings = siblings->next;
630                         }
631                     }
632                     if (pos != 0) {
633                         ctxt->xpathCtxt->contextSize = len;
634                         ctxt->xpathCtxt->proximityPosition = pos;
635                         select->previous = node;
636                         select->index = pos;
637                         select->len = len;
638                     }
639                 }
640                 oldNode = ctxt->node;
641                 ctxt->node = node;
642
643                 if ((step->value == NULL) ||
644                     (!xsltEvalXPathPredicate(ctxt, step->value))) {
645                     if (pos != 0) {
646                         ctxt->xpathCtxt->contextSize = oldCS;
647                         ctxt->xpathCtxt->proximityPosition = oldCP;
648                     }
649                     ctxt->node = oldNode;
650                     return(0);
651                 }
652                 if (pos != 0) {
653                     ctxt->xpathCtxt->contextSize = oldCS;
654                     ctxt->xpathCtxt->proximityPosition = oldCP;
655                 }
656                 ctxt->node = oldNode;
657                 break;
658             }
659             case XSLT_OP_PI:
660                 if (node->type != XML_PI_NODE)
661                     return(0);
662                 if (step->value != NULL) {
663                     if (!xmlStrEqual(step->value, node->name))
664                         return(0);
665                 }
666                 break;
667             case XSLT_OP_COMMENT:
668                 if (node->type != XML_COMMENT_NODE)
669                     return(0);
670                 break;
671             case XSLT_OP_TEXT:
672                 if ((node->type != XML_TEXT_NODE) &&
673                     (node->type != XML_CDATA_SECTION_NODE))
674                     return(0);
675                 break;
676             case XSLT_OP_NODE:
677                 switch (node->type) {
678                     case XML_DOCUMENT_NODE:
679                     case XML_HTML_DOCUMENT_NODE:
680                     case XML_ELEMENT_NODE:
681                     case XML_CDATA_SECTION_NODE:
682                     case XML_PI_NODE:
683                     case XML_COMMENT_NODE:
684                     case XML_TEXT_NODE:
685                     case XML_ATTRIBUTE_NODE:
686                         break;
687                     default:
688                         return(0);
689                 }
690                 break;
691         }
692     }
693     return(1);
694 }
695
696 /**
697  * xsltTestCompMatchList:
698  * @ctxt:  a XSLT process context
699  * @node: a node
700  * @comp: the precompiled pattern list
701  *
702  * Test wether the node matches one of the patterns in the list
703  *
704  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
705  */
706 int
707 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
708                       xsltCompMatchPtr comp) {
709     int ret;
710
711     if ((ctxt == NULL) || (node == NULL))
712         return(-1);
713     while (comp != NULL) {
714         ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
715         if (ret == 1)
716             return(1);
717         comp = comp->next;
718     }
719     return(0);
720 }
721
722 /************************************************************************
723  *                                                                      *
724  *                      Dedicated parser for templates                  *
725  *                                                                      *
726  ************************************************************************/
727
728 #define CUR (*ctxt->cur)
729 #define SKIP(val) ctxt->cur += (val)
730 #define NXT(val) ctxt->cur[(val)]
731 #define CUR_PTR ctxt->cur
732
733 #define SKIP_BLANKS                                                     \
734     while (IS_BLANK(CUR)) NEXT
735
736 #define CURRENT (*ctxt->cur)
737 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
738
739
740 #define PUSH(op, val, val2)                                             \
741     if (xsltCompMatchAdd(ctxt->comp, (op), (val), (val2))) goto error;
742
743 #define SWAP()                                          \
744     xsltSwapTopCompMatch(ctxt->comp);
745
746 #define XSLT_ERROR(X)                                                   \
747     { xsltError(ctxt, __FILE__, __LINE__, X);                   \
748       ctxt->error = (X); return; }
749
750 #define XSLT_ERROR0(X)                                                  \
751     { xsltError(ctxt, __FILE__, __LINE__, X);                   \
752       ctxt->error = (X); return(0); }
753
754 /**
755  * xsltScanLiteral:
756  * @ctxt:  the XPath Parser context
757  *
758  * Parse an XPath Litteral:
759  *
760  * [29] Literal ::= '"' [^"]* '"'
761  *                | "'" [^']* "'"
762  *
763  * Returns the Literal parsed or NULL
764  */
765
766 xmlChar *
767 xsltScanLiteral(xsltParserContextPtr ctxt) {
768     const xmlChar *q;
769     xmlChar *ret = NULL;
770
771     SKIP_BLANKS;
772     if (CUR == '"') {
773         NEXT;
774         q = CUR_PTR;
775         while ((IS_CHAR(CUR)) && (CUR != '"'))
776             NEXT;
777         if (!IS_CHAR(CUR)) {
778             /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
779             ctxt->error = 1;
780             return(NULL);
781         } else {
782             ret = xmlStrndup(q, CUR_PTR - q);
783             NEXT;
784         }
785     } else if (CUR == '\'') {
786         NEXT;
787         q = CUR_PTR;
788         while ((IS_CHAR(CUR)) && (CUR != '\''))
789             NEXT;
790         if (!IS_CHAR(CUR)) {
791             /* XP_ERROR(XPATH_UNFINISHED_LITERAL_ERROR); */
792             ctxt->error = 1;
793             return(NULL);
794         } else {
795             ret = xmlStrndup(q, CUR_PTR - q);
796             NEXT;
797         }
798     } else {
799         /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
800         ctxt->error = 1;
801         return(NULL);
802     }
803     return(ret);
804 }
805
806 /**
807  * xsltScanName:
808  * @ctxt:  the XPath Parser context
809  *
810  * Trickery: parse an XML name but without consuming the input flow
811  * Needed to avoid insanity in the parser state.
812  *
813  * [4] NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' |
814  *                  CombiningChar | Extender
815  *
816  * [5] Name ::= (Letter | '_' | ':') (NameChar)*
817  *
818  * [6] Names ::= Name (S Name)*
819  *
820  * Returns the Name parsed or NULL
821  */
822
823 xmlChar *
824 xsltScanName(xsltParserContextPtr ctxt) {
825     xmlChar buf[XML_MAX_NAMELEN];
826     int len = 0;
827
828     SKIP_BLANKS;
829     if (!IS_LETTER(CUR) && (CUR != '_') &&
830         (CUR != ':')) {
831         return(NULL);
832     }
833
834     while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
835            (NXT(len) == '.') || (NXT(len) == '-') ||
836            (NXT(len) == '_') || (NXT(len) == ':') || 
837            (IS_COMBINING(NXT(len))) ||
838            (IS_EXTENDER(NXT(len)))) {
839         buf[len] = NXT(len);
840         len++;
841         if (len >= XML_MAX_NAMELEN) {
842             xmlGenericError(xmlGenericErrorContext, 
843                "xmlScanName: reached XML_MAX_NAMELEN limit\n");
844             while ((IS_LETTER(NXT(len))) || (IS_DIGIT(NXT(len))) ||
845                    (NXT(len) == '.') || (NXT(len) == '-') ||
846                    (NXT(len) == '_') || (NXT(len) == ':') || 
847                    (IS_COMBINING(NXT(len))) ||
848                    (IS_EXTENDER(NXT(len))))
849                  len++;
850             break;
851         }
852     }
853     SKIP(len);
854     return(xmlStrndup(buf, len));
855 }
856 /*
857  * xsltCompileIdKeyPattern:
858  * @comp:  the compilation context
859  * @name:  a preparsed name
860  * @aid:  whether id/key are allowed there
861  *
862  * Compile the XSLT LocationIdKeyPattern
863  * [3] IdKeyPattern ::= 'id' '(' Literal ')'
864  *                    | 'key' '(' Literal ',' Literal ')'
865  *
866  * also handle NodeType and PI from:
867  *
868  * [7]  NodeTest ::= NameTest
869  *                 | NodeType '(' ')'
870  *                 | 'processing-instruction' '(' Literal ')'
871  */
872 void
873 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name, int aid) {
874     xmlChar *lit = NULL;
875     xmlChar *lit2 = NULL;
876
877     if (CUR != '(') {
878         xsltGenericError(xsltGenericErrorContext,
879                 "xsltCompileIdKeyPattern : ( expected\n");
880         ctxt->error = 1;
881         return;
882     }
883     if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
884         NEXT;
885         SKIP_BLANKS;
886         lit = xsltScanLiteral(ctxt);
887         if (ctxt->error)
888             return;
889         SKIP_BLANKS;
890         if (CUR != ')') {
891             xsltGenericError(xsltGenericErrorContext,
892                     "xsltCompileIdKeyPattern : ) expected\n");
893             ctxt->error = 1;
894             return;
895         }
896         NEXT;
897         PUSH(XSLT_OP_ID, lit, NULL);
898     } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
899         NEXT;
900         SKIP_BLANKS;
901         lit = xsltScanLiteral(ctxt);
902         if (ctxt->error)
903             return;
904         SKIP_BLANKS;
905         if (CUR != ',') {
906             xsltGenericError(xsltGenericErrorContext,
907                     "xsltCompileIdKeyPattern : , expected\n");
908             ctxt->error = 1;
909             return;
910         }
911         NEXT;
912         SKIP_BLANKS;
913         lit2 = xsltScanLiteral(ctxt);
914         if (ctxt->error)
915             return;
916         SKIP_BLANKS;
917         if (CUR != ')') {
918             xsltGenericError(xsltGenericErrorContext,
919                     "xsltCompileIdKeyPattern : ) expected\n");
920             ctxt->error = 1;
921             return;
922         }
923         NEXT;
924         /* TODO: support namespace in keys */
925         PUSH(XSLT_OP_KEY, lit, lit2);
926     } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
927         NEXT;
928         SKIP_BLANKS;
929         if (CUR != ')') {
930             lit = xsltScanLiteral(ctxt);
931             if (ctxt->error)
932                 return;
933             SKIP_BLANKS;
934             if (CUR != ')') {
935                 xsltGenericError(xsltGenericErrorContext,
936                         "xsltCompileIdKeyPattern : ) expected\n");
937                 ctxt->error = 1;
938                 return;
939             }
940         }
941         NEXT;
942         PUSH(XSLT_OP_PI, lit, NULL);
943     } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
944         NEXT;
945         SKIP_BLANKS;
946         if (CUR != ')') {
947             xsltGenericError(xsltGenericErrorContext,
948                     "xsltCompileIdKeyPattern : ) expected\n");
949             ctxt->error = 1;
950             return;
951         }
952         NEXT;
953         PUSH(XSLT_OP_TEXT, NULL, NULL);
954     } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
955         NEXT;
956         SKIP_BLANKS;
957         if (CUR != ')') {
958             xsltGenericError(xsltGenericErrorContext,
959                     "xsltCompileIdKeyPattern : ) expected\n");
960             ctxt->error = 1;
961             return;
962         }
963         NEXT;
964         PUSH(XSLT_OP_COMMENT, NULL, NULL);
965     } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
966         NEXT;
967         SKIP_BLANKS;
968         if (CUR != ')') {
969             xsltGenericError(xsltGenericErrorContext,
970                     "xsltCompileIdKeyPattern : ) expected\n");
971             ctxt->error = 1;
972             return;
973         }
974         NEXT;
975         PUSH(XSLT_OP_NODE, NULL, NULL);
976     } else if (aid) {
977         xsltGenericError(xsltGenericErrorContext,
978             "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
979         ctxt->error = 1;
980         return;
981     } else {
982         xsltGenericError(xsltGenericErrorContext,
983             "xsltCompileIdKeyPattern : node type\n");
984         ctxt->error = 1;
985         return;
986     }
987 error:
988     if (name != NULL)
989         xmlFree(name);
990 }
991
992 /**
993  * xsltCompileStepPattern:
994  * @comp:  the compilation context
995  * @token:  a posible precompiled name
996  *
997  * Compile the XSLT StepPattern and generates a precompiled
998  * form suitable for fast matching.
999  *
1000  * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* 
1001  * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1002  *                                     | ('child' | 'attribute') '::'
1003  * from XPath
1004  * [7]  NodeTest ::= NameTest
1005  *                 | NodeType '(' ')'
1006  *                 | 'processing-instruction' '(' Literal ')'
1007  * [8] Predicate ::= '[' PredicateExpr ']'
1008  * [9] PredicateExpr ::= Expr
1009  * [13] AbbreviatedAxisSpecifier ::= '@'?
1010  * [37] NameTest ::= '*' | NCName ':' '*' | QName
1011  */
1012
1013 void
1014 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token) {
1015     xmlChar *name = NULL;
1016     xmlChar *prefix = NULL;
1017     xmlChar *ncname = NULL;
1018     xmlChar *URL = NULL;
1019
1020     SKIP_BLANKS;
1021     if ((token == NULL) && (CUR == '@')) {
1022         NEXT;
1023         if (CUR == '*') {
1024             NEXT;
1025             PUSH(XSLT_OP_ATTR, NULL, NULL);
1026             return;
1027         }
1028         token = xsltScanName(ctxt);
1029         if (token == NULL) {
1030             xsltGenericError(xsltGenericErrorContext,
1031                     "xsltCompileStepPattern : Name expected\n");
1032             ctxt->error = 1;
1033             goto error;
1034         }
1035         PUSH(XSLT_OP_ATTR, token, NULL);
1036         return;
1037     }
1038     if (token == NULL)
1039         token = xsltScanName(ctxt);
1040     if (token == NULL) {
1041         if (CUR == '*') {
1042             NEXT;
1043             PUSH(XSLT_OP_ALL, token, NULL);
1044             goto parse_predicate;
1045         } else {
1046             xsltGenericError(xsltGenericErrorContext,
1047                     "xsltCompileStepPattern : Name expected\n");
1048             ctxt->error = 1;
1049             goto error;
1050         }
1051     }
1052
1053
1054     SKIP_BLANKS;
1055     if (CUR == '(') {
1056         xsltCompileIdKeyPattern(ctxt, token, 0);
1057         if (ctxt->error)
1058             goto error;
1059     } else if (CUR == ':') {
1060         NEXT;
1061         if (NXT(1) != ':') {
1062             xsltGenericError(xsltGenericErrorContext,
1063                     "xsltCompileStepPattern : sequence '::' expected\n");
1064             ctxt->error = 1;
1065             goto error;
1066         }
1067         NEXT;
1068         if (xmlStrEqual(token, (const xmlChar *) "child")) {
1069             name = xsltScanName(ctxt);
1070             if (name == NULL) {
1071                 xsltGenericError(xsltGenericErrorContext,
1072                         "xsltCompileStepPattern : QName expected\n");
1073                 ctxt->error = 1;
1074                 goto error;
1075             }
1076             ncname = xmlSplitQName2(name, &prefix);
1077             if (ncname != NULL) {
1078                 if (prefix != NULL) {
1079                     xmlNsPtr ns;
1080
1081                     ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1082                     if (ns == NULL) {
1083                         xsltGenericError(xsltGenericErrorContext,
1084                             "xsl: pattern, no namespace bound to prefix %s\n",
1085                                          prefix);
1086                     } else {
1087                         URL = xmlStrdup(ns->href);
1088                     }
1089                     xmlFree(prefix);
1090                 }
1091                 xmlFree(name);
1092                 name = ncname;
1093             }
1094             PUSH(XSLT_OP_CHILD, name, URL);
1095         } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1096             name = xsltScanName(ctxt);
1097             if (name == NULL) {
1098                 xsltGenericError(xsltGenericErrorContext,
1099                         "xsltCompileStepPattern : QName expected\n");
1100                 ctxt->error = 1;
1101                 goto error;
1102             }
1103             ncname = xmlSplitQName2(name, &prefix);
1104             if (ncname != NULL) {
1105                 if (prefix != NULL) {
1106                     xmlNsPtr ns;
1107
1108                     ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1109                     if (ns == NULL) {
1110                         xsltGenericError(xsltGenericErrorContext,
1111                             "xsl: pattern, no namespace bound to prefix %s\n",
1112                                          prefix);
1113                     } else {
1114                         URL = xmlStrdup(ns->href);
1115                     }
1116                     xmlFree(prefix);
1117                 }
1118                 xmlFree(name);
1119                 name = ncname;
1120             }
1121             PUSH(XSLT_OP_ATTR, name, URL);
1122         } else {
1123             xsltGenericError(xsltGenericErrorContext,
1124                 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1125             ctxt->error = 1;
1126             goto error;
1127         }
1128         xmlFree(token);
1129     } else if (CUR == '*') {
1130         NEXT;
1131         PUSH(XSLT_OP_ALL, token, NULL);
1132     } else {
1133         ncname = xmlSplitQName2(token, &prefix);
1134         if (ncname != NULL) {
1135             if (prefix != NULL) {
1136                 xmlNsPtr ns;
1137
1138                 ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1139                 if (ns == NULL) {
1140                     xsltGenericError(xsltGenericErrorContext,
1141                         "xsl: pattern, no namespace bound to prefix %s\n",
1142                                      prefix);
1143                 } else {
1144                     URL = xmlStrdup(ns->href);
1145                 }
1146                 xmlFree(prefix);
1147             }
1148             xmlFree(token);
1149             token = ncname;
1150         }
1151         PUSH(XSLT_OP_ELEM, token, URL);
1152     }
1153 parse_predicate:
1154     SKIP_BLANKS;
1155     while (CUR == '[') {
1156         const xmlChar *q;
1157         xmlChar *ret = NULL;
1158
1159         NEXT;
1160         q = CUR_PTR;
1161         /* TODO: avoid breaking in strings ... */
1162         while ((IS_CHAR(CUR)) && (CUR != ']'))
1163             NEXT;
1164         if (!IS_CHAR(CUR)) {
1165             xsltGenericError(xsltGenericErrorContext,
1166                     "xsltCompileStepPattern : ']' expected\n");
1167             ctxt->error = 1;
1168             goto error;
1169         }
1170         ret = xmlStrndup(q, CUR_PTR - q);
1171         PUSH(XSLT_OP_PREDICATE, ret, NULL);
1172         /* push the predicate lower than local test */
1173         SWAP();
1174         NEXT;
1175     }
1176     return;
1177 error:
1178     if (token != NULL)
1179         xmlFree(token);
1180     if (name != NULL)
1181         xmlFree(name);
1182 }
1183
1184 /**
1185  * xsltCompileRelativePathPattern:
1186  * @comp:  the compilation context
1187  * @token:  a posible precompiled name
1188  *
1189  * Compile the XSLT RelativePathPattern and generates a precompiled
1190  * form suitable for fast matching.
1191  *
1192  * [4] RelativePathPattern ::= StepPattern
1193  *                           | RelativePathPattern '/' StepPattern
1194  *                           | RelativePathPattern '//' StepPattern
1195  */
1196 void
1197 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token) {
1198     xsltCompileStepPattern(ctxt, token);
1199     if (ctxt->error)
1200         goto error;
1201     SKIP_BLANKS;
1202     while ((CUR != 0) && (CUR != '|')) {
1203         if ((CUR == '/') && (NXT(1) == '/')) {
1204             PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1205             NEXT;
1206             NEXT;
1207             SKIP_BLANKS;
1208             xsltCompileStepPattern(ctxt, NULL);
1209         } else if (CUR == '/') {
1210             PUSH(XSLT_OP_PARENT, NULL, NULL);
1211             NEXT;
1212             SKIP_BLANKS;
1213             if ((CUR != 0) || (CUR == '|')) {
1214                 xsltCompileRelativePathPattern(ctxt, NULL);
1215             }
1216         } else {
1217             ctxt->error = 1;
1218         }
1219         if (ctxt->error)
1220             goto error;
1221         SKIP_BLANKS;
1222     }
1223 error:
1224     return;
1225 }
1226
1227 /**
1228  * xsltCompileLocationPathPattern:
1229  * @comp:  the compilation context
1230  *
1231  * Compile the XSLT LocationPathPattern and generates a precompiled
1232  * form suitable for fast matching.
1233  *
1234  * [2] LocationPathPattern ::= '/' RelativePathPattern?
1235  *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
1236  *                           | '//'? RelativePathPattern
1237  */
1238 void
1239 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt) {
1240     SKIP_BLANKS;
1241     if ((CUR == '/') && (NXT(1) == '/')) {
1242         /*
1243          * since we reverse the query
1244          * a leading // can be safely ignored
1245          */
1246         NEXT;
1247         NEXT;
1248         xsltCompileRelativePathPattern(ctxt, NULL);
1249     } else if (CUR == '/') {
1250         /*
1251          * We need to find root as the parent
1252          */
1253         NEXT;
1254         SKIP_BLANKS;
1255         PUSH(XSLT_OP_ROOT, NULL, NULL);
1256         if ((CUR != 0) || (CUR == '|')) {
1257             PUSH(XSLT_OP_PARENT, NULL, NULL);
1258             xsltCompileRelativePathPattern(ctxt, NULL);
1259         }
1260     } else if (CUR == '*') {
1261         xsltCompileRelativePathPattern(ctxt, NULL);
1262     } else if (CUR == '@') {
1263         xsltCompileRelativePathPattern(ctxt, NULL);
1264     } else {
1265         xmlChar *name;
1266         name = xsltScanName(ctxt);
1267         if (name == NULL) {
1268             xsltGenericError(xsltGenericErrorContext,
1269                     "xsltCompileLocationPathPattern : Name expected\n");
1270             ctxt->error = 1;
1271             return;
1272         }
1273         SKIP_BLANKS;
1274         if (CUR == '(') {
1275             xsltCompileIdKeyPattern(ctxt, name, 1);
1276             if ((CUR == '/') && (NXT(1) == '/')) {
1277                 PUSH(XSLT_OP_ANCESTOR, NULL, NULL);
1278                 NEXT;
1279                 NEXT;
1280                 SKIP_BLANKS;
1281                 xsltCompileRelativePathPattern(ctxt, NULL);
1282             } else if (CUR == '/') {
1283                 PUSH(XSLT_OP_PARENT, NULL, NULL);
1284                 NEXT;
1285                 SKIP_BLANKS;
1286                 xsltCompileRelativePathPattern(ctxt, NULL);
1287             }
1288             return;
1289         }
1290         xsltCompileRelativePathPattern(ctxt, name);
1291     }
1292 error:
1293     return;
1294 }
1295
1296 /**
1297  * xsltCompilePattern:
1298  * @pattern an XSLT pattern
1299  * @doc:  the containing document
1300  * @node:  the containing element
1301  *
1302  * Compile the XSLT pattern and generates a list of precompiled form suitable
1303  * for fast matching.
1304  *
1305  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1306  *
1307  * Returns the generated pattern list or NULL in case of failure
1308  */
1309
1310 xsltCompMatchPtr
1311 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc, xmlNodePtr node) {
1312     xsltParserContextPtr ctxt = NULL;
1313     xsltCompMatchPtr element, first = NULL, previous = NULL;
1314     int current, start, end;
1315
1316     if (pattern == NULL) {
1317         xsltGenericError(xsltGenericErrorContext,
1318                          "xsltCompilePattern : NULL pattern\n");
1319         return(NULL);
1320     }
1321
1322 #ifdef DEBUG_PATTERN
1323     xsltGenericDebug(xsltGenericDebugContext,
1324                      "xsltCompilePattern : parsing '%s'\n", pattern);
1325 #endif
1326
1327     ctxt = xsltNewParserContext();
1328     if (ctxt == NULL)
1329         return(NULL);
1330     ctxt->doc = doc;
1331     ctxt->elem = node;
1332     current = end = 0;
1333     while (pattern[current] != 0) {
1334         start = current;
1335         while (IS_BLANK(pattern[current]))
1336             current++;
1337         end = current;
1338         while ((pattern[end] != 0) && (pattern[end] != '|'))
1339             end++;
1340         if (current == end) {
1341             xsltGenericError(xsltGenericErrorContext,
1342                              "xsltCompilePattern : NULL pattern\n");
1343             goto error;
1344         }
1345         element = xsltNewCompMatch();
1346         if (element == NULL) {
1347             goto error;
1348         }
1349         if (first == NULL)
1350             first = element;
1351         else if (previous != NULL)
1352             previous->next = element;
1353         previous = element;
1354
1355         ctxt->comp = element;
1356         ctxt->base = xmlStrndup(&pattern[start], end - start);
1357         ctxt->cur = &(ctxt->base)[current - start];
1358         xsltCompileLocationPathPattern(ctxt);
1359         if (ctxt->base)
1360             xmlFree((xmlChar *)ctxt->base);
1361         if (ctxt->error)
1362             goto error;
1363
1364         /*
1365          * Reverse for faster interpretation.
1366          */
1367         xsltReverseCompMatch(element);
1368
1369         /*
1370          * Set-up the priority
1371          */
1372         if (((element->steps[0].op == XSLT_OP_ELEM) ||
1373              (element->steps[0].op == XSLT_OP_ATTR)) &&
1374             (element->steps[0].value != NULL) &&
1375             (element->steps[1].op == XSLT_OP_END)) {
1376             element->priority = 0;
1377         } else if ((element->steps[0].op == XSLT_OP_ROOT) &&
1378                    (element->steps[1].op == XSLT_OP_END)) {
1379             element->priority = 0;
1380         } else if ((element->steps[0].op == XSLT_OP_PI) &&
1381                    (element->steps[0].value != NULL) &&
1382                    (element->steps[1].op == XSLT_OP_END)) {
1383             element->priority = 0;
1384         } else if ((element->steps[0].op == XSLT_OP_NS) &&
1385                    (element->steps[0].value != NULL) &&
1386                    (element->steps[1].op == XSLT_OP_END)) {
1387             element->priority = -0.25;
1388         } else if (((element->steps[0].op == XSLT_OP_PI) ||
1389                     (element->steps[0].op == XSLT_OP_TEXT) ||
1390                     (element->steps[0].op == XSLT_OP_ALL) ||
1391                     (element->steps[0].op == XSLT_OP_NODE) ||
1392                     (element->steps[0].op == XSLT_OP_COMMENT)) &&
1393                    (element->steps[1].op == XSLT_OP_END)) {
1394             element->priority = -0.5;
1395         } else {
1396             element->priority = 0.5;
1397         }
1398         if (pattern[end] == '|')
1399             end++;
1400         current = end;
1401     }
1402     if (end == 0) {
1403         xsltGenericError(xsltGenericErrorContext,
1404                          "xsltCompilePattern : NULL pattern\n");
1405         goto error;
1406     }
1407
1408     xsltFreeParserContext(ctxt);
1409     return(first);
1410
1411 error:
1412     if (ctxt != NULL)
1413         xsltFreeParserContext(ctxt);
1414     if (first != NULL)
1415         xsltFreeCompMatchList(first);
1416     return(NULL);
1417 }
1418
1419 /************************************************************************
1420  *                                                                      *
1421  *                      Module interfaces                               *
1422  *                                                                      *
1423  ************************************************************************/
1424
1425 /**
1426  * xsltAddTemplate:
1427  * @style: an XSLT stylesheet
1428  * @cur: an XSLT template
1429  * @mode:  the mode name or NULL
1430  * @modeURI:  the mode URI or NULL
1431  *
1432  * Register the XSLT pattern associated to @cur
1433  *
1434  * Returns -1 in case of error, 0 otherwise
1435  */
1436 int
1437 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
1438                 const xmlChar *mode, const xmlChar *modeURI) {
1439     xsltCompMatchPtr pat, list, *top = NULL, next;
1440     const xmlChar *name = NULL;
1441
1442     if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
1443         return(-1);
1444
1445     pat = xsltCompilePattern(cur->match, style->doc, cur->elem);
1446     while (pat) {
1447         next = pat->next;
1448         pat->next = NULL;
1449         
1450         pat->template = cur;
1451         if (mode != NULL)
1452             pat->mode = xmlStrdup(mode);
1453         if (modeURI != NULL)
1454             pat->modeURI = xmlStrdup(modeURI);
1455         if (cur->priority == XSLT_PAT_NO_PRIORITY)
1456             cur->priority = pat->priority;
1457         else
1458             pat->priority = cur->priority;
1459
1460         /*
1461          * insert it in the hash table list corresponding to its lookup name
1462          */
1463         switch (pat->steps[0].op) {
1464         case XSLT_OP_ATTR:
1465             if (pat->steps[0].value != NULL)
1466                 name = pat->steps[0].value;
1467             else
1468                 top = (xsltCompMatchPtr *) &(style->attrMatch);
1469             break;
1470         case XSLT_OP_ELEM:
1471         case XSLT_OP_CHILD:
1472         case XSLT_OP_PARENT:
1473         case XSLT_OP_ANCESTOR:
1474         case XSLT_OP_NS:
1475             name = pat->steps[0].value;
1476             break;
1477         case XSLT_OP_ROOT:
1478             top = (xsltCompMatchPtr *) &(style->rootMatch);
1479             break;
1480         case XSLT_OP_KEY:
1481             top = (xsltCompMatchPtr *) &(style->keyMatch);
1482             break;
1483         case XSLT_OP_ID:
1484             /* TODO optimize ID !!! */
1485         case XSLT_OP_ALL:
1486             top = (xsltCompMatchPtr *) &(style->elemMatch);
1487             break;
1488         case XSLT_OP_END:
1489         case XSLT_OP_PREDICATE:
1490             xsltGenericError(xsltGenericErrorContext,
1491                              "xsltAddTemplate: invalid compiled pattern\n");
1492             xsltFreeCompMatch(pat);
1493             return(-1);
1494             /*
1495              * TODO: some flags at the top level about type based patterns
1496              *       would be faster than inclusion in the hash table.
1497              */
1498         case XSLT_OP_PI:
1499             if (pat->steps[0].value != NULL)
1500                 name = pat->steps[0].value;
1501             else
1502                 top = (xsltCompMatchPtr *) &(style->piMatch);
1503             break;
1504         case XSLT_OP_COMMENT:
1505             top = (xsltCompMatchPtr *) &(style->commentMatch);
1506             break;
1507         case XSLT_OP_TEXT:
1508             top = (xsltCompMatchPtr *) &(style->textMatch);
1509             break;
1510         case XSLT_OP_NODE:
1511             if (pat->steps[0].value != NULL)
1512                 name = pat->steps[0].value;
1513             else
1514                 top = (xsltCompMatchPtr *) &(style->elemMatch);
1515             
1516             break;
1517         }
1518         if (name != NULL) {
1519             if (style->templatesHash == NULL) {
1520                 style->templatesHash = xmlHashCreate(1024);
1521                 if (style->templatesHash == NULL) {
1522                     xsltFreeCompMatch(pat);
1523                     return(-1);
1524                 }
1525                 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
1526             } else {
1527                 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
1528                                                          name, mode, modeURI);
1529                 if (list == NULL) {
1530                     xmlHashAddEntry3(style->templatesHash, name,
1531                                      mode, modeURI, pat);
1532                 } else {
1533                     /*
1534                      * Note '<=' since one must choose among the matching
1535                      * template rules that are left, the one that occurs
1536                      * last in the stylesheet
1537                      */
1538                     if (list->priority <= pat->priority) {
1539                         pat->next = list;
1540                         xmlHashUpdateEntry3(style->templatesHash, name,
1541                                             mode, modeURI, pat, NULL);
1542                     } else {
1543                         while (list->next != NULL) {
1544                             if (list->next->priority <= pat->priority)
1545                                 break;
1546                             list = list->next;
1547                         }
1548                         pat->next = list->next;
1549                         list->next = pat;
1550                     }
1551                 }
1552             }
1553         } else if (top != NULL) {
1554             list = *top;
1555             if (list == NULL) {
1556                 *top = pat;
1557                 pat->next = NULL;
1558             } else if (list->priority <= pat->priority) {
1559                 pat->next = list;
1560                 *top = pat;
1561             } else {
1562                 while (list->next != NULL) {
1563                     if (list->next->priority <= pat->priority)
1564                         break;
1565                     list = list->next;
1566                 }
1567                 pat->next = list->next;
1568                 list->next = pat;
1569             }
1570         } else {
1571             xsltGenericError(xsltGenericErrorContext,
1572                              "xsltAddTemplate: invalid compiled pattern\n");
1573             xsltFreeCompMatch(pat);
1574             return(-1);
1575         }
1576 #ifdef DEBUG_PATTERN
1577         xsltGenericDebug(xsltGenericDebugContext,
1578                      "added pattern : '%s' priority %f\n",
1579                          pat->template->match, pat->priority);
1580 #endif
1581
1582         pat = next;
1583     }
1584     return(0);
1585 }
1586
1587 /**
1588  * xsltGetTemplate:
1589  * @ctxt:  a XSLT process context
1590  * @mode:  the mode 
1591  * @style:  the current style
1592  *
1593  * Finds the template applying to this node, if @style is non-NULL
1594  * it means one need to look for the next imported template in scope.
1595  *
1596  * Returns the xsltTemplatePtr or NULL if not found
1597  */
1598 xsltTemplatePtr
1599 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
1600                 xsltStylesheetPtr style) {
1601     xsltStylesheetPtr curstyle;
1602     xsltTemplatePtr ret = NULL;
1603     const xmlChar *name = NULL;
1604     xsltCompMatchPtr list = NULL;
1605
1606     if ((ctxt == NULL) || (node == NULL))
1607         return(NULL);
1608
1609     if (style == NULL) {
1610         curstyle = ctxt->style;
1611     } else {
1612         curstyle = xsltNextImport(style);
1613     }
1614
1615     while ((curstyle != NULL) && (curstyle != style)) {
1616         /* TODO : handle IDs/keys here ! */
1617         if (curstyle->templatesHash != NULL) {
1618             /*
1619              * Use the top name as selector
1620              */
1621             switch (node->type) {
1622                 case XML_ELEMENT_NODE:
1623                 case XML_ATTRIBUTE_NODE:
1624                 case XML_PI_NODE:
1625                     name = node->name;
1626                     break;
1627                 case XML_DOCUMENT_NODE:
1628                 case XML_HTML_DOCUMENT_NODE:
1629                 case XML_TEXT_NODE:
1630                 case XML_CDATA_SECTION_NODE:
1631                 case XML_COMMENT_NODE:
1632                 case XML_ENTITY_REF_NODE:
1633                 case XML_ENTITY_NODE:
1634                 case XML_DOCUMENT_TYPE_NODE:
1635                 case XML_DOCUMENT_FRAG_NODE:
1636                 case XML_NOTATION_NODE:
1637                 case XML_DTD_NODE:
1638                 case XML_ELEMENT_DECL:
1639                 case XML_ATTRIBUTE_DECL:
1640                 case XML_ENTITY_DECL:
1641                 case XML_NAMESPACE_DECL:
1642                 case XML_XINCLUDE_START:
1643                 case XML_XINCLUDE_END:
1644                     break;
1645                 default:
1646                     return(NULL);
1647
1648             }
1649         }
1650         if (name != NULL) {
1651             /*
1652              * find the list of appliable expressions based on the name
1653              */
1654             list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
1655                                              name, ctxt->mode, ctxt->modeURI);
1656         }
1657         while (list != NULL) {
1658             if (xsltTestCompMatch(ctxt, list, node,
1659                                   ctxt->mode, ctxt->modeURI)) {
1660                 ret = list->template;
1661                 break;
1662             }
1663             list = list->next;
1664         }
1665         list = NULL;
1666
1667         /*
1668          * find alternate generic matches
1669          */
1670         switch (node->type) {
1671             case XML_ELEMENT_NODE:
1672                 list = curstyle->elemMatch;
1673                 break;
1674             case XML_ATTRIBUTE_NODE:
1675                 list = curstyle->attrMatch;
1676                 break;
1677             case XML_PI_NODE:
1678                 list = curstyle->piMatch;
1679                 break;
1680             case XML_DOCUMENT_NODE:
1681             case XML_HTML_DOCUMENT_NODE:
1682                 list = curstyle->rootMatch;
1683                 break;
1684             case XML_TEXT_NODE:
1685             case XML_CDATA_SECTION_NODE:
1686                 list = curstyle->textMatch;
1687                 break;
1688             case XML_COMMENT_NODE:
1689                 list = curstyle->commentMatch;
1690                 break;
1691             case XML_ENTITY_REF_NODE:
1692             case XML_ENTITY_NODE:
1693             case XML_DOCUMENT_TYPE_NODE:
1694             case XML_DOCUMENT_FRAG_NODE:
1695             case XML_NOTATION_NODE:
1696             case XML_DTD_NODE:
1697             case XML_ELEMENT_DECL:
1698             case XML_ATTRIBUTE_DECL:
1699             case XML_ENTITY_DECL:
1700             case XML_NAMESPACE_DECL:
1701             case XML_XINCLUDE_START:
1702             case XML_XINCLUDE_END:
1703                 break;
1704             default:
1705                 break;
1706
1707         }
1708         while ((list != NULL) &&
1709                ((ret == NULL)  || (list->priority > ret->priority))) {
1710             if (xsltTestCompMatch(ctxt, list, node,
1711                                   ctxt->mode, ctxt->modeURI)) {
1712                 ret = list->template;
1713                 break;
1714             }
1715             list = list->next;
1716         }
1717         if (node->_private != NULL) {
1718             list = curstyle->keyMatch;
1719             while ((list != NULL) &&
1720                    ((ret == NULL)  || (list->priority > ret->priority))) {
1721                 if (xsltTestCompMatch(ctxt, list, node,
1722                                       ctxt->mode, ctxt->modeURI)) {
1723                     ret = list->template;
1724                     break;
1725                 }
1726                 list = list->next;
1727             }
1728         }
1729         if (ret != NULL)
1730             return(ret);
1731
1732         /*
1733          * Cycle on next curstylesheet import.
1734          */
1735         curstyle = xsltNextImport(curstyle);
1736     }
1737     return(NULL);
1738 }
1739
1740
1741 /**
1742  * xsltFreeTemplateHashes:
1743  * @style: an XSLT stylesheet
1744  *
1745  * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
1746  */
1747 void
1748 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
1749     if (style->templatesHash != NULL)
1750         xmlHashFree((xmlHashTablePtr) style->templatesHash,
1751                     (xmlHashDeallocator) xsltFreeCompMatchList);
1752     if (style->rootMatch != NULL)
1753         xsltFreeCompMatchList(style->rootMatch);
1754     if (style->keyMatch != NULL)
1755         xsltFreeCompMatchList(style->keyMatch);
1756     if (style->elemMatch != NULL)
1757         xsltFreeCompMatchList(style->elemMatch);
1758     if (style->attrMatch != NULL)
1759         xsltFreeCompMatchList(style->attrMatch);
1760     if (style->parentMatch != NULL)
1761         xsltFreeCompMatchList(style->parentMatch);
1762     if (style->textMatch != NULL)
1763         xsltFreeCompMatchList(style->textMatch);
1764     if (style->piMatch != NULL)
1765         xsltFreeCompMatchList(style->piMatch);
1766     if (style->commentMatch != NULL)
1767         xsltFreeCompMatchList(style->commentMatch);
1768 }
1769
1770 #if 0
1771 /**
1772  * xsltMatchPattern
1773  * @node: a node in the source tree
1774  * @pattern: an XSLT pattern
1775  *
1776  * Determine if a node matches a pattern.
1777  */
1778 int
1779 xsltMatchPattern(xsltTransformContextPtr context,
1780                  xmlNodePtr node,
1781                  const xmlChar *pattern)
1782 {
1783     int match = 0;
1784     xsltCompMatchPtr first, comp;
1785
1786     if ((context != NULL) && (pattern != NULL)) {
1787         first = xsltCompilePattern(pattern);
1788         for (comp = first; comp != NULL; comp = comp->next) {
1789             match = xsltTestCompMatch(context, comp, node, NULL, NULL);
1790             if (match)
1791                 break; /* for */
1792         }
1793         if (first)
1794             xsltFreeCompMatchList(first);
1795     }
1796     return match;
1797 }
1798 #endif