- add sources.
[platform/framework/web/crosswalk.git] / src / third_party / libxslt / libxslt / pattern.c
1 /*
2  * pattern.c: Implemetation of the template match compilation and lookup
3  *
4  * Reference:
5  *   http://www.w3.org/TR/1999/REC-xslt-19991116
6  *
7  * See Copyright for the status of this software.
8  *
9  * daniel@veillard.com
10  */
11
12 /*
13  * TODO: handle pathological cases like *[*[@a="b"]]
14  * TODO: detect [number] at compilation, optimize accordingly
15  */
16
17 #define IN_LIBXSLT
18 #include "libxslt.h"
19
20 #include <string.h>
21
22 #include <libxml/xmlmemory.h>
23 #include <libxml/tree.h>
24 #include <libxml/valid.h>
25 #include <libxml/hash.h>
26 #include <libxml/xmlerror.h>
27 #include <libxml/parserInternals.h>
28 #include "xslt.h"
29 #include "xsltInternals.h"
30 #include "xsltutils.h"
31 #include "imports.h"
32 #include "templates.h"
33 #include "keys.h"
34 #include "pattern.h"
35 #include "documents.h"
36
37 #ifdef WITH_XSLT_DEBUG
38 #define WITH_XSLT_DEBUG_PATTERN
39 #endif
40
41 /*
42  * Types are private:
43  */
44
45 typedef enum {
46     XSLT_OP_END=0,
47     XSLT_OP_ROOT,
48     XSLT_OP_ELEM,
49     XSLT_OP_ATTR,
50     XSLT_OP_PARENT,
51     XSLT_OP_ANCESTOR,
52     XSLT_OP_ID,
53     XSLT_OP_KEY,
54     XSLT_OP_NS,
55     XSLT_OP_ALL,
56     XSLT_OP_PI,
57     XSLT_OP_COMMENT,
58     XSLT_OP_TEXT,
59     XSLT_OP_NODE,
60     XSLT_OP_PREDICATE
61 } xsltOp;
62
63 typedef enum {
64     AXIS_CHILD=1,
65     AXIS_ATTRIBUTE
66 } xsltAxis;
67
68 typedef struct _xsltStepState xsltStepState;
69 typedef xsltStepState *xsltStepStatePtr;
70 struct _xsltStepState {
71     int step;
72     xmlNodePtr node;
73 };
74
75 typedef struct _xsltStepStates xsltStepStates;
76 typedef xsltStepStates *xsltStepStatesPtr;
77 struct _xsltStepStates {
78     int nbstates;
79     int maxstates;
80     xsltStepStatePtr states;
81 };
82
83 typedef struct _xsltStepOp xsltStepOp;
84 typedef xsltStepOp *xsltStepOpPtr;
85 struct _xsltStepOp {
86     xsltOp op;
87     xmlChar *value;
88     xmlChar *value2;
89     xmlChar *value3;
90     xmlXPathCompExprPtr comp;
91     /*
92      * Optimisations for count
93      */
94     int        previousExtra;
95     int        indexExtra;
96     int        lenExtra;
97 };
98
99 struct _xsltCompMatch {
100     struct _xsltCompMatch *next; /* siblings in the name hash */
101     float priority;              /* the priority */
102     const xmlChar *pattern;       /* the pattern */
103     const xmlChar *mode;         /* the mode */
104     const xmlChar *modeURI;      /* the mode URI */
105     xsltTemplatePtr template;    /* the associated template */
106
107     int direct;
108     /* TODO fix the statically allocated size steps[] */
109     int nbStep;
110     int maxStep;
111     xmlNsPtr *nsList;           /* the namespaces in scope */
112     int nsNr;                   /* the number of namespaces in scope */
113     xsltStepOpPtr steps;        /* ops for computation */
114 };
115
116 typedef struct _xsltParserContext xsltParserContext;
117 typedef xsltParserContext *xsltParserContextPtr;
118 struct _xsltParserContext {
119     xsltStylesheetPtr style;            /* the stylesheet */
120     xsltTransformContextPtr ctxt;       /* the transformation or NULL */
121     const xmlChar *cur;                 /* the current char being parsed */
122     const xmlChar *base;                /* the full expression */
123     xmlDocPtr      doc;                 /* the source document */
124     xmlNodePtr    elem;                 /* the source element */
125     int error;                          /* error code */
126     xsltCompMatchPtr comp;              /* the result */
127 };
128
129 /************************************************************************
130  *                                                                      *
131  *                      Type functions                                  *
132  *                                                                      *
133  ************************************************************************/
134
135 /**
136  * xsltNewCompMatch:
137  *
138  * Create a new XSLT CompMatch
139  *
140  * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
141  */
142 static xsltCompMatchPtr
143 xsltNewCompMatch(void) {
144     xsltCompMatchPtr cur;
145
146     cur = (xsltCompMatchPtr) xmlMalloc(sizeof(xsltCompMatch));
147     if (cur == NULL) {
148         xsltTransformError(NULL, NULL, NULL,
149                 "xsltNewCompMatch : out of memory error\n");
150         return(NULL);
151     }
152     memset(cur, 0, sizeof(xsltCompMatch));
153     cur->maxStep = 10;
154     cur->nbStep = 0;
155     cur-> steps = (xsltStepOpPtr) xmlMalloc(sizeof(xsltStepOp) *
156                                             cur->maxStep);
157     if (cur->steps == NULL) {
158         xsltTransformError(NULL, NULL, NULL,
159                 "xsltNewCompMatch : out of memory error\n");
160         xmlFree(cur);
161         return(NULL);
162     }
163     cur->nsNr = 0;
164     cur->nsList = NULL;
165     cur->direct = 0;
166     return(cur);
167 }
168
169 /**
170  * xsltFreeCompMatch:
171  * @comp:  an XSLT comp
172  *
173  * Free up the memory allocated by @comp
174  */
175 static void
176 xsltFreeCompMatch(xsltCompMatchPtr comp) {
177     xsltStepOpPtr op;
178     int i;
179
180     if (comp == NULL)
181         return;
182     if (comp->pattern != NULL)
183         xmlFree((xmlChar *)comp->pattern);
184     if (comp->nsList != NULL)
185         xmlFree(comp->nsList);
186     for (i = 0;i < comp->nbStep;i++) {
187         op = &comp->steps[i];
188         if (op->value != NULL)
189             xmlFree(op->value);
190         if (op->value2 != NULL)
191             xmlFree(op->value2);
192         if (op->value3 != NULL)
193             xmlFree(op->value3);
194         if (op->comp != NULL)
195             xmlXPathFreeCompExpr(op->comp);
196     }
197     xmlFree(comp->steps);
198     memset(comp, -1, sizeof(xsltCompMatch));
199     xmlFree(comp);
200 }
201
202 /**
203  * xsltFreeCompMatchList:
204  * @comp:  an XSLT comp list
205  *
206  * Free up the memory allocated by all the elements of @comp
207  */
208 void
209 xsltFreeCompMatchList(xsltCompMatchPtr comp) {
210     xsltCompMatchPtr cur;
211
212     while (comp != NULL) {
213         cur = comp;
214         comp = comp->next;
215         xsltFreeCompMatch(cur);
216     }
217 }
218
219 /**
220  * xsltNormalizeCompSteps:
221  * @payload: pointer to template hash table entry
222  * @data: pointer to the stylesheet
223  * @name: template match name
224  *
225  * This is a hashtable scanner function to normalize the compiled
226  * steps of an imported stylesheet.
227  */
228 void xsltNormalizeCompSteps(void *payload,
229         void *data, const xmlChar *name ATTRIBUTE_UNUSED) {
230     xsltCompMatchPtr comp = payload;
231     xsltStylesheetPtr style = data;
232     int ix;
233
234     for (ix = 0; ix < comp->nbStep; ix++) {
235         comp->steps[ix].previousExtra += style->extrasNr;
236         comp->steps[ix].indexExtra += style->extrasNr;
237         comp->steps[ix].lenExtra += style->extrasNr;
238     }
239 }
240
241 /**
242  * xsltNewParserContext:
243  * @style:  the stylesheet
244  * @ctxt:  the transformation context, if done at run-time
245  *
246  * Create a new XSLT ParserContext
247  *
248  * Returns the newly allocated xsltParserContextPtr or NULL in case of error
249  */
250 static xsltParserContextPtr
251 xsltNewParserContext(xsltStylesheetPtr style, xsltTransformContextPtr ctxt) {
252     xsltParserContextPtr cur;
253
254     cur = (xsltParserContextPtr) xmlMalloc(sizeof(xsltParserContext));
255     if (cur == NULL) {
256         xsltTransformError(NULL, NULL, NULL,
257                 "xsltNewParserContext : malloc failed\n");
258         return(NULL);
259     }
260     memset(cur, 0, sizeof(xsltParserContext));
261     cur->style = style;
262     cur->ctxt = ctxt;
263     return(cur);
264 }
265
266 /**
267  * xsltFreeParserContext:
268  * @ctxt:  an XSLT parser context
269  *
270  * Free up the memory allocated by @ctxt
271  */
272 static void
273 xsltFreeParserContext(xsltParserContextPtr ctxt) {
274     if (ctxt == NULL)
275         return;
276     memset(ctxt, -1, sizeof(xsltParserContext));
277     xmlFree(ctxt);
278 }
279
280 /**
281  * xsltCompMatchAdd:
282  * @comp:  the compiled match expression
283  * @op:  an op
284  * @value:  the first value
285  * @value2:  the second value
286  * @novar:  flag to set XML_XPATH_NOVAR
287  *
288  * Add an step to an XSLT Compiled Match
289  *
290  * Returns -1 in case of failure, 0 otherwise.
291  */
292 static int
293 xsltCompMatchAdd(xsltParserContextPtr ctxt, xsltCompMatchPtr comp,
294                  xsltOp op, xmlChar * value, xmlChar * value2, int novar)
295 {
296     if (comp->nbStep >= comp->maxStep) {
297         xsltStepOpPtr tmp;
298
299         tmp = (xsltStepOpPtr) xmlRealloc(comp->steps, comp->maxStep * 2 *
300                                          sizeof(xsltStepOp));
301         if (tmp == NULL) {
302             xsltGenericError(xsltGenericErrorContext,
303              "xsltCompMatchAdd: memory re-allocation failure.\n");
304             if (ctxt->style != NULL)
305                 ctxt->style->errors++;
306             return (-1);
307         }
308         comp->maxStep *= 2;
309         comp->steps = tmp;
310     }
311     comp->steps[comp->nbStep].op = op;
312     comp->steps[comp->nbStep].value = value;
313     comp->steps[comp->nbStep].value2 = value2;
314     comp->steps[comp->nbStep].value3 = NULL;
315     comp->steps[comp->nbStep].comp = NULL;
316     if (ctxt->ctxt != NULL) {
317         comp->steps[comp->nbStep].previousExtra =
318             xsltAllocateExtraCtxt(ctxt->ctxt);
319         comp->steps[comp->nbStep].indexExtra =
320             xsltAllocateExtraCtxt(ctxt->ctxt);
321         comp->steps[comp->nbStep].lenExtra =
322             xsltAllocateExtraCtxt(ctxt->ctxt);
323     } else {
324         comp->steps[comp->nbStep].previousExtra =
325             xsltAllocateExtra(ctxt->style);
326         comp->steps[comp->nbStep].indexExtra =
327             xsltAllocateExtra(ctxt->style);
328         comp->steps[comp->nbStep].lenExtra =
329             xsltAllocateExtra(ctxt->style);
330     }
331     if (op == XSLT_OP_PREDICATE) {
332         xmlXPathContextPtr xctxt;
333
334         if (ctxt->style != NULL)
335             xctxt = xmlXPathNewContext(ctxt->style->doc);
336         else
337             xctxt = xmlXPathNewContext(NULL);
338 #ifdef XML_XPATH_NOVAR
339         if (novar != 0)
340             xctxt->flags = XML_XPATH_NOVAR;
341 #endif
342         if (ctxt->style != NULL)
343             xctxt->dict = ctxt->style->dict;
344         comp->steps[comp->nbStep].comp = xmlXPathCtxtCompile(xctxt, value);
345         xmlXPathFreeContext(xctxt);
346         if (comp->steps[comp->nbStep].comp == NULL) {
347             xsltTransformError(NULL, ctxt->style, ctxt->elem,
348                     "Failed to compile predicate\n");
349             if (ctxt->style != NULL)
350                 ctxt->style->errors++;
351         }
352     }
353     comp->nbStep++;
354     return (0);
355 }
356
357 /**
358  * xsltSwapTopCompMatch:
359  * @comp:  the compiled match expression
360  *
361  * reverse the two top steps.
362  */
363 static void
364 xsltSwapTopCompMatch(xsltCompMatchPtr comp) {
365     int i;
366     int j = comp->nbStep - 1;
367
368     if (j > 0) {
369         register xmlChar *tmp;
370         register xsltOp op;
371         register xmlXPathCompExprPtr expr; 
372         register int t;
373         i = j - 1;
374         tmp = comp->steps[i].value;
375         comp->steps[i].value = comp->steps[j].value;
376         comp->steps[j].value = tmp;
377         tmp = comp->steps[i].value2;
378         comp->steps[i].value2 = comp->steps[j].value2;
379         comp->steps[j].value2 = tmp;
380         tmp = comp->steps[i].value3;
381         comp->steps[i].value3 = comp->steps[j].value3;
382         comp->steps[j].value3 = tmp;
383         op = comp->steps[i].op;
384         comp->steps[i].op = comp->steps[j].op;
385         comp->steps[j].op = op;
386         expr = comp->steps[i].comp;
387         comp->steps[i].comp = comp->steps[j].comp;
388         comp->steps[j].comp = expr;
389         t = comp->steps[i].previousExtra;
390         comp->steps[i].previousExtra = comp->steps[j].previousExtra;
391         comp->steps[j].previousExtra = t;
392         t = comp->steps[i].indexExtra;
393         comp->steps[i].indexExtra = comp->steps[j].indexExtra;
394         comp->steps[j].indexExtra = t;
395         t = comp->steps[i].lenExtra;
396         comp->steps[i].lenExtra = comp->steps[j].lenExtra;
397         comp->steps[j].lenExtra = t;
398     }
399 }
400
401 /**
402  * xsltReverseCompMatch:
403  * @ctxt: the parser context
404  * @comp:  the compiled match expression
405  *
406  * reverse all the stack of expressions
407  */
408 static void
409 xsltReverseCompMatch(xsltParserContextPtr ctxt, xsltCompMatchPtr comp) {
410     int i = 0;
411     int j = comp->nbStep - 1;
412
413     while (j > i) {
414         register xmlChar *tmp;
415         register xsltOp op;
416         register xmlXPathCompExprPtr expr;
417         register int t;
418
419         tmp = comp->steps[i].value;
420         comp->steps[i].value = comp->steps[j].value;
421         comp->steps[j].value = tmp;
422         tmp = comp->steps[i].value2;
423         comp->steps[i].value2 = comp->steps[j].value2;
424         comp->steps[j].value2 = tmp;
425         tmp = comp->steps[i].value3;
426         comp->steps[i].value3 = comp->steps[j].value3;
427         comp->steps[j].value3 = tmp;
428         op = comp->steps[i].op;
429         comp->steps[i].op = comp->steps[j].op;
430         comp->steps[j].op = op;
431         expr = comp->steps[i].comp;
432         comp->steps[i].comp = comp->steps[j].comp;
433         comp->steps[j].comp = expr;
434         t = comp->steps[i].previousExtra;
435         comp->steps[i].previousExtra = comp->steps[j].previousExtra;
436         comp->steps[j].previousExtra = t;
437         t = comp->steps[i].indexExtra;
438         comp->steps[i].indexExtra = comp->steps[j].indexExtra;
439         comp->steps[j].indexExtra = t;
440         t = comp->steps[i].lenExtra;
441         comp->steps[i].lenExtra = comp->steps[j].lenExtra;
442         comp->steps[j].lenExtra = t;
443         j--;
444         i++;
445     }
446     xsltCompMatchAdd(ctxt, comp, XSLT_OP_END, NULL, NULL, 0);
447
448     /*
449      * detect consecutive XSLT_OP_PREDICATE indicating a direct
450      * matching should be done.
451      */
452     for (i = 0;i < comp->nbStep - 1;i++) {
453         if ((comp->steps[i].op == XSLT_OP_PREDICATE) &&
454             (comp->steps[i + 1].op == XSLT_OP_PREDICATE)) {
455
456             comp->direct = 1;
457             if (comp->pattern[0] != '/') {
458                 xmlChar *query;
459
460                 query = xmlStrdup((const xmlChar *)"//");
461                 query = xmlStrcat(query, comp->pattern);
462
463                 xmlFree((xmlChar *) comp->pattern);
464                 comp->pattern = query;
465             }
466             break;
467         }
468     }
469 }
470
471 /************************************************************************
472  *                                                                      *
473  *              The interpreter for the precompiled patterns            *
474  *                                                                      *
475  ************************************************************************/
476
477 static int
478 xsltPatPushState(xsltTransformContextPtr ctxt, xsltStepStates *states,
479                  int step, xmlNodePtr node) {
480     if ((states->states == NULL) || (states->maxstates <= 0)) {
481         states->maxstates = 4;
482         states->nbstates = 0;
483         states->states = xmlMalloc(4 * sizeof(xsltStepState));
484     }
485     else if (states->maxstates <= states->nbstates) {
486         xsltStepState *tmp;
487
488         tmp = (xsltStepStatePtr) xmlRealloc(states->states,
489                                2 * states->maxstates * sizeof(xsltStepState));
490         if (tmp == NULL) {
491             xsltGenericError(xsltGenericErrorContext,
492              "xsltPatPushState: memory re-allocation failure.\n");
493             ctxt->state = XSLT_STATE_STOPPED;
494             return(-1);
495         }
496         states->states = tmp;
497         states->maxstates *= 2;
498     }
499     states->states[states->nbstates].step = step;
500     states->states[states->nbstates++].node = node;
501 #if 0
502     fprintf(stderr, "Push: %d, %s\n", step, node->name);
503 #endif
504     return(0);
505 }
506
507 /**
508  * xsltTestCompMatchDirect:
509  * @ctxt:  a XSLT process context
510  * @comp: the precompiled pattern
511  * @node: a node
512  * @nsList: the namespaces in scope
513  * @nsNr: the number of namespaces in scope
514  *
515  * Test whether the node matches the pattern, do a direct evalutation
516  * and not a step by step evaluation.
517  *
518  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
519  */
520 static int
521 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
522                         xmlNodePtr node, xmlNsPtr *nsList, int nsNr) {
523     xsltStepOpPtr sel = NULL;
524     xmlDocPtr prevdoc;
525     xmlDocPtr doc;
526     xmlXPathObjectPtr list;
527     int ix, j;
528     int nocache = 0;
529     int isRVT;
530
531     doc = node->doc;
532     if (XSLT_IS_RES_TREE_FRAG(doc))
533         isRVT = 1;
534     else
535         isRVT = 0;
536     sel = &comp->steps[0]; /* store extra in first step arbitrarily */
537
538     prevdoc = (xmlDocPtr)
539         XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
540     ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
541     list = (xmlXPathObjectPtr)
542         XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra);
543     
544     if ((list == NULL) || (prevdoc != doc)) {
545         xmlXPathObjectPtr newlist;
546         xmlNodePtr parent = node->parent;
547         xmlDocPtr olddoc;
548         xmlNodePtr oldnode;
549         int oldNsNr;
550         xmlNsPtr *oldNamespaces;
551
552         oldnode = ctxt->xpathCtxt->node;
553         olddoc = ctxt->xpathCtxt->doc;
554         oldNsNr = ctxt->xpathCtxt->nsNr;
555         oldNamespaces = ctxt->xpathCtxt->namespaces;
556         ctxt->xpathCtxt->node = node;
557         ctxt->xpathCtxt->doc = doc;
558         ctxt->xpathCtxt->namespaces = nsList;
559         ctxt->xpathCtxt->nsNr = nsNr;
560         newlist = xmlXPathEval(comp->pattern, ctxt->xpathCtxt);
561         ctxt->xpathCtxt->node = oldnode;
562         ctxt->xpathCtxt->doc = olddoc;
563         ctxt->xpathCtxt->namespaces = oldNamespaces;
564         ctxt->xpathCtxt->nsNr = oldNsNr;
565         if (newlist == NULL)
566             return(-1);
567         if (newlist->type != XPATH_NODESET) {
568             xmlXPathFreeObject(newlist);
569             return(-1);
570         }
571         ix = 0;
572
573         if ((parent == NULL) || (node->doc == NULL) || isRVT)
574             nocache = 1;
575         
576         if (nocache == 0) {
577             if (list != NULL)
578                 xmlXPathFreeObject(list);
579             list = newlist;
580
581             XSLT_RUNTIME_EXTRA_LST(ctxt, sel->lenExtra) =
582                 (void *) list;
583             XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
584                 (void *) doc;
585             XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
586                 0;
587             XSLT_RUNTIME_EXTRA_FREE(ctxt, sel->lenExtra) =
588                 (xmlFreeFunc) xmlXPathFreeObject;
589         } else
590             list = newlist;
591     }
592     if ((list->nodesetval == NULL) ||
593         (list->nodesetval->nodeNr <= 0)) {
594         if (nocache == 1)
595             xmlXPathFreeObject(list);
596         return(0);
597     }
598     /* TODO: store the index and use it for the scan */
599     if (ix == 0) {
600         for (j = 0;j < list->nodesetval->nodeNr;j++) {
601             if (list->nodesetval->nodeTab[j] == node) {
602                 if (nocache == 1)
603                     xmlXPathFreeObject(list);
604                 return(1);
605             }
606         }
607     } else {
608     }
609     if (nocache == 1)
610         xmlXPathFreeObject(list);
611     return(0);
612 }
613
614 /**
615  * xsltTestCompMatch:
616  * @ctxt:  a XSLT process context
617  * @comp: the precompiled pattern
618  * @node: a node
619  * @mode:  the mode name or NULL
620  * @modeURI:  the mode URI or NULL
621  *
622  * Test whether the node matches the pattern
623  *
624  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
625  */
626 static int
627 xsltTestCompMatch(xsltTransformContextPtr ctxt, xsltCompMatchPtr comp,
628                   xmlNodePtr node, const xmlChar *mode,
629                   const xmlChar *modeURI) {
630     int i;
631     xsltStepOpPtr step, sel = NULL;
632     xsltStepStates states = {0, 0, NULL}; /* // may require backtrack */
633
634     if ((comp == NULL) || (node == NULL) || (ctxt == NULL)) {
635         xsltTransformError(ctxt, NULL, node,
636                 "xsltTestCompMatch: null arg\n");
637         return(-1);
638     }
639     if (mode != NULL) {
640         if (comp->mode == NULL)
641             return(0);
642         /*
643          * both mode strings must be interned on the stylesheet dictionary
644          */
645         if (comp->mode != mode)
646             return(0);
647     } else {
648         if (comp->mode != NULL)
649             return(0);
650     }
651     if (modeURI != NULL) {
652         if (comp->modeURI == NULL)
653             return(0);
654         /*
655          * both modeURI strings must be interned on the stylesheet dictionary
656          */
657         if (comp->modeURI != modeURI)
658             return(0);
659     } else {
660         if (comp->modeURI != NULL)
661             return(0);
662     }
663
664     i = 0;
665 restart:
666     for (;i < comp->nbStep;i++) {
667         step = &comp->steps[i];
668         if (step->op != XSLT_OP_PREDICATE)
669             sel = step;
670         switch (step->op) {
671             case XSLT_OP_END:
672                 goto found;
673             case XSLT_OP_ROOT:
674                 if ((node->type == XML_DOCUMENT_NODE) ||
675 #ifdef LIBXML_DOCB_ENABLED
676                     (node->type == XML_DOCB_DOCUMENT_NODE) ||
677 #endif
678                     (node->type == XML_HTML_DOCUMENT_NODE))
679                     continue;
680                 if ((node->type == XML_ELEMENT_NODE) && (node->name[0] == ' '))
681                     continue;
682                 goto rollback;
683             case XSLT_OP_ELEM:
684                 if (node->type != XML_ELEMENT_NODE)
685                     goto rollback;
686                 if (step->value == NULL)
687                     continue;
688                 if (step->value[0] != node->name[0])
689                     goto rollback;
690                 if (!xmlStrEqual(step->value, node->name))
691                     goto rollback;
692
693                 /* Namespace test */
694                 if (node->ns == NULL) {
695                     if (step->value2 != NULL)
696                         goto rollback;
697                 } else if (node->ns->href != NULL) {
698                     if (step->value2 == NULL)
699                         goto rollback;
700                     if (!xmlStrEqual(step->value2, node->ns->href))
701                         goto rollback;
702                 }
703                 continue;
704             case XSLT_OP_ATTR:
705                 if (node->type != XML_ATTRIBUTE_NODE)
706                     goto rollback;
707                 if (step->value != NULL) {
708                     if (step->value[0] != node->name[0])
709                         goto rollback;
710                     if (!xmlStrEqual(step->value, node->name))
711                         goto rollback;
712                 }
713                 /* Namespace test */
714                 if (node->ns == NULL) {
715                     if (step->value2 != NULL)
716                         goto rollback;
717                 } else if (step->value2 != NULL) {
718                     if (!xmlStrEqual(step->value2, node->ns->href))
719                         goto rollback;
720                 }
721                 continue;
722             case XSLT_OP_PARENT:
723                 if ((node->type == XML_DOCUMENT_NODE) ||
724                     (node->type == XML_HTML_DOCUMENT_NODE) ||
725 #ifdef LIBXML_DOCB_ENABLED
726                     (node->type == XML_DOCB_DOCUMENT_NODE) ||
727 #endif
728                     (node->type == XML_NAMESPACE_DECL))
729                     goto rollback;
730                 node = node->parent;
731                 if (node == NULL)
732                     goto rollback;
733                 if (step->value == NULL)
734                     continue;
735                 if (step->value[0] != node->name[0])
736                     goto rollback;
737                 if (!xmlStrEqual(step->value, node->name))
738                     goto rollback;
739                 /* Namespace test */
740                 if (node->ns == NULL) {
741                     if (step->value2 != NULL)
742                         goto rollback;
743                 } else if (node->ns->href != NULL) {
744                     if (step->value2 == NULL)
745                         goto rollback;
746                     if (!xmlStrEqual(step->value2, node->ns->href))
747                         goto rollback;
748                 }
749                 continue;
750             case XSLT_OP_ANCESTOR:
751                 /* TODO: implement coalescing of ANCESTOR/NODE ops */
752                 if (step->value == NULL) {
753                     step = &comp->steps[i+1];
754                     if (step->op == XSLT_OP_ROOT)
755                         goto found;
756                     /* added NS, ID and KEY as a result of bug 168208 */
757                     if ((step->op != XSLT_OP_ELEM) && 
758                         (step->op != XSLT_OP_ALL) && 
759                         (step->op != XSLT_OP_NS) &&
760                         (step->op != XSLT_OP_ID) &&
761                         (step->op != XSLT_OP_KEY))
762                         goto rollback;
763                 }
764                 if (node == NULL)
765                     goto rollback;
766                 if ((node->type == XML_DOCUMENT_NODE) ||
767                     (node->type == XML_HTML_DOCUMENT_NODE) ||
768 #ifdef LIBXML_DOCB_ENABLED
769                     (node->type == XML_DOCB_DOCUMENT_NODE) ||
770 #endif
771                     (node->type == XML_NAMESPACE_DECL))
772                     goto rollback;
773                 node = node->parent;
774                 if ((step->op != XSLT_OP_ELEM) && step->op != XSLT_OP_ALL) {
775                     xsltPatPushState(ctxt, &states, i, node);
776                     continue;
777                 }
778                 i++;
779                 if (step->value == NULL) {
780                     xsltPatPushState(ctxt, &states, i - 1, node);
781                     continue;
782                 }
783                 while (node != NULL) {
784                     if ((node->type == XML_ELEMENT_NODE) &&
785                         (step->value[0] == node->name[0]) &&
786                         (xmlStrEqual(step->value, node->name))) {
787                         /* Namespace test */
788                         if (node->ns == NULL) {
789                             if (step->value2 == NULL)
790                                 break;
791                         } else if (node->ns->href != NULL) {
792                             if ((step->value2 != NULL) &&
793                                 (xmlStrEqual(step->value2, node->ns->href)))
794                                 break;
795                         }
796                     }
797                     node = node->parent;
798                 }
799                 if (node == NULL)
800                     goto rollback;
801                 xsltPatPushState(ctxt, &states, i - 1, node);
802                 continue;
803             case XSLT_OP_ID: {
804                 /* TODO Handle IDs decently, must be done differently */
805                 xmlAttrPtr id;
806
807                 if (node->type != XML_ELEMENT_NODE)
808                     goto rollback;
809
810                 id = xmlGetID(node->doc, step->value);
811                 if ((id == NULL) || (id->parent != node))
812                     goto rollback;
813                 break;
814             }
815             case XSLT_OP_KEY: {
816                 xmlNodeSetPtr list;
817                 int indx;
818
819                 list = xsltGetKey(ctxt, step->value,
820                                   step->value3, step->value2);
821                 if (list == NULL)
822                     goto rollback;
823                 for (indx = 0;indx < list->nodeNr;indx++)
824                     if (list->nodeTab[indx] == node)
825                         break;
826                 if (indx >= list->nodeNr)
827                     goto rollback;
828                 break;
829             }
830             case XSLT_OP_NS:
831                 if (node->type != XML_ELEMENT_NODE)
832                     goto rollback;
833                 if (node->ns == NULL) {
834                     if (step->value != NULL)
835                         goto rollback;
836                 } else if (node->ns->href != NULL) {
837                     if (step->value == NULL)
838                         goto rollback;
839                     if (!xmlStrEqual(step->value, node->ns->href))
840                         goto rollback;
841                 }
842                 break;
843             case XSLT_OP_ALL:
844                 if (node->type != XML_ELEMENT_NODE)
845                     goto rollback;
846                 break;
847             case XSLT_OP_PREDICATE: {
848                 xmlNodePtr oldNode;
849                 xmlDocPtr doc;
850                 int oldCS, oldCP;
851                 int pos = 0, len = 0;
852                 int isRVT;
853
854                 /*
855                  * when there is cascading XSLT_OP_PREDICATE, then use a
856                  * direct computation approach. It's not done directly
857                  * at the beginning of the routine to filter out as much
858                  * as possible this costly computation.
859                  */
860                 if (comp->direct) {
861                     if (states.states != NULL) {
862                         /* Free the rollback states */
863                         xmlFree(states.states);
864                     }
865                     return(xsltTestCompMatchDirect(ctxt, comp, node,
866                                                    comp->nsList, comp->nsNr));
867                 }
868
869                 doc = node->doc;
870                 if (XSLT_IS_RES_TREE_FRAG(doc))
871                     isRVT = 1;
872                 else
873                     isRVT = 0;
874
875                 /*
876                  * Depending on the last selection, one may need to
877                  * recompute contextSize and proximityPosition.
878                  */
879                 oldCS = ctxt->xpathCtxt->contextSize;
880                 oldCP = ctxt->xpathCtxt->proximityPosition;
881                 if ((sel != NULL) &&
882                     (sel->op == XSLT_OP_ELEM) &&
883                     (sel->value != NULL) &&
884                     (node->type == XML_ELEMENT_NODE) &&
885                     (node->parent != NULL)) {
886                     xmlNodePtr previous;
887                     int ix, nocache = 0;
888
889                     previous = (xmlNodePtr)
890                         XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
891                     ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
892                     if ((previous != NULL) &&
893                         (previous->parent == node->parent)) {
894                         /*
895                          * just walk back to adjust the index
896                          */
897                         int indx = 0;
898                         xmlNodePtr sibling = node;
899
900                         while (sibling != NULL) {
901                             if (sibling == previous)
902                                 break;
903                             if ((previous->type == XML_ELEMENT_NODE) &&
904                                 (previous->name != NULL) &&
905                                 (sibling->name != NULL) &&
906                                 (previous->name[0] == sibling->name[0]) &&
907                                 (xmlStrEqual(previous->name, sibling->name)))
908                             {
909                                 if ((sel->value2 == NULL) ||
910                                     ((sibling->ns != NULL) &&
911                                      (xmlStrEqual(sel->value2,
912                                                   sibling->ns->href))))
913                                     indx++;
914                             }
915                             sibling = sibling->prev;
916                         }
917                         if (sibling == NULL) {
918                             /* hum going backward in document order ... */
919                             indx = 0;
920                             sibling = node;
921                             while (sibling != NULL) {
922                                 if (sibling == previous)
923                                     break;
924                                 if ((previous->type == XML_ELEMENT_NODE) &&
925                                     (previous->name != NULL) &&
926                                     (sibling->name != NULL) &&
927                                     (previous->name[0] == sibling->name[0]) &&
928                                     (xmlStrEqual(previous->name, sibling->name)))
929                                 {
930                                     if ((sel->value2 == NULL) ||
931                                         ((sibling->ns != NULL) &&
932                                         (xmlStrEqual(sel->value2,
933                                         sibling->ns->href))))
934                                     {
935                                         indx--;
936                                     }
937                                 }
938                                 sibling = sibling->next;
939                             }
940                         }
941                         if (sibling != NULL) {
942                             pos = ix + indx;
943                             /*
944                              * If the node is in a Value Tree we need to
945                              * save len, but cannot cache the node!
946                              * (bugs 153137 and 158840)
947                              */
948                             if (node->doc != NULL) {
949                                 len = XSLT_RUNTIME_EXTRA(ctxt,
950                                         sel->lenExtra, ival);
951                                 if (!isRVT) {
952                                     XSLT_RUNTIME_EXTRA(ctxt,
953                                         sel->previousExtra, ptr) = node;
954                                     XSLT_RUNTIME_EXTRA(ctxt,
955                                         sel->indexExtra, ival) = pos;
956                                 }
957                             }
958                             ix = pos;
959                         } else
960                             pos = 0;
961                     } else {
962                         /*
963                          * recompute the index
964                          */
965                         xmlNodePtr parent = node->parent;
966                         xmlNodePtr siblings = NULL;
967
968                         if (parent) siblings = parent->children;
969
970                         while (siblings != NULL) {
971                             if (siblings->type == XML_ELEMENT_NODE) {
972                                 if (siblings == node) {
973                                     len++;
974                                     pos = len;
975                                 } else if ((node->name != NULL) &&
976                                            (siblings->name != NULL) &&
977                                     (node->name[0] == siblings->name[0]) &&
978                                     (xmlStrEqual(node->name, siblings->name))) {
979                                     if ((sel->value2 == NULL) ||
980                                         ((siblings->ns != NULL) &&
981                                          (xmlStrEqual(sel->value2,
982                                                       siblings->ns->href))))
983                                         len++;
984                                 }
985                             }
986                             siblings = siblings->next;
987                         }
988                         if ((parent == NULL) || (node->doc == NULL))
989                             nocache = 1;
990                         else {
991                             while (parent->parent != NULL)
992                                 parent = parent->parent;
993                             if (((parent->type != XML_DOCUMENT_NODE) &&
994                                  (parent->type != XML_HTML_DOCUMENT_NODE)) ||
995                                  (parent != (xmlNodePtr) node->doc))
996                                 nocache = 1;
997                         }
998                     }
999                     if (pos != 0) {
1000                         ctxt->xpathCtxt->contextSize = len;
1001                         ctxt->xpathCtxt->proximityPosition = pos;
1002                         /*
1003                          * If the node is in a Value Tree we cannot
1004                          * cache it !
1005                          */
1006                         if ((!isRVT) && (node->doc != NULL) &&
1007                             (nocache == 0)) {
1008                             XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
1009                                 node;
1010                             XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
1011                                 pos;
1012                             XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
1013                                 len;
1014                         }
1015                     }
1016                 } else if ((sel != NULL) && (sel->op == XSLT_OP_ALL) &&
1017                            (node->type == XML_ELEMENT_NODE)) {
1018                     xmlNodePtr previous;
1019                     int ix, nocache = 0;
1020
1021                     previous = (xmlNodePtr)
1022                         XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr);
1023                     ix = XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival);
1024                     if ((previous != NULL) &&
1025                         (previous->parent == node->parent)) {
1026                         /*
1027                          * just walk back to adjust the index
1028                          */
1029                         int indx = 0;
1030                         xmlNodePtr sibling = node;
1031
1032                         while (sibling != NULL) {
1033                             if (sibling == previous)
1034                                 break;
1035                             if (sibling->type == XML_ELEMENT_NODE)
1036                                 indx++;
1037                             sibling = sibling->prev;
1038                         }
1039                         if (sibling == NULL) {
1040                             /* hum going backward in document order ... */
1041                             indx = 0;
1042                             sibling = node;
1043                             while (sibling != NULL) {
1044                                 if (sibling == previous)
1045                                     break;
1046                                 if (sibling->type == XML_ELEMENT_NODE)
1047                                     indx--;
1048                                 sibling = sibling->next;
1049                             }
1050                         }
1051                         if (sibling != NULL) {
1052                             pos = ix + indx;
1053                             /*
1054                              * If the node is in a Value Tree we cannot
1055                              * cache it !
1056                              */
1057                             if ((node->doc != NULL) && !isRVT) {
1058                                 len = XSLT_RUNTIME_EXTRA(ctxt,
1059                                         sel->lenExtra, ival);
1060                                 XSLT_RUNTIME_EXTRA(ctxt,
1061                                         sel->previousExtra, ptr) = node;
1062                                 XSLT_RUNTIME_EXTRA(ctxt,
1063                                         sel->indexExtra, ival) = pos;
1064                             }
1065                         } else
1066                             pos = 0;
1067                     } else {
1068                         /*
1069                          * recompute the index
1070                          */
1071                         xmlNodePtr parent = node->parent;
1072                         xmlNodePtr siblings = NULL;
1073
1074                         if (parent) siblings = parent->children;
1075
1076                         while (siblings != NULL) {
1077                             if (siblings->type == XML_ELEMENT_NODE) {
1078                                 len++;
1079                                 if (siblings == node) {
1080                                     pos = len;
1081                                 }
1082                             }
1083                             siblings = siblings->next;
1084                         }
1085                         if ((parent == NULL) || (node->doc == NULL))
1086                             nocache = 1;
1087                         else {
1088                             while (parent->parent != NULL)
1089                                 parent = parent->parent;
1090                             if (((parent->type != XML_DOCUMENT_NODE) &&
1091                                  (parent->type != XML_HTML_DOCUMENT_NODE)) ||
1092                                  (parent != (xmlNodePtr) node->doc))
1093                                 nocache = 1;
1094                         }
1095                     }
1096                     if (pos != 0) {
1097                         ctxt->xpathCtxt->contextSize = len;
1098                         ctxt->xpathCtxt->proximityPosition = pos;
1099                         /*
1100                          * If the node is in a Value Tree we cannot
1101                          * cache it !
1102                          */
1103                         if ((node->doc != NULL) && (nocache == 0) && !isRVT) {
1104                             XSLT_RUNTIME_EXTRA(ctxt, sel->previousExtra, ptr) =
1105                                 node;
1106                             XSLT_RUNTIME_EXTRA(ctxt, sel->indexExtra, ival) =
1107                                 pos;
1108                             XSLT_RUNTIME_EXTRA(ctxt, sel->lenExtra, ival) =
1109                                 len;
1110                         }
1111                     }
1112                 }
1113                 oldNode = ctxt->node;
1114                 ctxt->node = node;
1115
1116                 if (step->value == NULL)
1117                     goto wrong_index;
1118                 if (step->comp == NULL)
1119                     goto wrong_index;
1120
1121                 if (!xsltEvalXPathPredicate(ctxt, step->comp, comp->nsList,
1122                                             comp->nsNr))
1123                     goto wrong_index;
1124
1125                 if (pos != 0) {
1126                     ctxt->xpathCtxt->contextSize = oldCS;
1127                     ctxt->xpathCtxt->proximityPosition = oldCP;
1128                 }
1129                 ctxt->node = oldNode;
1130                 break;
1131 wrong_index:
1132                 if (pos != 0) {
1133                     ctxt->xpathCtxt->contextSize = oldCS;
1134                     ctxt->xpathCtxt->proximityPosition = oldCP;
1135                 }
1136                 ctxt->node = oldNode;
1137                 goto rollback;
1138             }
1139             case XSLT_OP_PI:
1140                 if (node->type != XML_PI_NODE)
1141                     goto rollback;
1142                 if (step->value != NULL) {
1143                     if (!xmlStrEqual(step->value, node->name))
1144                         goto rollback;
1145                 }
1146                 break;
1147             case XSLT_OP_COMMENT:
1148                 if (node->type != XML_COMMENT_NODE)
1149                     goto rollback;
1150                 break;
1151             case XSLT_OP_TEXT:
1152                 if ((node->type != XML_TEXT_NODE) &&
1153                     (node->type != XML_CDATA_SECTION_NODE))
1154                     goto rollback;
1155                 break;
1156             case XSLT_OP_NODE:
1157                 switch (node->type) {
1158                     case XML_ELEMENT_NODE:
1159                     case XML_CDATA_SECTION_NODE:
1160                     case XML_PI_NODE:
1161                     case XML_COMMENT_NODE:
1162                     case XML_TEXT_NODE:
1163                         break;
1164                     default:
1165                         goto rollback;
1166                 }
1167                 break;
1168         }
1169     }
1170 found:
1171     if (states.states != NULL) {
1172         /* Free the rollback states */
1173         xmlFree(states.states);
1174     }
1175     return(1);
1176 rollback:
1177     /* got an error try to rollback */
1178     if (states.states == NULL)
1179         return(0);
1180     if (states.nbstates <= 0) {
1181         xmlFree(states.states);
1182         return(0);
1183     }
1184     states.nbstates--;
1185     i = states.states[states.nbstates].step;
1186     node = states.states[states.nbstates].node;
1187 #if 0
1188     fprintf(stderr, "Pop: %d, %s\n", i, node->name);
1189 #endif
1190     goto restart;
1191 }
1192
1193 /**
1194  * xsltTestCompMatchList:
1195  * @ctxt:  a XSLT process context
1196  * @node: a node
1197  * @comp: the precompiled pattern list
1198  *
1199  * Test whether the node matches one of the patterns in the list
1200  *
1201  * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1202  */
1203 int
1204 xsltTestCompMatchList(xsltTransformContextPtr ctxt, xmlNodePtr node,
1205                       xsltCompMatchPtr comp) {
1206     int ret;
1207
1208     if ((ctxt == NULL) || (node == NULL))
1209         return(-1);
1210     while (comp != NULL) {
1211         ret = xsltTestCompMatch(ctxt, comp, node, NULL, NULL);
1212         if (ret == 1)
1213             return(1);
1214         comp = comp->next;
1215     }
1216     return(0);
1217 }
1218
1219 /************************************************************************
1220  *                                                                      *
1221  *                      Dedicated parser for templates                  *
1222  *                                                                      *
1223  ************************************************************************/
1224
1225 #define CUR (*ctxt->cur)
1226 #define SKIP(val) ctxt->cur += (val)
1227 #define NXT(val) ctxt->cur[(val)]
1228 #define CUR_PTR ctxt->cur
1229
1230 #define SKIP_BLANKS                                                     \
1231     while (IS_BLANK_CH(CUR)) NEXT
1232
1233 #define CURRENT (*ctxt->cur)
1234 #define NEXT ((*ctxt->cur) ?  ctxt->cur++: ctxt->cur)
1235
1236
1237 #define PUSH(op, val, val2, novar)                                              \
1238     if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1239
1240 #define SWAP()                                          \
1241     xsltSwapTopCompMatch(ctxt->comp);
1242
1243 #define XSLT_ERROR(X)                                                   \
1244     { xsltError(ctxt, __FILE__, __LINE__, X);                   \
1245       ctxt->error = (X); return; }
1246
1247 #define XSLT_ERROR0(X)                                                  \
1248     { xsltError(ctxt, __FILE__, __LINE__, X);                   \
1249       ctxt->error = (X); return(0); }
1250
1251 /**
1252  * xsltScanLiteral:
1253  * @ctxt:  the XPath Parser context
1254  *
1255  * Parse an XPath Litteral:
1256  *
1257  * [29] Literal ::= '"' [^"]* '"'
1258  *                | "'" [^']* "'"
1259  *
1260  * Returns the Literal parsed or NULL
1261  */
1262
1263 static xmlChar *
1264 xsltScanLiteral(xsltParserContextPtr ctxt) {
1265     const xmlChar *q, *cur;
1266     xmlChar *ret = NULL;
1267     int val, len;
1268
1269     SKIP_BLANKS;
1270     if (CUR == '"') {
1271         NEXT;
1272         cur = q = CUR_PTR;
1273         val = xmlStringCurrentChar(NULL, cur, &len);
1274         while ((IS_CHAR(val)) && (val != '"')) {
1275             cur += len;
1276             val = xmlStringCurrentChar(NULL, cur, &len);
1277         }
1278         if (!IS_CHAR(val)) {
1279             ctxt->error = 1;
1280             return(NULL);
1281         } else {
1282             ret = xmlStrndup(q, cur - q);
1283         }
1284         cur += len;
1285         CUR_PTR = cur;
1286     } else if (CUR == '\'') {
1287         NEXT;
1288         cur = q = CUR_PTR;
1289         val = xmlStringCurrentChar(NULL, cur, &len);
1290         while ((IS_CHAR(val)) && (val != '\'')) {
1291             cur += len;
1292             val = xmlStringCurrentChar(NULL, cur, &len);
1293         }
1294         if (!IS_CHAR(val)) {
1295             ctxt->error = 1;
1296             return(NULL);
1297         } else {
1298             ret = xmlStrndup(q, cur - q);
1299         }
1300         cur += len;
1301         CUR_PTR = cur;
1302     } else {
1303         /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1304         ctxt->error = 1;
1305         return(NULL);
1306     }
1307     return(ret);
1308 }
1309
1310 /**
1311  * xsltScanNCName:
1312  * @ctxt:  the XPath Parser context
1313  *
1314  * Parses a non qualified name
1315  *
1316  * Returns the Name parsed or NULL
1317  */
1318
1319 static xmlChar *
1320 xsltScanNCName(xsltParserContextPtr ctxt) {
1321     const xmlChar *q, *cur;
1322     xmlChar *ret = NULL;
1323     int val, len;
1324
1325     SKIP_BLANKS;
1326
1327     cur = q = CUR_PTR;
1328     val = xmlStringCurrentChar(NULL, cur, &len);
1329     if (!IS_LETTER(val) && (val != '_'))
1330         return(NULL);
1331
1332     while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
1333            (val == '.') || (val == '-') ||
1334            (val == '_') ||
1335            (IS_COMBINING(val)) ||
1336            (IS_EXTENDER(val))) {
1337         cur += len;
1338         val = xmlStringCurrentChar(NULL, cur, &len);
1339     }
1340     ret = xmlStrndup(q, cur - q);
1341     CUR_PTR = cur;
1342     return(ret);
1343 }
1344
1345 /*
1346  * xsltCompileIdKeyPattern:
1347  * @ctxt:  the compilation context
1348  * @name:  a preparsed name
1349  * @aid:  whether id/key are allowed there
1350  * @novar:  flag to prohibit xslt var
1351  *
1352  * Compile the XSLT LocationIdKeyPattern
1353  * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1354  *                    | 'key' '(' Literal ',' Literal ')'
1355  *
1356  * also handle NodeType and PI from:
1357  *
1358  * [7]  NodeTest ::= NameTest
1359  *                 | NodeType '(' ')'
1360  *                 | 'processing-instruction' '(' Literal ')'
1361  */
1362 static void
1363 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt, xmlChar *name,
1364                 int aid, int novar, xsltAxis axis) {
1365     xmlChar *lit = NULL;
1366     xmlChar *lit2 = NULL;
1367
1368     if (CUR != '(') {
1369         xsltTransformError(NULL, NULL, NULL,
1370                 "xsltCompileIdKeyPattern : ( expected\n");
1371         ctxt->error = 1;
1372         return;
1373     }
1374     if ((aid) && (xmlStrEqual(name, (const xmlChar *)"id"))) {
1375         if (axis != 0) {
1376             xsltTransformError(NULL, NULL, NULL,
1377                     "xsltCompileIdKeyPattern : NodeTest expected\n");
1378             ctxt->error = 1;
1379             return;
1380         }
1381         NEXT;
1382         SKIP_BLANKS;
1383         lit = xsltScanLiteral(ctxt);
1384         if (ctxt->error)
1385             return;
1386         SKIP_BLANKS;
1387         if (CUR != ')') {
1388             xsltTransformError(NULL, NULL, NULL,
1389                     "xsltCompileIdKeyPattern : ) expected\n");
1390             ctxt->error = 1;
1391             return;
1392         }
1393         NEXT;
1394         PUSH(XSLT_OP_ID, lit, NULL, novar);
1395     } else if ((aid) && (xmlStrEqual(name, (const xmlChar *)"key"))) {
1396         if (axis != 0) {
1397             xsltTransformError(NULL, NULL, NULL,
1398                     "xsltCompileIdKeyPattern : NodeTest expected\n");
1399             ctxt->error = 1;
1400             return;
1401         }
1402         NEXT;
1403         SKIP_BLANKS;
1404         lit = xsltScanLiteral(ctxt);
1405         if (ctxt->error)
1406             return;
1407         SKIP_BLANKS;
1408         if (CUR != ',') {
1409             xsltTransformError(NULL, NULL, NULL,
1410                     "xsltCompileIdKeyPattern : , expected\n");
1411             ctxt->error = 1;
1412             return;
1413         }
1414         NEXT;
1415         SKIP_BLANKS;
1416         lit2 = xsltScanLiteral(ctxt);
1417         if (ctxt->error)
1418             return;
1419         SKIP_BLANKS;
1420         if (CUR != ')') {
1421             xsltTransformError(NULL, NULL, NULL,
1422                     "xsltCompileIdKeyPattern : ) expected\n");
1423             ctxt->error = 1;
1424             return;
1425         }
1426         NEXT;
1427         /* URGENT TODO: support namespace in keys */
1428         PUSH(XSLT_OP_KEY, lit, lit2, novar);
1429     } else if (xmlStrEqual(name, (const xmlChar *)"processing-instruction")) {
1430         NEXT;
1431         SKIP_BLANKS;
1432         if (CUR != ')') {
1433             lit = xsltScanLiteral(ctxt);
1434             if (ctxt->error)
1435                 return;
1436             SKIP_BLANKS;
1437             if (CUR != ')') {
1438                 xsltTransformError(NULL, NULL, NULL,
1439                         "xsltCompileIdKeyPattern : ) expected\n");
1440                 ctxt->error = 1;
1441                 return;
1442             }
1443         }
1444         NEXT;
1445         PUSH(XSLT_OP_PI, lit, NULL, novar);
1446     } else if (xmlStrEqual(name, (const xmlChar *)"text")) {
1447         NEXT;
1448         SKIP_BLANKS;
1449         if (CUR != ')') {
1450             xsltTransformError(NULL, NULL, NULL,
1451                     "xsltCompileIdKeyPattern : ) expected\n");
1452             ctxt->error = 1;
1453             return;
1454         }
1455         NEXT;
1456         PUSH(XSLT_OP_TEXT, NULL, NULL, novar);
1457     } else if (xmlStrEqual(name, (const xmlChar *)"comment")) {
1458         NEXT;
1459         SKIP_BLANKS;
1460         if (CUR != ')') {
1461             xsltTransformError(NULL, NULL, NULL,
1462                     "xsltCompileIdKeyPattern : ) expected\n");
1463             ctxt->error = 1;
1464             return;
1465         }
1466         NEXT;
1467         PUSH(XSLT_OP_COMMENT, NULL, NULL, novar);
1468     } else if (xmlStrEqual(name, (const xmlChar *)"node")) {
1469         NEXT;
1470         SKIP_BLANKS;
1471         if (CUR != ')') {
1472             xsltTransformError(NULL, NULL, NULL,
1473                     "xsltCompileIdKeyPattern : ) expected\n");
1474             ctxt->error = 1;
1475             return;
1476         }
1477         NEXT;
1478         if (axis == AXIS_ATTRIBUTE) {
1479             PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1480         }
1481         else {
1482             PUSH(XSLT_OP_NODE, NULL, NULL, novar);
1483         }
1484     } else if (aid) {
1485         xsltTransformError(NULL, NULL, NULL,
1486             "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1487         ctxt->error = 1;
1488         return;
1489     } else {
1490         xsltTransformError(NULL, NULL, NULL,
1491             "xsltCompileIdKeyPattern : node type\n");
1492         ctxt->error = 1;
1493         return;
1494     }
1495 error:
1496     if (name != NULL)
1497         xmlFree(name);
1498 }
1499
1500 /**
1501  * xsltCompileStepPattern:
1502  * @ctxt:  the compilation context
1503  * @token:  a posible precompiled name
1504  * @novar: flag to prohibit xslt variables from pattern
1505  *
1506  * Compile the XSLT StepPattern and generates a precompiled
1507  * form suitable for fast matching.
1508  *
1509  * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate* 
1510  * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1511  *                                     | ('child' | 'attribute') '::'
1512  * from XPath
1513  * [7]  NodeTest ::= NameTest
1514  *                 | NodeType '(' ')'
1515  *                 | 'processing-instruction' '(' Literal ')'
1516  * [8] Predicate ::= '[' PredicateExpr ']'
1517  * [9] PredicateExpr ::= Expr
1518  * [13] AbbreviatedAxisSpecifier ::= '@'?
1519  * [37] NameTest ::= '*' | NCName ':' '*' | QName
1520  */
1521
1522 static void
1523 xsltCompileStepPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1524     xmlChar *name = NULL;
1525     const xmlChar *URI = NULL;
1526     xmlChar *URL = NULL;
1527     int level;
1528     xsltAxis axis = 0;
1529
1530     SKIP_BLANKS;
1531     if ((token == NULL) && (CUR == '@')) {
1532         NEXT;
1533         axis = AXIS_ATTRIBUTE;
1534     }
1535 parse_node_test:
1536     if (token == NULL)
1537         token = xsltScanNCName(ctxt);
1538     if (token == NULL) {
1539         if (CUR == '*') {
1540             NEXT;
1541             if (axis == AXIS_ATTRIBUTE) {
1542                 PUSH(XSLT_OP_ATTR, NULL, NULL, novar);
1543             }
1544             else {
1545                 PUSH(XSLT_OP_ALL, NULL, NULL, novar);
1546             }
1547             goto parse_predicate;
1548         } else {
1549             xsltTransformError(NULL, NULL, NULL,
1550                     "xsltCompileStepPattern : Name expected\n");
1551             ctxt->error = 1;
1552             goto error;
1553         }
1554     }
1555
1556
1557     SKIP_BLANKS;
1558     if (CUR == '(') {
1559         xsltCompileIdKeyPattern(ctxt, token, 0, novar, axis);
1560         if (ctxt->error)
1561             goto error;
1562     } else if (CUR == ':') {
1563         NEXT;
1564         if (CUR != ':') {
1565             xmlChar *prefix = token;
1566             xmlNsPtr ns;
1567
1568             /*
1569              * This is a namespace match
1570              */
1571             token = xsltScanNCName(ctxt);
1572             ns = xmlSearchNs(ctxt->doc, ctxt->elem, prefix);
1573             if (ns == NULL) {
1574                 xsltTransformError(NULL, NULL, NULL,
1575             "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1576                                  prefix);
1577                 xmlFree(prefix);
1578                 ctxt->error = 1;
1579                 goto error;
1580             } else {
1581                 URL = xmlStrdup(ns->href);
1582             }
1583             xmlFree(prefix);
1584             if (token == NULL) {
1585                 if (CUR == '*') {
1586                     NEXT;
1587                     if (axis == AXIS_ATTRIBUTE) {
1588                         PUSH(XSLT_OP_ATTR, NULL, URL, novar);
1589                     }
1590                     else {
1591                         PUSH(XSLT_OP_NS, URL, NULL, novar);
1592                     }
1593                 } else {
1594                     xsltTransformError(NULL, NULL, NULL,
1595                             "xsltCompileStepPattern : Name expected\n");
1596                     ctxt->error = 1;
1597                     goto error;
1598                 }
1599             } else {
1600                 if (axis == AXIS_ATTRIBUTE) {
1601                     PUSH(XSLT_OP_ATTR, token, URL, novar);
1602                 }
1603                 else {
1604                     PUSH(XSLT_OP_ELEM, token, URL, novar);
1605                 }
1606             }
1607         } else {
1608             if (axis != 0) {
1609                 xsltTransformError(NULL, NULL, NULL,
1610                     "xsltCompileStepPattern : NodeTest expected\n");
1611                 ctxt->error = 1;
1612                 goto error;
1613             }
1614             NEXT;
1615             if (xmlStrEqual(token, (const xmlChar *) "child")) {
1616                 axis = AXIS_CHILD;
1617             } else if (xmlStrEqual(token, (const xmlChar *) "attribute")) {
1618                 axis = AXIS_ATTRIBUTE;
1619             } else {
1620                 xsltTransformError(NULL, NULL, NULL,
1621                     "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1622                 ctxt->error = 1;
1623                 goto error;
1624             }
1625             xmlFree(token);
1626             SKIP_BLANKS;
1627             token = xsltScanNCName(ctxt);
1628             goto parse_node_test;
1629         }
1630     } else {
1631         URI = xsltGetQNameURI(ctxt->elem, &token);
1632         if (token == NULL) {
1633             ctxt->error = 1;
1634             goto error;
1635         }
1636         if (URI != NULL)
1637             URL = xmlStrdup(URI);
1638         if (axis == AXIS_ATTRIBUTE) {
1639             PUSH(XSLT_OP_ATTR, token, URL, novar);
1640         }
1641         else {
1642             PUSH(XSLT_OP_ELEM, token, URL, novar);
1643         }
1644     }
1645 parse_predicate:
1646     SKIP_BLANKS;
1647     level = 0;
1648     while (CUR == '[') {
1649         const xmlChar *q;
1650         xmlChar *ret = NULL;
1651
1652         level++;
1653         NEXT;
1654         q = CUR_PTR;
1655         while (CUR != 0) {
1656             /* Skip over nested predicates */
1657             if (CUR == '[')
1658                 level++;
1659             else if (CUR == ']') {
1660                 level--;
1661                 if (level == 0)
1662                     break;
1663             } else if (CUR == '"') {
1664                 NEXT;
1665                 while ((CUR != 0) && (CUR != '"'))
1666                     NEXT;
1667             } else if (CUR == '\'') {
1668                 NEXT;
1669                 while ((CUR != 0) && (CUR != '\''))
1670                     NEXT;
1671             }
1672             NEXT;
1673         }
1674         if (CUR == 0) {
1675             xsltTransformError(NULL, NULL, NULL,
1676                     "xsltCompileStepPattern : ']' expected\n");
1677             ctxt->error = 1;
1678             return;
1679         }
1680         ret = xmlStrndup(q, CUR_PTR - q);
1681         PUSH(XSLT_OP_PREDICATE, ret, NULL, novar);
1682         /* push the predicate lower than local test */
1683         SWAP();
1684         NEXT;
1685         SKIP_BLANKS;
1686     }
1687     return;
1688 error:
1689     if (token != NULL)
1690         xmlFree(token);
1691     if (name != NULL)
1692         xmlFree(name);
1693 }
1694
1695 /**
1696  * xsltCompileRelativePathPattern:
1697  * @comp:  the compilation context
1698  * @token:  a posible precompiled name
1699  * @novar:  flag to prohibit xslt variables
1700  *
1701  * Compile the XSLT RelativePathPattern and generates a precompiled
1702  * form suitable for fast matching.
1703  *
1704  * [4] RelativePathPattern ::= StepPattern
1705  *                           | RelativePathPattern '/' StepPattern
1706  *                           | RelativePathPattern '//' StepPattern
1707  */
1708 static void
1709 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt, xmlChar *token, int novar) {
1710     xsltCompileStepPattern(ctxt, token, novar);
1711     if (ctxt->error)
1712         goto error;
1713     SKIP_BLANKS;
1714     while ((CUR != 0) && (CUR != '|')) {
1715         if ((CUR == '/') && (NXT(1) == '/')) {
1716             PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1717             NEXT;
1718             NEXT;
1719             SKIP_BLANKS;
1720             xsltCompileStepPattern(ctxt, NULL, novar);
1721         } else if (CUR == '/') {
1722             PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1723             NEXT;
1724             SKIP_BLANKS;
1725             if ((CUR != 0) && (CUR != '|')) {
1726                 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1727             }
1728         } else {
1729             ctxt->error = 1;
1730         }
1731         if (ctxt->error)
1732             goto error;
1733         SKIP_BLANKS;
1734     }
1735 error:
1736     return;
1737 }
1738
1739 /**
1740  * xsltCompileLocationPathPattern:
1741  * @ctxt:  the compilation context
1742  * @novar:  flag to prohibit xslt variables
1743  *
1744  * Compile the XSLT LocationPathPattern and generates a precompiled
1745  * form suitable for fast matching.
1746  *
1747  * [2] LocationPathPattern ::= '/' RelativePathPattern?
1748  *                           | IdKeyPattern (('/' | '//') RelativePathPattern)?
1749  *                           | '//'? RelativePathPattern
1750  */
1751 static void
1752 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt, int novar) {
1753     SKIP_BLANKS;
1754     if ((CUR == '/') && (NXT(1) == '/')) {
1755         /*
1756          * since we reverse the query
1757          * a leading // can be safely ignored
1758          */
1759         NEXT;
1760         NEXT;
1761         ctxt->comp->priority = 0.5;     /* '//' means not 0 priority */
1762         xsltCompileRelativePathPattern(ctxt, NULL, novar);
1763     } else if (CUR == '/') {
1764         /*
1765          * We need to find root as the parent
1766          */
1767         NEXT;
1768         SKIP_BLANKS;
1769         PUSH(XSLT_OP_ROOT, NULL, NULL, novar);
1770         if ((CUR != 0) && (CUR != '|')) {
1771             PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1772             xsltCompileRelativePathPattern(ctxt, NULL, novar);
1773         }
1774     } else if (CUR == '*') {
1775         xsltCompileRelativePathPattern(ctxt, NULL, novar);
1776     } else if (CUR == '@') {
1777         xsltCompileRelativePathPattern(ctxt, NULL, novar);
1778     } else {
1779         xmlChar *name;
1780         name = xsltScanNCName(ctxt);
1781         if (name == NULL) {
1782             xsltTransformError(NULL, NULL, NULL,
1783                     "xsltCompileLocationPathPattern : Name expected\n");
1784             ctxt->error = 1;
1785             return;
1786         }
1787         SKIP_BLANKS;
1788         if ((CUR == '(') && !xmlXPathIsNodeType(name)) {
1789             xsltCompileIdKeyPattern(ctxt, name, 1, novar, 0);
1790             if (ctxt->error)
1791                 return;
1792             if ((CUR == '/') && (NXT(1) == '/')) {
1793                 PUSH(XSLT_OP_ANCESTOR, NULL, NULL, novar);
1794                 NEXT;
1795                 NEXT;
1796                 SKIP_BLANKS;
1797                 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1798             } else if (CUR == '/') {
1799                 PUSH(XSLT_OP_PARENT, NULL, NULL, novar);
1800                 NEXT;
1801                 SKIP_BLANKS;
1802                 xsltCompileRelativePathPattern(ctxt, NULL, novar);
1803             }
1804             return;
1805         }
1806         xsltCompileRelativePathPattern(ctxt, name, novar);
1807     }
1808 error:
1809     return;
1810 }
1811
1812 /**
1813  * xsltCompilePatternInternal:
1814  * @pattern: an XSLT pattern
1815  * @doc:  the containing document
1816  * @node:  the containing element
1817  * @style:  the stylesheet
1818  * @runtime:  the transformation context, if done at run-time
1819  * @novar:  flag to prohibit xslt variables
1820  *
1821  * Compile the XSLT pattern and generates a list of precompiled form suitable
1822  * for fast matching.
1823  *
1824  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1825  *
1826  * Returns the generated pattern list or NULL in case of failure
1827  */
1828
1829 static xsltCompMatchPtr
1830 xsltCompilePatternInternal(const xmlChar *pattern, xmlDocPtr doc,
1831                    xmlNodePtr node, xsltStylesheetPtr style,
1832                    xsltTransformContextPtr runtime, int novar) {
1833     xsltParserContextPtr ctxt = NULL;
1834     xsltCompMatchPtr element, first = NULL, previous = NULL;
1835     int current, start, end, level, j;
1836
1837     if (pattern == NULL) {
1838         xsltTransformError(NULL, NULL, node,
1839                          "xsltCompilePattern : NULL pattern\n");
1840         return(NULL);
1841     }
1842
1843     ctxt = xsltNewParserContext(style, runtime);
1844     if (ctxt == NULL)
1845         return(NULL);
1846     ctxt->doc = doc;
1847     ctxt->elem = node;
1848     current = end = 0;
1849     while (pattern[current] != 0) {
1850         start = current;
1851         while (IS_BLANK_CH(pattern[current]))
1852             current++;
1853         end = current;
1854         level = 0;
1855         while ((pattern[end] != 0) && ((pattern[end] != '|') || (level != 0))) {
1856             if (pattern[end] == '[')
1857                 level++;
1858             else if (pattern[end] == ']')
1859                 level--;
1860             else if (pattern[end] == '\'') {
1861                 end++;
1862                 while ((pattern[end] != 0) && (pattern[end] != '\''))
1863                     end++;
1864             } else if (pattern[end] == '"') {
1865                 end++;
1866                 while ((pattern[end] != 0) && (pattern[end] != '"'))
1867                     end++;
1868             }
1869       if (pattern[end] == 0)
1870           break;
1871             end++;
1872         }
1873         if (current == end) {
1874             xsltTransformError(NULL, NULL, node,
1875                              "xsltCompilePattern : NULL pattern\n");
1876             goto error;
1877         }
1878         element = xsltNewCompMatch();
1879         if (element == NULL) {
1880             goto error;
1881         }
1882         if (first == NULL)
1883             first = element;
1884         else if (previous != NULL)
1885             previous->next = element;
1886         previous = element;
1887
1888         ctxt->comp = element;
1889         ctxt->base = xmlStrndup(&pattern[start], end - start);
1890         if (ctxt->base == NULL)
1891             goto error;
1892         ctxt->cur = &(ctxt->base)[current - start];
1893         element->pattern = ctxt->base;
1894         element->nsList = xmlGetNsList(doc, node);
1895         j = 0;
1896         if (element->nsList != NULL) {
1897             while (element->nsList[j] != NULL)
1898                 j++;
1899         }
1900         element->nsNr = j;
1901
1902
1903 #ifdef WITH_XSLT_DEBUG_PATTERN
1904         xsltGenericDebug(xsltGenericDebugContext,
1905                          "xsltCompilePattern : parsing '%s'\n",
1906                          element->pattern);
1907 #endif
1908         /*
1909          Preset default priority to be zero.
1910          This may be changed by xsltCompileLocationPathPattern.
1911          */
1912         element->priority = 0;
1913         xsltCompileLocationPathPattern(ctxt, novar);
1914         if (ctxt->error) {
1915             xsltTransformError(NULL, style, node,
1916                              "xsltCompilePattern : failed to compile '%s'\n",
1917                              element->pattern);
1918             if (style != NULL) style->errors++;
1919             goto error;
1920         }
1921
1922         /*
1923          * Reverse for faster interpretation.
1924          */
1925         xsltReverseCompMatch(ctxt, element);
1926
1927         /*
1928          * Set-up the priority
1929          */
1930         if (element->priority == 0) {   /* if not yet determined */
1931             if (((element->steps[0].op == XSLT_OP_ELEM) ||
1932                  (element->steps[0].op == XSLT_OP_ATTR) ||
1933                  (element->steps[0].op == XSLT_OP_PI)) &&
1934                 (element->steps[0].value != NULL) &&
1935                 (element->steps[1].op == XSLT_OP_END)) {
1936                 ;       /* previously preset */
1937             } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1938                        (element->steps[0].value2 != NULL) &&
1939                        (element->steps[1].op == XSLT_OP_END)) {
1940                         element->priority = -0.25;
1941             } else if ((element->steps[0].op == XSLT_OP_NS) &&
1942                        (element->steps[0].value != NULL) &&
1943                        (element->steps[1].op == XSLT_OP_END)) {
1944                         element->priority = -0.25;
1945             } else if ((element->steps[0].op == XSLT_OP_ATTR) &&
1946                        (element->steps[0].value == NULL) &&
1947                        (element->steps[0].value2 == NULL) &&
1948                        (element->steps[1].op == XSLT_OP_END)) {
1949                         element->priority = -0.5;
1950             } else if (((element->steps[0].op == XSLT_OP_PI) ||
1951                        (element->steps[0].op == XSLT_OP_TEXT) ||
1952                        (element->steps[0].op == XSLT_OP_ALL) ||
1953                        (element->steps[0].op == XSLT_OP_NODE) ||
1954                        (element->steps[0].op == XSLT_OP_COMMENT)) &&
1955                        (element->steps[1].op == XSLT_OP_END)) {
1956                         element->priority = -0.5;
1957             } else {
1958                 element->priority = 0.5;
1959             }
1960         }
1961 #ifdef WITH_XSLT_DEBUG_PATTERN
1962         xsltGenericDebug(xsltGenericDebugContext,
1963                      "xsltCompilePattern : parsed %s, default priority %f\n",
1964                          element->pattern, element->priority);
1965 #endif
1966         if (pattern[end] == '|')
1967             end++;
1968         current = end;
1969     }
1970     if (end == 0) {
1971         xsltTransformError(NULL, style, node,
1972                          "xsltCompilePattern : NULL pattern\n");
1973         if (style != NULL) style->errors++;
1974         goto error;
1975     }
1976
1977     xsltFreeParserContext(ctxt);
1978     return(first);
1979
1980 error:
1981     if (ctxt != NULL)
1982         xsltFreeParserContext(ctxt);
1983     if (first != NULL)
1984         xsltFreeCompMatchList(first);
1985     return(NULL);
1986 }
1987
1988 /**
1989  * xsltCompilePattern:
1990  * @pattern: an XSLT pattern
1991  * @doc:  the containing document
1992  * @node:  the containing element
1993  * @style:  the stylesheet
1994  * @runtime:  the transformation context, if done at run-time
1995  *
1996  * Compile the XSLT pattern and generates a list of precompiled form suitable
1997  * for fast matching.
1998  *
1999  * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
2000  *
2001  * Returns the generated pattern list or NULL in case of failure
2002  */
2003
2004 xsltCompMatchPtr
2005 xsltCompilePattern(const xmlChar *pattern, xmlDocPtr doc,
2006                    xmlNodePtr node, xsltStylesheetPtr style,
2007                    xsltTransformContextPtr runtime) {
2008     return (xsltCompilePatternInternal(pattern, doc, node, style, runtime, 0));
2009 }
2010
2011 /************************************************************************
2012  *                                                                      *
2013  *                      Module interfaces                               *
2014  *                                                                      *
2015  ************************************************************************/
2016
2017 /**
2018  * xsltAddTemplate:
2019  * @style: an XSLT stylesheet
2020  * @cur: an XSLT template
2021  * @mode:  the mode name or NULL
2022  * @modeURI:  the mode URI or NULL
2023  *
2024  * Register the XSLT pattern associated to @cur
2025  *
2026  * Returns -1 in case of error, 0 otherwise
2027  */
2028 int
2029 xsltAddTemplate(xsltStylesheetPtr style, xsltTemplatePtr cur,
2030                 const xmlChar *mode, const xmlChar *modeURI) {
2031     xsltCompMatchPtr pat, list, next;
2032     /*
2033      * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2034      *  avoids gcc 'type-punned pointer' warning.
2035      */
2036     void **top = NULL;
2037     const xmlChar *name = NULL;
2038     float priority;              /* the priority */
2039
2040     if ((style == NULL) || (cur == NULL) || (cur->match == NULL))
2041         return(-1);
2042
2043     priority = cur->priority;
2044     pat = xsltCompilePatternInternal(cur->match, style->doc, cur->elem,
2045                     style, NULL, 1);
2046     if (pat == NULL)
2047         return(-1);
2048     while (pat) {
2049         next = pat->next;
2050         pat->next = NULL;
2051         name = NULL;
2052         
2053         pat->template = cur;
2054         if (mode != NULL)
2055             pat->mode = xmlDictLookup(style->dict, mode, -1);
2056         if (modeURI != NULL)
2057             pat->modeURI = xmlDictLookup(style->dict, modeURI, -1);
2058         if (priority != XSLT_PAT_NO_PRIORITY)
2059             pat->priority = priority;
2060
2061         /*
2062          * insert it in the hash table list corresponding to its lookup name
2063          */
2064         switch (pat->steps[0].op) {
2065         case XSLT_OP_ATTR:
2066             if (pat->steps[0].value != NULL)
2067                 name = pat->steps[0].value;
2068             else
2069                 top = &(style->attrMatch);
2070             break;
2071         case XSLT_OP_PARENT:
2072         case XSLT_OP_ANCESTOR:
2073             top = &(style->elemMatch);
2074             break;
2075         case XSLT_OP_ROOT:
2076             top = &(style->rootMatch);
2077             break;
2078         case XSLT_OP_KEY:
2079             top = &(style->keyMatch);
2080             break;
2081         case XSLT_OP_ID:
2082             /* TODO optimize ID !!! */
2083         case XSLT_OP_NS:
2084         case XSLT_OP_ALL:
2085             top = &(style->elemMatch);
2086             break;
2087         case XSLT_OP_END:
2088         case XSLT_OP_PREDICATE:
2089             xsltTransformError(NULL, style, NULL,
2090                              "xsltAddTemplate: invalid compiled pattern\n");
2091             xsltFreeCompMatch(pat);
2092             return(-1);
2093             /*
2094              * TODO: some flags at the top level about type based patterns
2095              *       would be faster than inclusion in the hash table.
2096              */
2097         case XSLT_OP_PI:
2098             if (pat->steps[0].value != NULL)
2099                 name = pat->steps[0].value;
2100             else
2101                 top = &(style->piMatch);
2102             break;
2103         case XSLT_OP_COMMENT:
2104             top = &(style->commentMatch);
2105             break;
2106         case XSLT_OP_TEXT:
2107             top = &(style->textMatch);
2108             break;
2109         case XSLT_OP_ELEM:
2110         case XSLT_OP_NODE:
2111             if (pat->steps[0].value != NULL)
2112                 name = pat->steps[0].value;
2113             else
2114                 top = &(style->elemMatch);
2115             break;
2116         }
2117         if (name != NULL) {
2118             if (style->templatesHash == NULL) {
2119                 style->templatesHash = xmlHashCreate(1024);
2120                 if (style->templatesHash == NULL) {
2121                     xsltFreeCompMatch(pat);
2122                     return(-1);
2123                 }
2124                 xmlHashAddEntry3(style->templatesHash, name, mode, modeURI, pat);
2125             } else {
2126                 list = (xsltCompMatchPtr) xmlHashLookup3(style->templatesHash,
2127                                                          name, mode, modeURI);
2128                 if (list == NULL) {
2129                     xmlHashAddEntry3(style->templatesHash, name,
2130                                      mode, modeURI, pat);
2131                 } else {
2132                     /*
2133                      * Note '<=' since one must choose among the matching
2134                      * template rules that are left, the one that occurs
2135                      * last in the stylesheet
2136                      */
2137                     if (list->priority <= pat->priority) {
2138                         pat->next = list;
2139                         xmlHashUpdateEntry3(style->templatesHash, name,
2140                                             mode, modeURI, pat, NULL);
2141                     } else {
2142                         while (list->next != NULL) {
2143                             if (list->next->priority <= pat->priority)
2144                                 break;
2145                             list = list->next;
2146                         }
2147                         pat->next = list->next;
2148                         list->next = pat;
2149                     }
2150                 }
2151             }
2152         } else if (top != NULL) {
2153             list = *top;
2154             if (list == NULL) {
2155                 *top = pat;
2156                 pat->next = NULL;
2157             } else if (list->priority <= pat->priority) {
2158                 pat->next = list;
2159                 *top = pat;
2160             } else {
2161                 while (list->next != NULL) {
2162                     if (list->next->priority <= pat->priority)
2163                         break;
2164                     list = list->next;
2165                 }
2166                 pat->next = list->next;
2167                 list->next = pat;
2168             }
2169         } else {
2170             xsltTransformError(NULL, style, NULL,
2171                              "xsltAddTemplate: invalid compiled pattern\n");
2172             xsltFreeCompMatch(pat);
2173             return(-1);
2174         }
2175 #ifdef WITH_XSLT_DEBUG_PATTERN
2176         if (mode)
2177             xsltGenericDebug(xsltGenericDebugContext,
2178                          "added pattern : '%s' mode '%s' priority %f\n",
2179                              pat->pattern, pat->mode, pat->priority);
2180         else
2181             xsltGenericDebug(xsltGenericDebugContext,
2182                          "added pattern : '%s' priority %f\n",
2183                              pat->pattern, pat->priority);
2184 #endif
2185
2186         pat = next;
2187     }
2188     return(0);
2189 }
2190
2191 static int
2192 xsltComputeAllKeys(xsltTransformContextPtr ctxt, xmlNodePtr contextNode)
2193 {
2194     if ((ctxt == NULL) || (contextNode == NULL)) {
2195         xsltTransformError(ctxt, NULL, ctxt->inst,
2196             "Internal error in xsltComputeAllKeys(): "
2197             "Bad arguments.\n");
2198         return(-1);
2199     }
2200
2201     if (ctxt->document == NULL) {
2202         /*
2203         * The document info will only be NULL if we have a RTF.
2204         */
2205         if (contextNode->doc->_private != NULL)
2206             goto doc_info_mismatch;
2207         /*
2208         * On-demand creation of the document info (needed for keys).
2209         */
2210         ctxt->document = xsltNewDocument(ctxt, contextNode->doc);
2211         if (ctxt->document == NULL)
2212             return(-1);
2213     }
2214     return xsltInitAllDocKeys(ctxt);
2215
2216 doc_info_mismatch:
2217     xsltTransformError(ctxt, NULL, ctxt->inst,
2218         "Internal error in xsltComputeAllKeys(): "
2219         "The context's document info doesn't match the "
2220         "document info of the current result tree.\n");
2221     ctxt->state = XSLT_STATE_STOPPED;
2222     return(-1);
2223 }
2224
2225 /**
2226  * xsltGetTemplate:
2227  * @ctxt:  a XSLT process context
2228  * @node:  the node being processed
2229  * @style:  the current style
2230  *
2231  * Finds the template applying to this node, if @style is non-NULL
2232  * it means one needs to look for the next imported template in scope.
2233  *
2234  * Returns the xsltTemplatePtr or NULL if not found
2235  */
2236 xsltTemplatePtr
2237 xsltGetTemplate(xsltTransformContextPtr ctxt, xmlNodePtr node,
2238                 xsltStylesheetPtr style)
2239 {
2240     xsltStylesheetPtr curstyle;
2241     xsltTemplatePtr ret = NULL;
2242     const xmlChar *name = NULL;
2243     xsltCompMatchPtr list = NULL;
2244     float priority;
2245     int keyed = 0;
2246
2247     if ((ctxt == NULL) || (node == NULL))
2248         return(NULL);
2249
2250     if (style == NULL) {
2251         curstyle = ctxt->style;
2252     } else {
2253         curstyle = xsltNextImport(style);
2254     }
2255
2256     while ((curstyle != NULL) && (curstyle != style)) {
2257         priority = XSLT_PAT_NO_PRIORITY;
2258         /* TODO : handle IDs/keys here ! */
2259         if (curstyle->templatesHash != NULL) {
2260             /*
2261              * Use the top name as selector
2262              */
2263             switch (node->type) {
2264                 case XML_ELEMENT_NODE:
2265                     if (node->name[0] == ' ')
2266                         break;
2267                 case XML_ATTRIBUTE_NODE:
2268                 case XML_PI_NODE:
2269                     name = node->name;
2270                     break;
2271                 case XML_DOCUMENT_NODE:
2272                 case XML_HTML_DOCUMENT_NODE:
2273                 case XML_TEXT_NODE:
2274                 case XML_CDATA_SECTION_NODE:
2275                 case XML_COMMENT_NODE:
2276                 case XML_ENTITY_REF_NODE:
2277                 case XML_ENTITY_NODE:
2278                 case XML_DOCUMENT_TYPE_NODE:
2279                 case XML_DOCUMENT_FRAG_NODE:
2280                 case XML_NOTATION_NODE:
2281                 case XML_DTD_NODE:
2282                 case XML_ELEMENT_DECL:
2283                 case XML_ATTRIBUTE_DECL:
2284                 case XML_ENTITY_DECL:
2285                 case XML_NAMESPACE_DECL:
2286                 case XML_XINCLUDE_START:
2287                 case XML_XINCLUDE_END:
2288                     break;
2289                 default:
2290                     return(NULL);
2291
2292             }
2293         }
2294         if (name != NULL) {
2295             /*
2296              * find the list of applicable expressions based on the name
2297              */
2298             list = (xsltCompMatchPtr) xmlHashLookup3(curstyle->templatesHash,
2299                                              name, ctxt->mode, ctxt->modeURI);
2300         } else
2301             list = NULL;
2302         while (list != NULL) {
2303             if (xsltTestCompMatch(ctxt, list, node,
2304                                   ctxt->mode, ctxt->modeURI)) {
2305                 ret = list->template;
2306                 priority = list->priority;
2307                 break;
2308             }
2309             list = list->next;
2310         }
2311         list = NULL;
2312
2313         /*
2314          * find alternate generic matches
2315          */
2316         switch (node->type) {
2317             case XML_ELEMENT_NODE:
2318                 if (node->name[0] == ' ')
2319                     list = curstyle->rootMatch;
2320                 else
2321                     list = curstyle->elemMatch;
2322                 if (node->psvi != NULL) keyed = 1;
2323                 break;
2324             case XML_ATTRIBUTE_NODE: {
2325                 xmlAttrPtr attr;
2326
2327                 list = curstyle->attrMatch;
2328                 attr = (xmlAttrPtr) node;
2329                 if (attr->psvi != NULL) keyed = 1;
2330                 break;
2331             }
2332             case XML_PI_NODE:
2333                 list = curstyle->piMatch;
2334                 if (node->psvi != NULL) keyed = 1;
2335                 break;
2336             case XML_DOCUMENT_NODE:
2337             case XML_HTML_DOCUMENT_NODE: {
2338                 xmlDocPtr doc;
2339
2340                 list = curstyle->rootMatch;
2341                 doc = (xmlDocPtr) node;
2342                 if (doc->psvi != NULL) keyed = 1;
2343                 break;
2344             }
2345             case XML_TEXT_NODE:
2346             case XML_CDATA_SECTION_NODE:
2347                 list = curstyle->textMatch;
2348                 if (node->psvi != NULL) keyed = 1;
2349                 break;
2350             case XML_COMMENT_NODE:
2351                 list = curstyle->commentMatch;
2352                 if (node->psvi != NULL) keyed = 1;
2353                 break;
2354             case XML_ENTITY_REF_NODE:
2355             case XML_ENTITY_NODE:
2356             case XML_DOCUMENT_TYPE_NODE:
2357             case XML_DOCUMENT_FRAG_NODE:
2358             case XML_NOTATION_NODE:
2359             case XML_DTD_NODE:
2360             case XML_ELEMENT_DECL:
2361             case XML_ATTRIBUTE_DECL:
2362             case XML_ENTITY_DECL:
2363             case XML_NAMESPACE_DECL:
2364             case XML_XINCLUDE_START:
2365             case XML_XINCLUDE_END:
2366                 break;
2367             default:
2368                 break;
2369         }
2370         while ((list != NULL) &&
2371                ((ret == NULL)  || (list->priority > priority))) {
2372             if (xsltTestCompMatch(ctxt, list, node,
2373                                   ctxt->mode, ctxt->modeURI)) {
2374                 ret = list->template;
2375                 priority = list->priority;
2376                 break;
2377             }
2378             list = list->next;
2379         }
2380         /*
2381          * Some of the tests for elements can also apply to documents
2382          */
2383         if ((node->type == XML_DOCUMENT_NODE) ||
2384             (node->type == XML_HTML_DOCUMENT_NODE) ||
2385             (node->type == XML_TEXT_NODE)) {
2386             list = curstyle->elemMatch;
2387             while ((list != NULL) &&
2388                    ((ret == NULL)  || (list->priority > priority))) {
2389                 if (xsltTestCompMatch(ctxt, list, node,
2390                                       ctxt->mode, ctxt->modeURI)) {
2391                     ret = list->template;
2392                     priority = list->priority;
2393                     break;
2394                 }
2395                 list = list->next;
2396             }
2397         } else if ((node->type == XML_PI_NODE) ||
2398                    (node->type == XML_COMMENT_NODE)) {
2399             list = curstyle->elemMatch;
2400             while ((list != NULL) &&
2401                    ((ret == NULL)  || (list->priority > priority))) {
2402                 if (xsltTestCompMatch(ctxt, list, node,
2403                                       ctxt->mode, ctxt->modeURI)) {
2404                     ret = list->template;
2405                     priority = list->priority;
2406                     break;
2407                 }
2408                 list = list->next;
2409             }
2410         }
2411
2412 keyed_match:
2413         if (keyed) {
2414             list = curstyle->keyMatch;
2415             while ((list != NULL) &&
2416                    ((ret == NULL)  || (list->priority > priority))) {
2417                 if (xsltTestCompMatch(ctxt, list, node,
2418                                       ctxt->mode, ctxt->modeURI)) {
2419                     ret = list->template;
2420                     priority = list->priority;
2421                     break;
2422                 }
2423                 list = list->next;
2424             }
2425         }
2426         else if (ctxt->hasTemplKeyPatterns &&
2427             ((ctxt->document == NULL) ||
2428              (ctxt->document->nbKeysComputed < ctxt->nbKeys)))
2429         {
2430             /*
2431             * Compute all remaining keys for this document.
2432             *
2433             * REVISIT TODO: I think this could be further optimized.
2434             */
2435             if (xsltComputeAllKeys(ctxt, node) == -1)
2436                 goto error;
2437
2438             switch (node->type) {
2439                 case XML_ELEMENT_NODE:              
2440                     if (node->psvi != NULL) keyed = 1;
2441                     break;
2442                 case XML_ATTRIBUTE_NODE:
2443                     if (((xmlAttrPtr) node)->psvi != NULL) keyed = 1;
2444                     break;
2445                 case XML_TEXT_NODE:
2446                 case XML_CDATA_SECTION_NODE:
2447                 case XML_COMMENT_NODE:
2448                 case XML_PI_NODE:               
2449                     if (node->psvi != NULL) keyed = 1;
2450                     break;
2451                 case XML_DOCUMENT_NODE:
2452                 case XML_HTML_DOCUMENT_NODE:
2453                     if (((xmlDocPtr) node)->psvi != NULL) keyed = 1;
2454                     break;              
2455                 default:
2456                     break;
2457             }
2458             if (keyed)
2459                 goto keyed_match;
2460         }
2461         if (ret != NULL)
2462             return(ret);
2463
2464         /*
2465          * Cycle on next curstylesheet import.
2466          */
2467         curstyle = xsltNextImport(curstyle);
2468     }
2469
2470 error:
2471     return(NULL);
2472 }
2473
2474 /**
2475  * xsltCleanupTemplates:
2476  * @style: an XSLT stylesheet
2477  *
2478  * Cleanup the state of the templates used by the stylesheet and
2479  * the ones it imports.
2480  */
2481 void
2482 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED) {
2483 }
2484
2485 /**
2486  * xsltFreeTemplateHashes:
2487  * @style: an XSLT stylesheet
2488  *
2489  * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
2490  */
2491 void
2492 xsltFreeTemplateHashes(xsltStylesheetPtr style) {
2493     if (style->templatesHash != NULL)
2494         xmlHashFree((xmlHashTablePtr) style->templatesHash,
2495                     (xmlHashDeallocator) xsltFreeCompMatchList);
2496     if (style->rootMatch != NULL)
2497         xsltFreeCompMatchList(style->rootMatch);
2498     if (style->keyMatch != NULL)
2499         xsltFreeCompMatchList(style->keyMatch);
2500     if (style->elemMatch != NULL)
2501         xsltFreeCompMatchList(style->elemMatch);
2502     if (style->attrMatch != NULL)
2503         xsltFreeCompMatchList(style->attrMatch);
2504     if (style->parentMatch != NULL)
2505         xsltFreeCompMatchList(style->parentMatch);
2506     if (style->textMatch != NULL)
2507         xsltFreeCompMatchList(style->textMatch);
2508     if (style->piMatch != NULL)
2509         xsltFreeCompMatchList(style->piMatch);
2510     if (style->commentMatch != NULL)
2511         xsltFreeCompMatchList(style->commentMatch);
2512 }
2513