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
xsltNewCompMatch(void)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
xsltFreeCompMatch(xsltCompMatchPtr comp)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
xsltFreeCompMatchList(xsltCompMatchPtr comp)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
xsltFreeCompMatchListEntry(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)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 */
xsltNormalizeCompSteps(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)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
xsltNewParserContext(xsltStylesheetPtr style,xsltTransformContextPtr ctxt)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
xsltFreeParserContext(xsltParserContextPtr ctxt)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
xsltCompMatchAdd(xsltParserContextPtr ctxt,xsltCompMatchPtr comp,xsltOp op,xmlChar * value,xmlChar * value2,int novar)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
xsltSwapTopCompMatch(xsltCompMatchPtr comp)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
xsltReverseCompMatch(xsltParserContextPtr ctxt,xsltCompMatchPtr comp)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
xsltPatPushState(xsltTransformContextPtr ctxt,xsltStepStates * states,int step,xmlNodePtr node)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
xmlXPathFreeObjectWrapper(void * obj)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
xsltTestCompMatchDirect(xsltTransformContextPtr ctxt,xsltCompMatchPtr comp,xmlNodePtr node,xmlNsPtr * nsList,int nsNr)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
xsltTestStepMatch(xsltTransformContextPtr ctxt,xmlNodePtr node,xsltStepOpPtr step)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
xsltTestPredicateMatch(xsltTransformContextPtr ctxt,xsltCompMatchPtr comp,xmlNodePtr node,xsltStepOpPtr step,xsltStepOpPtr sel)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
xsltTestCompMatch(xsltTransformContextPtr ctxt,xsltCompMatchPtr comp,xmlNodePtr matchNode,const xmlChar * mode,const xmlChar * modeURI)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
xsltTestCompMatchList(xsltTransformContextPtr ctxt,xmlNodePtr node,xsltCompMatchPtr comp)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
xsltCompMatchClearCache(xsltTransformContextPtr ctxt,xsltCompMatchPtr comp)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 *
xsltScanLiteral(xsltParserContextPtr ctxt)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 *
xsltScanNCName(xsltParserContextPtr ctxt)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
xsltCompileIdKeyPattern(xsltParserContextPtr ctxt,xmlChar * name,int aid,int novar,xsltAxis axis)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
xsltCompileStepPattern(xsltParserContextPtr ctxt,xmlChar * token,int novar)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
xsltCompileRelativePathPattern(xsltParserContextPtr ctxt,xmlChar * token,int novar)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
xsltCompileLocationPathPattern(xsltParserContextPtr ctxt,int novar)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
xsltCompilePatternInternal(const xmlChar * pattern,xmlDocPtr doc,xmlNodePtr node,xsltStylesheetPtr style,xsltTransformContextPtr runtime,int novar)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
xsltCompilePattern(const xmlChar * pattern,xmlDocPtr doc,xmlNodePtr node,xsltStylesheetPtr style,xsltTransformContextPtr runtime)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
xsltAddTemplate(xsltStylesheetPtr style,xsltTemplatePtr cur,const xmlChar * mode,const xmlChar * modeURI)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
xsltComputeAllKeys(xsltTransformContextPtr ctxt,xmlNodePtr contextNode)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
xsltGetTemplate(xsltTransformContextPtr ctxt,xmlNodePtr node,xsltStylesheetPtr style)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
xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED)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
xsltFreeTemplateHashes(xsltStylesheetPtr style)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