2 * pattern.c: Implemetation of the template match compilation and lookup
5 * http://www.w3.org/TR/1999/REC-xslt-19991116
7 * See Copyright for the status of this software.
13 * TODO: handle pathological cases like *[*[@a="b"]]
14 * TODO: detect [number] at compilation, optimize accordingly
19 #ifdef WITH_XSLT_DEBUG
20 #define WITH_XSLT_DEBUG_PATTERN
50 typedef struct _xsltStepState xsltStepState
;
51 typedef xsltStepState
*xsltStepStatePtr
;
52 struct _xsltStepState
{
57 typedef struct _xsltStepStates xsltStepStates
;
58 typedef xsltStepStates
*xsltStepStatesPtr
;
59 struct _xsltStepStates
{
62 xsltStepStatePtr states
;
65 typedef struct _xsltStepOp xsltStepOp
;
66 typedef xsltStepOp
*xsltStepOpPtr
;
72 xmlXPathCompExprPtr comp
;
74 * Optimisations for count
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 */
91 /* TODO fix the statically allocated size steps[] */
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 */
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 */
113 /************************************************************************
117 ************************************************************************/
122 * Create a new XSLT CompMatch
124 * Returns the newly allocated xsltCompMatchPtr or NULL in case of error
126 static xsltCompMatchPtr
127 xsltNewCompMatch(void) {
128 xsltCompMatchPtr cur
;
130 cur
= (xsltCompMatchPtr
) xmlMalloc(sizeof(xsltCompMatch
));
132 xsltTransformError(NULL
, NULL
, NULL
,
133 "xsltNewCompMatch : out of memory error\n");
136 memset(cur
, 0, sizeof(xsltCompMatch
));
139 cur
-> steps
= (xsltStepOpPtr
) xmlMalloc(sizeof(xsltStepOp
) *
141 if (cur
->steps
== NULL
) {
142 xsltTransformError(NULL
, NULL
, NULL
,
143 "xsltNewCompMatch : out of memory error\n");
155 * @comp: an XSLT comp
157 * Free up the memory allocated by @comp
160 xsltFreeCompMatch(xsltCompMatchPtr comp
) {
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
)
174 if (op
->value2
!= NULL
)
176 if (op
->value3
!= NULL
)
178 if (op
->comp
!= NULL
)
179 xmlXPathFreeCompExpr(op
->comp
);
181 xmlFree(comp
->steps
);
182 memset(comp
, -1, sizeof(xsltCompMatch
));
187 * xsltFreeCompMatchList:
188 * @comp: an XSLT comp list
190 * Free up the memory allocated by all the elements of @comp
193 xsltFreeCompMatchList(xsltCompMatchPtr comp
) {
194 xsltCompMatchPtr cur
;
196 while (comp
!= NULL
) {
199 xsltFreeCompMatch(cur
);
204 xsltFreeCompMatchListEntry(void *payload
,
205 const xmlChar
*name ATTRIBUTE_UNUSED
) {
206 xsltFreeCompMatchList((xsltCompMatchPtr
) payload
);
210 * xsltNormalizeCompSteps:
211 * @payload: pointer to template hash table entry
212 * @data: pointer to the stylesheet
213 * @name: template match name
215 * This is a hashtable scanner function to normalize the compiled
216 * steps of an imported stylesheet.
218 void xsltNormalizeCompSteps(void *payload
,
219 void *data
, const xmlChar
*name ATTRIBUTE_UNUSED
) {
220 xsltCompMatchPtr comp
= payload
;
221 xsltStylesheetPtr style
= data
;
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
;
232 * xsltNewParserContext:
233 * @style: the stylesheet
234 * @ctxt: the transformation context, if done at run-time
236 * Create a new XSLT ParserContext
238 * Returns the newly allocated xsltParserContextPtr or NULL in case of error
240 static xsltParserContextPtr
241 xsltNewParserContext(xsltStylesheetPtr style
, xsltTransformContextPtr ctxt
) {
242 xsltParserContextPtr cur
;
244 cur
= (xsltParserContextPtr
) xmlMalloc(sizeof(xsltParserContext
));
246 xsltTransformError(NULL
, NULL
, NULL
,
247 "xsltNewParserContext : malloc failed\n");
250 memset(cur
, 0, sizeof(xsltParserContext
));
257 * xsltFreeParserContext:
258 * @ctxt: an XSLT parser context
260 * Free up the memory allocated by @ctxt
263 xsltFreeParserContext(xsltParserContextPtr ctxt
) {
266 memset(ctxt
, -1, sizeof(xsltParserContext
));
272 * @comp: the compiled match expression
274 * @value: the first value
275 * @value2: the second value
276 * @novar: flag to set XML_XPATH_NOVAR
278 * Add an step to an XSLT Compiled Match
280 * Returns -1 in case of failure, 0 otherwise.
283 xsltCompMatchAdd(xsltParserContextPtr ctxt
, xsltCompMatchPtr comp
,
284 xsltOp op
, xmlChar
* value
, xmlChar
* value2
, int novar
)
286 if (comp
->nbStep
>= comp
->maxStep
) {
289 tmp
= (xsltStepOpPtr
) xmlRealloc(comp
->steps
, comp
->maxStep
* 2 *
292 xsltGenericError(xsltGenericErrorContext
,
293 "xsltCompMatchAdd: memory re-allocation failure.\n");
294 if (ctxt
->style
!= NULL
)
295 ctxt
->style
->errors
++;
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
);
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
);
325 if (op
== XSLT_OP_PREDICATE
) {
326 xmlXPathContextPtr xctxt
;
328 if (ctxt
->style
!= NULL
)
329 xctxt
= xmlXPathNewContext(ctxt
->style
->doc
);
331 xctxt
= xmlXPathNewContext(NULL
);
332 #ifdef XML_XPATH_NOVAR
334 xctxt
->flags
= XML_XPATH_NOVAR
;
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
++;
352 * xsltSwapTopCompMatch:
353 * @comp: the compiled match expression
355 * reverse the two top steps.
358 xsltSwapTopCompMatch(xsltCompMatchPtr comp
) {
360 int j
= comp
->nbStep
- 1;
363 register xmlChar
*tmp
;
365 register xmlXPathCompExprPtr expr
;
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
;
396 * xsltReverseCompMatch:
397 * @ctxt: the parser context
398 * @comp: the compiled match expression
400 * reverse all the stack of expressions
403 xsltReverseCompMatch(xsltParserContextPtr ctxt
, xsltCompMatchPtr comp
) {
405 int j
= comp
->nbStep
- 1;
408 register xmlChar
*tmp
;
410 register xmlXPathCompExprPtr expr
;
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
;
440 xsltCompMatchAdd(ctxt
, comp
, XSLT_OP_END
, NULL
, NULL
, 0);
443 * Detect consecutive XSLT_OP_PREDICATE and predicates on ops which
444 * haven't been optimized yet indicating a direct matching should be done.
446 for (i
= 0;i
< comp
->nbStep
- 1;i
++) {
447 xsltOp op
= comp
->steps
[i
].op
;
449 if ((op
!= XSLT_OP_ELEM
) &&
450 (op
!= XSLT_OP_ALL
) &&
451 (comp
->steps
[i
+ 1].op
== XSLT_OP_PREDICATE
)) {
454 if (comp
->pattern
[0] != '/') {
457 query
= xmlStrdup((const xmlChar
*)"//");
458 query
= xmlStrcat(query
, comp
->pattern
);
460 xmlFree((xmlChar
*) comp
->pattern
);
461 comp
->pattern
= query
;
468 /************************************************************************
470 * The interpreter for the precompiled patterns *
472 ************************************************************************/
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
));
482 else if (states
->maxstates
<= states
->nbstates
) {
485 tmp
= (xsltStepStatePtr
) xmlRealloc(states
->states
,
486 2 * states
->maxstates
* sizeof(xsltStepState
));
488 xsltGenericError(xsltGenericErrorContext
,
489 "xsltPatPushState: memory re-allocation failure.\n");
490 ctxt
->state
= XSLT_STATE_STOPPED
;
493 states
->states
= tmp
;
494 states
->maxstates
*= 2;
496 states
->states
[states
->nbstates
].step
= step
;
497 states
->states
[states
->nbstates
++].node
= node
;
499 fprintf(stderr
, "Push: %d, %s\n", step
, node
->name
);
505 xmlXPathFreeObjectWrapper(void *obj
) {
506 xmlXPathFreeObject((xmlXPathObjectPtr
) obj
);
510 * xsltTestCompMatchDirect:
511 * @ctxt: a XSLT process context
512 * @comp: the precompiled pattern
514 * @nsList: the namespaces in scope
515 * @nsNr: the number of namespaces in scope
517 * Test whether the node matches the pattern, do a direct evalutation
518 * and not a step by step evaluation.
520 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
523 xsltTestCompMatchDirect(xsltTransformContextPtr ctxt
, xsltCompMatchPtr comp
,
524 xmlNodePtr node
, xmlNsPtr
*nsList
, int nsNr
) {
525 xsltStepOpPtr sel
= NULL
;
528 xmlXPathObjectPtr list
;
534 if (XSLT_IS_RES_TREE_FRAG(doc
))
538 sel
= &comp
->steps
[0]; /* store extra in first step arbitrarily */
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
);
546 if ((list
== NULL
) || (prevdoc
!= doc
)) {
547 xmlXPathObjectPtr newlist
;
548 xmlNodePtr parent
= node
->parent
;
551 int oldNsNr
, oldContextSize
, oldProximityPosition
;
552 xmlNsPtr
*oldNamespaces
;
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
;
573 if (newlist
->type
!= XPATH_NODESET
) {
574 xmlXPathFreeObject(newlist
);
579 if ((parent
== NULL
) || (node
->doc
== NULL
) || isRVT
||
585 xmlXPathFreeObject(list
);
588 XSLT_RUNTIME_EXTRA_LST(ctxt
, sel
->lenExtra
) =
590 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
) =
592 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) =
594 XSLT_RUNTIME_EXTRA_FREE(ctxt
, sel
->lenExtra
) =
595 xmlXPathFreeObjectWrapper
;
599 if ((list
->nodesetval
== NULL
) ||
600 (list
->nodesetval
->nodeNr
<= 0)) {
602 xmlXPathFreeObject(list
);
605 /* TODO: store the index and use it for the scan */
607 for (j
= 0;j
< list
->nodesetval
->nodeNr
;j
++) {
608 if (list
->nodesetval
->nodeTab
[j
] == node
) {
610 xmlXPathFreeObject(list
);
617 xmlXPathFreeObject(list
);
622 * xsltTestPredicateMatch:
623 * @ctxt: a XSLT process context
624 * @comp: the precompiled pattern
626 * @step: the predicate step
627 * @sel: the previous step
629 * Test whether the node matches the predicate
631 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
634 xsltTestPredicateMatch(xsltTransformContextPtr ctxt
, xsltCompMatchPtr comp
,
635 xmlNodePtr node
, xsltStepOpPtr step
,
640 int pos
= 0, len
= 0;
644 if (step
->value
== NULL
)
646 if (step
->comp
== NULL
)
650 if (XSLT_IS_RES_TREE_FRAG(doc
))
656 * Recompute contextSize and proximityPosition.
658 * TODO: Make this work for additional ops. Currently, only XSLT_OP_ELEM
659 * and XSLT_OP_ALL are supported.
661 oldCS
= ctxt
->xpathCtxt
->contextSize
;
662 oldCP
= ctxt
->xpathCtxt
->proximityPosition
;
664 (sel
->op
== XSLT_OP_ELEM
) &&
665 (sel
->value
!= NULL
) &&
666 (node
->type
== XML_ELEMENT_NODE
) &&
667 (node
->parent
!= NULL
)) {
671 previous
= (xmlNodePtr
)
672 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
673 if ((previous
!= NULL
) &&
674 (previous
->parent
== node
->parent
)) {
676 * just walk back to adjust the index
679 xmlNodePtr sibling
= node
;
681 while (sibling
!= NULL
) {
682 if (sibling
== previous
)
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
)))
690 if ((sel
->value2
== NULL
) ||
691 ((sibling
->ns
!= NULL
) &&
692 (xmlStrEqual(sel
->value2
, sibling
->ns
->href
))))
695 sibling
= sibling
->prev
;
697 if (sibling
== NULL
) {
698 /* hum going backward in document order ... */
701 while (sibling
!= NULL
) {
702 if (sibling
== previous
)
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
)))
710 if ((sel
->value2
== NULL
) ||
711 ((sibling
->ns
!= NULL
) &&
712 (xmlStrEqual(sel
->value2
,
713 sibling
->ns
->href
))))
718 sibling
= sibling
->next
;
721 if (sibling
!= NULL
) {
722 pos
= XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) + indx
;
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)
728 if (node
->doc
!= NULL
) {
729 len
= XSLT_RUNTIME_EXTRA(ctxt
, sel
->lenExtra
, ival
);
731 XSLT_RUNTIME_EXTRA(ctxt
,
732 sel
->previousExtra
, ptr
) = node
;
733 XSLT_RUNTIME_EXTRA(ctxt
, sel
->indexExtra
, ival
) = pos
;
740 * recompute the index
742 xmlNodePtr parent
= node
->parent
;
743 xmlNodePtr siblings
= NULL
;
745 if (parent
) siblings
= parent
->children
;
747 while (siblings
!= NULL
) {
748 if (siblings
->type
== XML_ELEMENT_NODE
) {
749 if (siblings
== node
) {
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
))))
762 siblings
= siblings
->next
;
764 if ((parent
== NULL
) || (node
->doc
== NULL
))
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
))
776 ctxt
->xpathCtxt
->contextSize
= len
;
777 ctxt
->xpathCtxt
->proximityPosition
= pos
;
779 * If the node is in a Value Tree we cannot
782 if ((!isRVT
) && (node
->doc
!= NULL
) &&
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
;
789 } else if ((sel
!= NULL
) && (sel
->op
== XSLT_OP_ALL
) &&
790 (node
->type
== XML_ELEMENT_NODE
)) {
794 previous
= (xmlNodePtr
)
795 XSLT_RUNTIME_EXTRA(ctxt
, sel
->previousExtra
, ptr
);
796 if ((previous
!= NULL
) &&
797 (previous
->parent
== node
->parent
)) {
799 * just walk back to adjust the index
802 xmlNodePtr sibling
= node
;
804 while (sibling
!= NULL
) {
805 if (sibling
== previous
)
807 if (sibling
->type
== XML_ELEMENT_NODE
)
809 sibling
= sibling
->prev
;
811 if (sibling
== NULL
) {
812 /* hum going backward in document order ... */
815 while (sibling
!= NULL
) {
816 if (sibling
== previous
)
818 if (sibling
->type
== XML_ELEMENT_NODE
)
820 sibling
= sibling
->next
;
823 if (sibling
!= NULL
) {
824 pos
= XSLT_RUNTIME_EXTRA(ctxt
,
825 sel
->indexExtra
, ival
) + indx
;
827 * If the node is in a Value Tree we cannot
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
;
839 * recompute the index
841 xmlNodePtr parent
= node
->parent
;
842 xmlNodePtr siblings
= NULL
;
844 if (parent
) siblings
= parent
->children
;
846 while (siblings
!= NULL
) {
847 if (siblings
->type
== XML_ELEMENT_NODE
) {
849 if (siblings
== node
) {
853 siblings
= siblings
->next
;
855 if ((parent
== NULL
) || (node
->doc
== NULL
))
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
))
867 ctxt
->xpathCtxt
->contextSize
= len
;
868 ctxt
->xpathCtxt
->proximityPosition
= pos
;
870 * If the node is in a Value Tree we cannot
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
;
881 oldNode
= ctxt
->node
;
884 match
= xsltEvalXPathPredicate(ctxt
, step
->comp
, comp
->nsList
, comp
->nsNr
);
887 ctxt
->xpathCtxt
->contextSize
= oldCS
;
888 ctxt
->xpathCtxt
->proximityPosition
= oldCP
;
890 ctxt
->node
= oldNode
;
897 * @ctxt: a XSLT process context
898 * @comp: the precompiled pattern
900 * @mode: the mode name or NULL
901 * @modeURI: the mode URI or NULL
903 * Test whether the node matches the pattern
905 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
908 xsltTestCompMatch(xsltTransformContextPtr ctxt
, xsltCompMatchPtr comp
,
909 xmlNodePtr matchNode
, const xmlChar
*mode
,
910 const xmlChar
*modeURI
) {
913 xmlNodePtr node
= matchNode
;
915 xsltStepOpPtr step
, sel
= NULL
;
916 xsltStepStates states
= {0, 0, NULL
}; /* // may require backtrack */
918 if ((comp
== NULL
) || (node
== NULL
) || (ctxt
== NULL
)) {
919 xsltTransformError(ctxt
, NULL
, node
,
920 "xsltTestCompMatch: null arg\n");
924 if (comp
->mode
== NULL
)
927 * both mode strings must be interned on the stylesheet dictionary
929 if (comp
->mode
!= mode
)
932 if (comp
->mode
!= NULL
)
935 if (modeURI
!= NULL
) {
936 if (comp
->modeURI
== NULL
)
939 * both modeURI strings must be interned on the stylesheet dictionary
941 if (comp
->modeURI
!= modeURI
)
944 if (comp
->modeURI
!= NULL
)
948 /* Some XPath functions rely on inst being set correctly. */
949 oldInst
= ctxt
->inst
;
950 ctxt
->inst
= comp
->node
;
954 for (;i
< comp
->nbStep
;i
++) {
955 step
= &comp
->steps
[i
];
956 if (step
->op
!= XSLT_OP_PREDICATE
)
962 if ((node
->type
== XML_DOCUMENT_NODE
) ||
963 #ifdef LIBXML_DOCB_ENABLED
964 (node
->type
== XML_DOCB_DOCUMENT_NODE
) ||
966 (node
->type
== XML_HTML_DOCUMENT_NODE
))
968 if ((node
->type
== XML_ELEMENT_NODE
) && (node
->name
[0] == ' '))
972 if (node
->type
!= XML_ELEMENT_NODE
)
974 if (step
->value
== NULL
)
976 if (step
->value
[0] != node
->name
[0])
978 if (!xmlStrEqual(step
->value
, node
->name
))
982 if (node
->ns
== NULL
) {
983 if (step
->value2
!= NULL
)
985 } else if (node
->ns
->href
!= NULL
) {
986 if (step
->value2
== NULL
)
988 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
993 if (node
->type
!= XML_ATTRIBUTE_NODE
)
995 if (step
->value
!= NULL
) {
996 if (step
->value
[0] != node
->name
[0])
998 if (!xmlStrEqual(step
->value
, node
->name
))
1001 /* Namespace test */
1002 if (node
->ns
== NULL
) {
1003 if (step
->value2
!= NULL
)
1005 } else if (step
->value2
!= NULL
) {
1006 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
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
) ||
1016 (node
->type
== XML_NAMESPACE_DECL
))
1018 node
= node
->parent
;
1021 if (step
->value
== NULL
)
1023 if (step
->value
[0] != node
->name
[0])
1025 if (!xmlStrEqual(step
->value
, node
->name
))
1027 /* Namespace test */
1028 if (node
->ns
== NULL
) {
1029 if (step
->value2
!= NULL
)
1031 } else if (node
->ns
->href
!= NULL
) {
1032 if (step
->value2
== NULL
)
1034 if (!xmlStrEqual(step
->value2
, node
->ns
->href
))
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
)
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
))
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
) ||
1059 (node
->type
== XML_NAMESPACE_DECL
))
1061 node
= node
->parent
;
1062 if ((step
->op
!= XSLT_OP_ELEM
) && step
->op
!= XSLT_OP_ALL
) {
1063 xsltPatPushState(ctxt
, &states
, i
, node
);
1067 if (step
->value
== NULL
) {
1068 xsltPatPushState(ctxt
, &states
, i
- 1, node
);
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
)
1079 } else if (node
->ns
->href
!= NULL
) {
1080 if ((step
->value2
!= NULL
) &&
1081 (xmlStrEqual(step
->value2
, node
->ns
->href
)))
1085 node
= node
->parent
;
1089 xsltPatPushState(ctxt
, &states
, i
- 1, node
);
1092 /* TODO Handle IDs decently, must be done differently */
1095 if (node
->type
!= XML_ELEMENT_NODE
)
1098 id
= xmlGetID(node
->doc
, step
->value
);
1099 if ((id
== NULL
) || (id
->parent
!= node
))
1107 list
= xsltGetKey(ctxt
, step
->value
,
1108 step
->value3
, step
->value2
);
1111 for (indx
= 0;indx
< list
->nodeNr
;indx
++)
1112 if (list
->nodeTab
[indx
] == node
)
1114 if (indx
>= list
->nodeNr
)
1119 if (node
->type
!= XML_ELEMENT_NODE
)
1121 if (node
->ns
== NULL
) {
1122 if (step
->value
!= NULL
)
1124 } else if (node
->ns
->href
!= NULL
) {
1125 if (step
->value
== NULL
)
1127 if (!xmlStrEqual(step
->value
, node
->ns
->href
))
1132 if (node
->type
!= XML_ELEMENT_NODE
)
1135 case XSLT_OP_PREDICATE
: {
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.
1144 found
= xsltTestCompMatchDirect(ctxt
, comp
, matchNode
,
1145 comp
->nsList
, comp
->nsNr
);
1149 if (!xsltTestPredicateMatch(ctxt
, comp
, node
, step
, sel
))
1155 if (node
->type
!= XML_PI_NODE
)
1157 if (step
->value
!= NULL
) {
1158 if (!xmlStrEqual(step
->value
, node
->name
))
1162 case XSLT_OP_COMMENT
:
1163 if (node
->type
!= XML_COMMENT_NODE
)
1167 if ((node
->type
!= XML_TEXT_NODE
) &&
1168 (node
->type
!= XML_CDATA_SECTION_NODE
))
1172 switch (node
->type
) {
1173 case XML_ELEMENT_NODE
:
1174 case XML_CDATA_SECTION_NODE
:
1176 case XML_COMMENT_NODE
:
1188 ctxt
->inst
= oldInst
;
1189 if (states
.states
!= NULL
) {
1190 /* Free the rollback states */
1191 xmlFree(states
.states
);
1195 /* got an error try to rollback */
1196 if (states
.states
== NULL
|| states
.nbstates
<= 0) {
1201 i
= states
.states
[states
.nbstates
].step
;
1202 node
= states
.states
[states
.nbstates
].node
;
1204 fprintf(stderr
, "Pop: %d, %s\n", i
, node
->name
);
1210 * xsltTestCompMatchList:
1211 * @ctxt: a XSLT process context
1213 * @comp: the precompiled pattern list
1215 * Test whether the node matches one of the patterns in the list
1217 * Returns 1 if it matches, 0 if it doesn't and -1 in case of failure
1220 xsltTestCompMatchList(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
1221 xsltCompMatchPtr comp
) {
1224 if ((ctxt
== NULL
) || (node
== NULL
))
1226 while (comp
!= NULL
) {
1227 ret
= xsltTestCompMatch(ctxt
, comp
, node
, NULL
, NULL
);
1235 /************************************************************************
1237 * Dedicated parser for templates *
1239 ************************************************************************/
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
1246 #define SKIP_BLANKS \
1247 while (IS_BLANK_CH(CUR)) NEXT
1249 #define CURRENT (*ctxt->cur)
1250 #define NEXT ((*ctxt->cur) ? ctxt->cur++: ctxt->cur)
1253 #define PUSH(op, val, val2, novar) \
1254 if (xsltCompMatchAdd(ctxt, ctxt->comp, (op), (val), (val2), (novar))) goto error;
1257 xsltSwapTopCompMatch(ctxt->comp);
1259 #define XSLT_ERROR(X) \
1260 { xsltError(ctxt, __FILE__, __LINE__, X); \
1261 ctxt->error = (X); return; }
1263 #define XSLT_ERROR0(X) \
1264 { xsltError(ctxt, __FILE__, __LINE__, X); \
1265 ctxt->error = (X); return(0); }
1269 * @ctxt: the XPath Parser context
1271 * Parse an XPath Litteral:
1273 * [29] Literal ::= '"' [^"]* '"'
1276 * Returns the Literal parsed or NULL
1280 xsltScanLiteral(xsltParserContextPtr ctxt
) {
1281 const xmlChar
*q
, *cur
;
1282 xmlChar
*ret
= NULL
;
1289 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1290 while ((IS_CHAR(val
)) && (val
!= '"')) {
1292 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1294 if (!IS_CHAR(val
)) {
1298 ret
= xmlStrndup(q
, cur
- q
);
1302 } else if (CUR
== '\'') {
1305 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1306 while ((IS_CHAR(val
)) && (val
!= '\'')) {
1308 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1310 if (!IS_CHAR(val
)) {
1314 ret
= xmlStrndup(q
, cur
- q
);
1319 /* XP_ERROR(XPATH_START_LITERAL_ERROR); */
1328 * @ctxt: the XPath Parser context
1330 * Parses a non qualified name
1332 * Returns the Name parsed or NULL
1336 xsltScanNCName(xsltParserContextPtr ctxt
) {
1337 const xmlChar
*q
, *cur
;
1338 xmlChar
*ret
= NULL
;
1344 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1345 if (!IS_LETTER(val
) && (val
!= '_'))
1348 while ((IS_LETTER(val
)) || (IS_DIGIT(val
)) ||
1349 (val
== '.') || (val
== '-') ||
1351 (IS_COMBINING(val
)) ||
1352 (IS_EXTENDER(val
))) {
1354 val
= xmlStringCurrentChar(NULL
, cur
, &len
);
1356 ret
= xmlStrndup(q
, cur
- q
);
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
1368 * Compile the XSLT LocationIdKeyPattern
1369 * [3] IdKeyPattern ::= 'id' '(' Literal ')'
1370 * | 'key' '(' Literal ',' Literal ')'
1372 * also handle NodeType and PI from:
1374 * [7] NodeTest ::= NameTest
1375 * | NodeType '(' ')'
1376 * | 'processing-instruction' '(' Literal ')'
1379 xsltCompileIdKeyPattern(xsltParserContextPtr ctxt
, xmlChar
*name
,
1380 int aid
, int novar
, xsltAxis axis
) {
1381 xmlChar
*lit
= NULL
;
1382 xmlChar
*lit2
= NULL
;
1385 xsltTransformError(NULL
, NULL
, NULL
,
1386 "xsltCompileIdKeyPattern : ( expected\n");
1390 if ((aid
) && (xmlStrEqual(name
, (const xmlChar
*)"id"))) {
1392 xsltTransformError(NULL
, NULL
, NULL
,
1393 "xsltCompileIdKeyPattern : NodeTest expected\n");
1399 lit
= xsltScanLiteral(ctxt
);
1401 xsltTransformError(NULL
, NULL
, NULL
,
1402 "xsltCompileIdKeyPattern : Literal expected\n");
1407 xsltTransformError(NULL
, NULL
, NULL
,
1408 "xsltCompileIdKeyPattern : ) expected\n");
1414 PUSH(XSLT_OP_ID
, lit
, NULL
, novar
);
1416 } else if ((aid
) && (xmlStrEqual(name
, (const xmlChar
*)"key"))) {
1418 xsltTransformError(NULL
, NULL
, NULL
,
1419 "xsltCompileIdKeyPattern : NodeTest expected\n");
1425 lit
= xsltScanLiteral(ctxt
);
1427 xsltTransformError(NULL
, NULL
, NULL
,
1428 "xsltCompileIdKeyPattern : Literal expected\n");
1433 xsltTransformError(NULL
, NULL
, NULL
,
1434 "xsltCompileIdKeyPattern : , expected\n");
1441 lit2
= xsltScanLiteral(ctxt
);
1443 xsltTransformError(NULL
, NULL
, NULL
,
1444 "xsltCompileIdKeyPattern : Literal expected\n");
1450 xsltTransformError(NULL
, NULL
, NULL
,
1451 "xsltCompileIdKeyPattern : ) expected\n");
1458 /* URGENT TODO: support namespace in keys */
1459 PUSH(XSLT_OP_KEY
, lit
, lit2
, novar
);
1462 } else if (xmlStrEqual(name
, (const xmlChar
*)"processing-instruction")) {
1466 lit
= xsltScanLiteral(ctxt
);
1468 xsltTransformError(NULL
, NULL
, NULL
,
1469 "xsltCompileIdKeyPattern : Literal expected\n");
1474 xsltTransformError(NULL
, NULL
, NULL
,
1475 "xsltCompileIdKeyPattern : ) expected\n");
1482 PUSH(XSLT_OP_PI
, lit
, NULL
, novar
);
1484 } else if (xmlStrEqual(name
, (const xmlChar
*)"text")) {
1488 xsltTransformError(NULL
, NULL
, NULL
,
1489 "xsltCompileIdKeyPattern : ) expected\n");
1494 PUSH(XSLT_OP_TEXT
, NULL
, NULL
, novar
);
1495 } else if (xmlStrEqual(name
, (const xmlChar
*)"comment")) {
1499 xsltTransformError(NULL
, NULL
, NULL
,
1500 "xsltCompileIdKeyPattern : ) expected\n");
1505 PUSH(XSLT_OP_COMMENT
, NULL
, NULL
, novar
);
1506 } else if (xmlStrEqual(name
, (const xmlChar
*)"node")) {
1510 xsltTransformError(NULL
, NULL
, NULL
,
1511 "xsltCompileIdKeyPattern : ) expected\n");
1516 if (axis
== AXIS_ATTRIBUTE
) {
1517 PUSH(XSLT_OP_ATTR
, NULL
, NULL
, novar
);
1520 PUSH(XSLT_OP_NODE
, NULL
, NULL
, novar
);
1523 xsltTransformError(NULL
, NULL
, NULL
,
1524 "xsltCompileIdKeyPattern : expecting 'key' or 'id' or node type\n");
1528 xsltTransformError(NULL
, NULL
, NULL
,
1529 "xsltCompileIdKeyPattern : node type\n");
1538 * xsltCompileStepPattern:
1539 * @ctxt: the compilation context
1540 * @token: a posible precompiled name
1541 * @novar: flag to prohibit xslt variables from pattern
1543 * Compile the XSLT StepPattern and generates a precompiled
1544 * form suitable for fast matching.
1546 * [5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
1547 * [6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
1548 * | ('child' | 'attribute') '::'
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
1560 xsltCompileStepPattern(xsltParserContextPtr ctxt
, xmlChar
*token
, int novar
) {
1561 xmlChar
*name
= NULL
;
1562 const xmlChar
*URI
= NULL
;
1563 xmlChar
*URL
= NULL
;
1568 if ((token
== NULL
) && (CUR
== '@')) {
1570 axis
= AXIS_ATTRIBUTE
;
1574 token
= xsltScanNCName(ctxt
);
1575 if (token
== NULL
) {
1578 if (axis
== AXIS_ATTRIBUTE
) {
1579 PUSH(XSLT_OP_ATTR
, NULL
, NULL
, novar
);
1582 PUSH(XSLT_OP_ALL
, NULL
, NULL
, novar
);
1584 goto parse_predicate
;
1586 xsltTransformError(NULL
, NULL
, NULL
,
1587 "xsltCompileStepPattern : Name expected\n");
1596 xsltCompileIdKeyPattern(ctxt
, token
, 0, novar
, axis
);
1601 } else if (CUR
== ':') {
1604 xmlChar
*prefix
= token
;
1608 * This is a namespace match
1610 token
= xsltScanNCName(ctxt
);
1611 ns
= xmlSearchNs(ctxt
->doc
, ctxt
->elem
, prefix
);
1613 xsltTransformError(NULL
, NULL
, NULL
,
1614 "xsltCompileStepPattern : no namespace bound to prefix %s\n",
1621 URL
= xmlStrdup(ns
->href
);
1625 if (token
== NULL
) {
1628 if (axis
== AXIS_ATTRIBUTE
) {
1629 PUSH(XSLT_OP_ATTR
, NULL
, URL
, novar
);
1633 PUSH(XSLT_OP_NS
, URL
, NULL
, novar
);
1637 xsltTransformError(NULL
, NULL
, NULL
,
1638 "xsltCompileStepPattern : Name expected\n");
1644 if (axis
== AXIS_ATTRIBUTE
) {
1645 PUSH(XSLT_OP_ATTR
, token
, URL
, novar
);
1650 PUSH(XSLT_OP_ELEM
, token
, URL
, novar
);
1657 xsltTransformError(NULL
, NULL
, NULL
,
1658 "xsltCompileStepPattern : NodeTest expected\n");
1663 if (xmlStrEqual(token
, (const xmlChar
*) "child")) {
1665 } else if (xmlStrEqual(token
, (const xmlChar
*) "attribute")) {
1666 axis
= AXIS_ATTRIBUTE
;
1668 xsltTransformError(NULL
, NULL
, NULL
,
1669 "xsltCompileStepPattern : 'child' or 'attribute' expected\n");
1676 token
= xsltScanNCName(ctxt
);
1677 goto parse_node_test
;
1680 URI
= xsltGetQNameURI(ctxt
->elem
, &token
);
1681 if (token
== NULL
) {
1686 URL
= xmlStrdup(URI
);
1687 if (axis
== AXIS_ATTRIBUTE
) {
1688 PUSH(XSLT_OP_ATTR
, token
, URL
, novar
);
1693 PUSH(XSLT_OP_ELEM
, token
, URL
, novar
);
1701 while (CUR
== '[') {
1703 xmlChar
*ret
= NULL
;
1709 /* Skip over nested predicates */
1712 else if (CUR
== ']') {
1716 } else if (CUR
== '"') {
1718 while ((CUR
!= 0) && (CUR
!= '"'))
1720 } else if (CUR
== '\'') {
1722 while ((CUR
!= 0) && (CUR
!= '\''))
1728 xsltTransformError(NULL
, NULL
, NULL
,
1729 "xsltCompileStepPattern : ']' expected\n");
1733 ret
= xmlStrndup(q
, CUR_PTR
- q
);
1734 PUSH(XSLT_OP_PREDICATE
, ret
, NULL
, novar
);
1736 /* push the predicate lower than local test */
1750 * xsltCompileRelativePathPattern:
1751 * @comp: the compilation context
1752 * @token: a posible precompiled name
1753 * @novar: flag to prohibit xslt variables
1755 * Compile the XSLT RelativePathPattern and generates a precompiled
1756 * form suitable for fast matching.
1758 * [4] RelativePathPattern ::= StepPattern
1759 * | RelativePathPattern '/' StepPattern
1760 * | RelativePathPattern '//' StepPattern
1763 xsltCompileRelativePathPattern(xsltParserContextPtr ctxt
, xmlChar
*token
, int novar
) {
1764 xsltCompileStepPattern(ctxt
, token
, novar
);
1768 while ((CUR
!= 0) && (CUR
!= '|')) {
1769 if ((CUR
== '/') && (NXT(1) == '/')) {
1770 PUSH(XSLT_OP_ANCESTOR
, NULL
, NULL
, novar
);
1774 xsltCompileStepPattern(ctxt
, NULL
, novar
);
1775 } else if (CUR
== '/') {
1776 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1779 if ((CUR
!= 0) && (CUR
!= '|')) {
1780 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1794 * xsltCompileLocationPathPattern:
1795 * @ctxt: the compilation context
1796 * @novar: flag to prohibit xslt variables
1798 * Compile the XSLT LocationPathPattern and generates a precompiled
1799 * form suitable for fast matching.
1801 * [2] LocationPathPattern ::= '/' RelativePathPattern?
1802 * | IdKeyPattern (('/' | '//') RelativePathPattern)?
1803 * | '//'? RelativePathPattern
1806 xsltCompileLocationPathPattern(xsltParserContextPtr ctxt
, int novar
) {
1808 if ((CUR
== '/') && (NXT(1) == '/')) {
1810 * since we reverse the query
1811 * a leading // can be safely ignored
1815 ctxt
->comp
->priority
= 0.5; /* '//' means not 0 priority */
1816 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1817 } else if (CUR
== '/') {
1819 * We need to find root as the parent
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
);
1828 } else if (CUR
== '*') {
1829 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1830 } else if (CUR
== '@') {
1831 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1834 name
= xsltScanNCName(ctxt
);
1836 xsltTransformError(NULL
, NULL
, NULL
,
1837 "xsltCompileLocationPathPattern : Name expected\n");
1842 if ((CUR
== '(') && !xmlXPathIsNodeType(name
)) {
1843 xsltCompileIdKeyPattern(ctxt
, name
, 1, novar
, 0);
1846 if ((CUR
== '/') && (NXT(1) == '/')) {
1847 PUSH(XSLT_OP_ANCESTOR
, NULL
, NULL
, novar
);
1851 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1852 } else if (CUR
== '/') {
1853 PUSH(XSLT_OP_PARENT
, NULL
, NULL
, novar
);
1856 xsltCompileRelativePathPattern(ctxt
, NULL
, novar
);
1860 xsltCompileRelativePathPattern(ctxt
, name
, novar
);
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
1875 * Compile the XSLT pattern and generates a list of precompiled form suitable
1876 * for fast matching.
1878 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
1880 * Returns the generated pattern list or NULL in case of failure
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
;
1891 if (pattern
== NULL
) {
1892 xsltTransformError(NULL
, NULL
, node
,
1893 "xsltCompilePattern : NULL pattern\n");
1897 ctxt
= xsltNewParserContext(style
, runtime
);
1903 while (pattern
[current
] != 0) {
1905 while (IS_BLANK_CH(pattern
[current
]))
1909 while ((pattern
[end
] != 0) && ((pattern
[end
] != '|') || (level
!= 0))) {
1910 if (pattern
[end
] == '[')
1912 else if (pattern
[end
] == ']')
1914 else if (pattern
[end
] == '\'') {
1916 while ((pattern
[end
] != 0) && (pattern
[end
] != '\''))
1918 } else if (pattern
[end
] == '"') {
1920 while ((pattern
[end
] != 0) && (pattern
[end
] != '"'))
1923 if (pattern
[end
] == 0)
1927 if (current
== end
) {
1928 xsltTransformError(NULL
, NULL
, node
,
1929 "xsltCompilePattern : NULL pattern\n");
1932 element
= xsltNewCompMatch();
1933 if (element
== NULL
) {
1938 else if (previous
!= NULL
)
1939 previous
->next
= element
;
1942 ctxt
->comp
= element
;
1943 ctxt
->base
= xmlStrndup(&pattern
[start
], end
- start
);
1944 if (ctxt
->base
== NULL
)
1946 ctxt
->cur
= &(ctxt
->base
)[current
- start
];
1947 element
->pattern
= ctxt
->base
;
1948 element
->node
= node
;
1949 element
->nsList
= xmlGetNsList(doc
, node
);
1951 if (element
->nsList
!= NULL
) {
1952 while (element
->nsList
[j
] != NULL
)
1956 element
->novar
= novar
;
1959 #ifdef WITH_XSLT_DEBUG_PATTERN
1960 xsltGenericDebug(xsltGenericDebugContext
,
1961 "xsltCompilePattern : parsing '%s'\n",
1965 Preset default priority to be zero.
1966 This may be changed by xsltCompileLocationPathPattern.
1968 element
->priority
= 0;
1969 xsltCompileLocationPathPattern(ctxt
, novar
);
1971 xsltTransformError(NULL
, style
, node
,
1972 "xsltCompilePattern : failed to compile '%s'\n",
1974 if (style
!= NULL
) style
->errors
++;
1979 * Reverse for faster interpretation.
1981 xsltReverseCompMatch(ctxt
, element
);
1984 * Set-up the priority
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;
2014 element
->priority
= 0.5;
2017 #ifdef WITH_XSLT_DEBUG_PATTERN
2018 xsltGenericDebug(xsltGenericDebugContext
,
2019 "xsltCompilePattern : parsed %s, default priority %f\n",
2020 element
->pattern
, element
->priority
);
2022 if (pattern
[end
] == '|')
2027 xsltTransformError(NULL
, style
, node
,
2028 "xsltCompilePattern : NULL pattern\n");
2029 if (style
!= NULL
) style
->errors
++;
2033 xsltFreeParserContext(ctxt
);
2038 xsltFreeParserContext(ctxt
);
2040 xsltFreeCompMatchList(first
);
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
2052 * Compile the XSLT pattern and generates a list of precompiled form suitable
2053 * for fast matching.
2055 * [1] Pattern ::= LocationPathPattern | Pattern '|' LocationPathPattern
2057 * Returns the generated pattern list or NULL in case of failure
2061 xsltCompilePattern(const xmlChar
*pattern
, xmlDocPtr doc
,
2062 xmlNodePtr node
, xsltStylesheetPtr style
,
2063 xsltTransformContextPtr runtime
) {
2064 return (xsltCompilePatternInternal(pattern
, doc
, node
, style
, runtime
, 0));
2067 /************************************************************************
2069 * Module interfaces *
2071 ************************************************************************/
2075 * @style: an XSLT stylesheet
2076 * @cur: an XSLT template
2077 * @mode: the mode name or NULL
2078 * @modeURI: the mode URI or NULL
2080 * Register the XSLT pattern associated to @cur
2082 * Returns -1 in case of error, 0 otherwise
2085 xsltAddTemplate(xsltStylesheetPtr style
, xsltTemplatePtr cur
,
2086 const xmlChar
*mode
, const xmlChar
*modeURI
) {
2087 xsltCompMatchPtr pat
, list
, next
;
2089 * 'top' will point to style->xxxMatch ptr - declaring as 'void'
2090 * avoids gcc 'type-punned pointer' warning.
2093 const xmlChar
*name
= NULL
;
2094 float priority
; /* the priority */
2096 if ((style
== NULL
) || (cur
== NULL
))
2099 /* Register named template */
2100 if (cur
->name
!= NULL
) {
2101 if (style
->namedTemplates
== NULL
) {
2102 style
->namedTemplates
= xmlHashCreate(10);
2103 if (style
->namedTemplates
== NULL
)
2107 void *dup
= xmlHashLookup2(style
->namedTemplates
, cur
->name
,
2110 xsltTransformError(NULL
, style
, cur
->elem
,
2111 "xsl:template: error duplicate name '%s'\n",
2118 xmlHashAddEntry2(style
->namedTemplates
, cur
->name
, cur
->nameURI
, cur
);
2121 if (cur
->match
== NULL
)
2124 priority
= cur
->priority
;
2125 pat
= xsltCompilePatternInternal(cur
->match
, style
->doc
, cur
->elem
,
2134 pat
->template = cur
;
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
;
2143 * insert it in the hash table list corresponding to its lookup name
2145 switch (pat
->steps
[0].op
) {
2147 if (pat
->steps
[0].value
!= NULL
)
2148 name
= pat
->steps
[0].value
;
2150 top
= &(style
->attrMatch
);
2152 case XSLT_OP_PARENT
:
2153 case XSLT_OP_ANCESTOR
:
2154 top
= &(style
->elemMatch
);
2157 top
= &(style
->rootMatch
);
2160 top
= &(style
->keyMatch
);
2163 /* TODO optimize ID !!! */
2166 top
= &(style
->elemMatch
);
2169 case XSLT_OP_PREDICATE
:
2170 xsltTransformError(NULL
, style
, NULL
,
2171 "xsltAddTemplate: invalid compiled pattern\n");
2172 xsltFreeCompMatch(pat
);
2175 * TODO: some flags at the top level about type based patterns
2176 * would be faster than inclusion in the hash table.
2179 if (pat
->steps
[0].value
!= NULL
)
2180 name
= pat
->steps
[0].value
;
2182 top
= &(style
->piMatch
);
2184 case XSLT_OP_COMMENT
:
2185 top
= &(style
->commentMatch
);
2188 top
= &(style
->textMatch
);
2192 if (pat
->steps
[0].value
!= NULL
)
2193 name
= pat
->steps
[0].value
;
2195 top
= &(style
->elemMatch
);
2199 if (style
->templatesHash
== NULL
) {
2200 style
->templatesHash
= xmlHashCreate(1024);
2201 if (style
->templatesHash
== NULL
) {
2202 xsltFreeCompMatch(pat
);
2205 xmlHashAddEntry3(style
->templatesHash
, name
, mode
, modeURI
, pat
);
2207 list
= (xsltCompMatchPtr
) xmlHashLookup3(style
->templatesHash
,
2208 name
, mode
, modeURI
);
2210 xmlHashAddEntry3(style
->templatesHash
, name
,
2211 mode
, modeURI
, pat
);
2214 * Note '<=' since one must choose among the matching
2215 * template rules that are left, the one that occurs
2216 * last in the stylesheet
2218 if (list
->priority
<= pat
->priority
) {
2220 xmlHashUpdateEntry3(style
->templatesHash
, name
,
2221 mode
, modeURI
, pat
, NULL
);
2223 while (list
->next
!= NULL
) {
2224 if (list
->next
->priority
<= pat
->priority
)
2228 pat
->next
= list
->next
;
2233 } else if (top
!= NULL
) {
2238 } else if (list
->priority
<= pat
->priority
) {
2242 while (list
->next
!= NULL
) {
2243 if (list
->next
->priority
<= pat
->priority
)
2247 pat
->next
= list
->next
;
2251 xsltTransformError(NULL
, style
, NULL
,
2252 "xsltAddTemplate: invalid compiled pattern\n");
2253 xsltFreeCompMatch(pat
);
2256 #ifdef WITH_XSLT_DEBUG_PATTERN
2258 xsltGenericDebug(xsltGenericDebugContext
,
2259 "added pattern : '%s' mode '%s' priority %f\n",
2260 pat
->pattern
, pat
->mode
, pat
->priority
);
2262 xsltGenericDebug(xsltGenericDebugContext
,
2263 "added pattern : '%s' priority %f\n",
2264 pat
->pattern
, pat
->priority
);
2273 xsltComputeAllKeys(xsltTransformContextPtr ctxt
, xmlNodePtr contextNode
)
2275 if ((ctxt
== NULL
) || (contextNode
== NULL
)) {
2276 xsltTransformError(ctxt
, NULL
, ctxt
->inst
,
2277 "Internal error in xsltComputeAllKeys(): "
2278 "Bad arguments.\n");
2282 if (ctxt
->document
== NULL
) {
2284 * The document info will only be NULL if we have a RTF.
2286 if (contextNode
->doc
->_private
!= NULL
)
2287 goto doc_info_mismatch
;
2289 * On-demand creation of the document info (needed for keys).
2291 ctxt
->document
= xsltNewDocument(ctxt
, contextNode
->doc
);
2292 if (ctxt
->document
== NULL
)
2295 return xsltInitAllDocKeys(ctxt
);
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
;
2308 * @ctxt: a XSLT process context
2309 * @node: the node being processed
2310 * @style: the current style
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.
2315 * Returns the xsltTemplatePtr or NULL if not found
2318 xsltGetTemplate(xsltTransformContextPtr ctxt
, xmlNodePtr node
,
2319 xsltStylesheetPtr style
)
2321 xsltStylesheetPtr curstyle
;
2322 xsltTemplatePtr ret
= NULL
;
2323 const xmlChar
*name
= NULL
;
2324 xsltCompMatchPtr list
= NULL
;
2328 if ((ctxt
== NULL
) || (node
== NULL
))
2331 if (style
== NULL
) {
2332 curstyle
= ctxt
->style
;
2334 curstyle
= xsltNextImport(style
);
2337 while ((curstyle
!= NULL
) && (curstyle
!= style
)) {
2338 priority
= XSLT_PAT_NO_PRIORITY
;
2339 /* TODO : handle IDs/keys here ! */
2340 if (curstyle
->templatesHash
!= NULL
) {
2342 * Use the top name as selector
2344 switch (node
->type
) {
2345 case XML_ELEMENT_NODE
:
2346 if (node
->name
[0] == ' ')
2348 case XML_ATTRIBUTE_NODE
:
2352 case XML_DOCUMENT_NODE
:
2353 case XML_HTML_DOCUMENT_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
:
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
:
2377 * find the list of applicable expressions based on the name
2379 list
= (xsltCompMatchPtr
) xmlHashLookup3(curstyle
->templatesHash
,
2380 name
, ctxt
->mode
, ctxt
->modeURI
);
2383 while (list
!= NULL
) {
2384 if (xsltTestCompMatch(ctxt
, list
, node
,
2385 ctxt
->mode
, ctxt
->modeURI
)) {
2386 ret
= list
->template;
2387 priority
= list
->priority
;
2395 * find alternate generic matches
2397 switch (node
->type
) {
2398 case XML_ELEMENT_NODE
:
2399 if (node
->name
[0] == ' ')
2400 list
= curstyle
->rootMatch
;
2402 list
= curstyle
->elemMatch
;
2403 if (node
->psvi
!= NULL
) keyed
= 1;
2405 case XML_ATTRIBUTE_NODE
: {
2408 list
= curstyle
->attrMatch
;
2409 attr
= (xmlAttrPtr
) node
;
2410 if (attr
->psvi
!= NULL
) keyed
= 1;
2414 list
= curstyle
->piMatch
;
2415 if (node
->psvi
!= NULL
) keyed
= 1;
2417 case XML_DOCUMENT_NODE
:
2418 case XML_HTML_DOCUMENT_NODE
: {
2421 list
= curstyle
->rootMatch
;
2422 doc
= (xmlDocPtr
) node
;
2423 if (doc
->psvi
!= NULL
) keyed
= 1;
2427 case XML_CDATA_SECTION_NODE
:
2428 list
= curstyle
->textMatch
;
2429 if (node
->psvi
!= NULL
) keyed
= 1;
2431 case XML_COMMENT_NODE
:
2432 list
= curstyle
->commentMatch
;
2433 if (node
->psvi
!= NULL
) keyed
= 1;
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
:
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
:
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
;
2462 * Some of the tests for elements can also apply to documents
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
;
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
;
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
;
2507 else if (ctxt
->hasTemplKeyPatterns
&&
2508 ((ctxt
->document
== NULL
) ||
2509 (ctxt
->document
->nbKeysComputed
< ctxt
->nbKeys
)))
2512 * Compute all remaining keys for this document.
2514 * REVISIT TODO: I think this could be further optimized.
2516 if (xsltComputeAllKeys(ctxt
, node
) == -1)
2519 switch (node
->type
) {
2520 case XML_ELEMENT_NODE
:
2521 if (node
->psvi
!= NULL
) keyed
= 1;
2523 case XML_ATTRIBUTE_NODE
:
2524 if (((xmlAttrPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2527 case XML_CDATA_SECTION_NODE
:
2528 case XML_COMMENT_NODE
:
2530 if (node
->psvi
!= NULL
) keyed
= 1;
2532 case XML_DOCUMENT_NODE
:
2533 case XML_HTML_DOCUMENT_NODE
:
2534 if (((xmlDocPtr
) node
)->psvi
!= NULL
) keyed
= 1;
2546 * Cycle on next curstylesheet import.
2548 curstyle
= xsltNextImport(curstyle
);
2556 * xsltCleanupTemplates:
2557 * @style: an XSLT stylesheet
2559 * Cleanup the state of the templates used by the stylesheet and
2560 * the ones it imports.
2563 xsltCleanupTemplates(xsltStylesheetPtr style ATTRIBUTE_UNUSED
) {
2567 * xsltFreeTemplateHashes:
2568 * @style: an XSLT stylesheet
2570 * Free up the memory used by xsltAddTemplate/xsltGetTemplate mechanism
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
);