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