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