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