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