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