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