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