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