2 #include "libexslt/libexslt.h"
4 #if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
5 #include <win32config.h>
12 #include <libxml/tree.h>
13 #include <libxml/xpath.h>
14 #include <libxml/xpathInternals.h>
15 #include <libxml/hash.h>
16 #include <libxml/debugXML.h>
18 #include <libxslt/xsltutils.h>
19 #include <libxslt/variables.h>
20 #include <libxslt/xsltInternals.h>
21 #include <libxslt/extensions.h>
22 #include <libxslt/transform.h>
23 #include <libxslt/imports.h>
27 typedef struct _exsltFuncFunctionData exsltFuncFunctionData
;
28 struct _exsltFuncFunctionData
{
29 int nargs
; /* number of arguments to the function */
30 xmlNodePtr content
; /* the func:fuction template content */
33 typedef struct _exsltFuncData exsltFuncData
;
34 struct _exsltFuncData
{
35 xmlHashTablePtr funcs
; /* pointer to the stylesheet module data */
36 xmlXPathObjectPtr result
; /* returned by func:result */
37 int error
; /* did an error occur? */
38 xmlDocPtr RVT
; /* result tree fragment */
41 typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp
;
42 struct _exsltFuncResultPreComp
{
44 xmlXPathCompExprPtr select
;
49 /* Used for callback function in exsltInitFunc */
50 typedef struct _exsltFuncImportRegData exsltFuncImportRegData
;
51 struct _exsltFuncImportRegData
{
52 xsltTransformContextPtr ctxt
;
56 static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt
,
58 static exsltFuncFunctionData
*exsltFuncNewFunctionData(void);
60 #define MAX_FUNC_RECURSION 1000
62 /*static const xmlChar *exsltResultDataID = (const xmlChar *) "EXSLT Result";*/
65 * exsltFuncRegisterFunc:
66 * @func: the #exsltFuncFunctionData for the function
67 * @ctxt: an XSLT transformation context
68 * @URI: the function namespace URI
69 * @name: the function name
71 * Registers a function declared by a func:function element
74 exsltFuncRegisterFunc (exsltFuncFunctionData
*data
,
75 xsltTransformContextPtr ctxt
,
76 const xmlChar
*URI
, const xmlChar
*name
,
77 ATTRIBUTE_UNUSED
const xmlChar
*ignored
) {
78 if ((data
== NULL
) || (ctxt
== NULL
) || (URI
== NULL
) || (name
== NULL
))
81 xsltGenericDebug(xsltGenericDebugContext
,
82 "exsltFuncRegisterFunc: register {%s}%s\n",
84 xsltRegisterExtFunction(ctxt
, name
, URI
,
85 exsltFuncFunctionFunction
);
89 * exsltFuncRegisterImportFunc
90 * @data: the exsltFuncFunctionData for the function
91 * @ch: structure containing context and hash table
92 * @URI: the function namespace URI
93 * @name: the function name
95 * Checks if imported function is already registered in top-level
96 * stylesheet. If not, copies function data and registers function
99 exsltFuncRegisterImportFunc (exsltFuncFunctionData
*data
,
100 exsltFuncImportRegData
*ch
,
101 const xmlChar
*URI
, const xmlChar
*name
,
102 ATTRIBUTE_UNUSED
const xmlChar
*ignored
) {
103 exsltFuncFunctionData
*func
=NULL
;
105 if ((data
== NULL
) || (ch
== NULL
) || (URI
== NULL
) || (name
== NULL
))
108 if (ch
->ctxt
== NULL
|| ch
->hash
== NULL
)
111 /* Check if already present */
112 func
= (exsltFuncFunctionData
*)xmlHashLookup2(ch
->hash
, URI
, name
);
113 if (func
== NULL
) { /* Not yet present - copy it in */
114 func
= exsltFuncNewFunctionData();
115 memcpy(func
, data
, sizeof(exsltFuncFunctionData
));
116 if (xmlHashAddEntry2(ch
->hash
, URI
, name
, func
) < 0) {
117 xsltGenericError(xsltGenericErrorContext
,
118 "Failed to register function {%s}%s\n",
120 } else { /* Do the registration */
121 xsltGenericDebug(xsltGenericDebugContext
,
122 "exsltFuncRegisterImportFunc: register {%s}%s\n",
124 xsltRegisterExtFunction(ch
->ctxt
, name
, URI
,
125 exsltFuncFunctionFunction
);
132 * @ctxt: an XSLT transformation context
133 * @URI: the namespace URI for the extension
135 * Initializes the EXSLT - Functions module.
136 * Called at transformation-time; merges all
137 * functions declared in the import tree taking
138 * import precedence into account, i.e. overriding
139 * functions with lower import precedence.
141 * Returns the data for this transformation
143 static exsltFuncData
*
144 exsltFuncInit (xsltTransformContextPtr ctxt
, const xmlChar
*URI
) {
146 xsltStylesheetPtr tmp
;
147 exsltFuncImportRegData ch
;
148 xmlHashTablePtr hash
;
150 ret
= (exsltFuncData
*) xmlMalloc (sizeof(exsltFuncData
));
152 xsltGenericError(xsltGenericErrorContext
,
153 "exsltFuncInit: not enough memory\n");
156 memset(ret
, 0, sizeof(exsltFuncData
));
161 ch
.hash
= (xmlHashTablePtr
) xsltStyleGetExtData(ctxt
->style
, URI
);
162 ret
->funcs
= ch
.hash
;
163 xmlHashScanFull(ch
.hash
, (xmlHashScannerFull
) exsltFuncRegisterFunc
, ctxt
);
166 while ((tmp
=xsltNextImport(tmp
))!=NULL
) {
167 hash
= xsltGetExtInfo(tmp
, URI
);
169 xmlHashScanFull(hash
,
170 (xmlHashScannerFull
) exsltFuncRegisterImportFunc
, &ch
);
179 * @ctxt: an XSLT transformation context
180 * @URI: the namespace URI for the extension
181 * @data: the module data to free up
183 * Shutdown the EXSLT - Functions module
184 * Called at transformation-time.
187 exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED
,
188 const xmlChar
*URI ATTRIBUTE_UNUSED
,
189 exsltFuncData
*data
) {
190 if (data
->result
!= NULL
)
191 xmlXPathFreeObject(data
->result
);
196 * exsltFuncStyleInit:
197 * @style: an XSLT stylesheet
198 * @URI: the namespace URI for the extension
200 * Allocates the stylesheet data for EXSLT - Function
201 * Called at compile-time.
203 * Returns the allocated data
205 static xmlHashTablePtr
206 exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED
,
207 const xmlChar
*URI ATTRIBUTE_UNUSED
) {
208 return xmlHashCreate(1);
212 * exsltFuncStyleShutdown:
213 * @style: an XSLT stylesheet
214 * @URI: the namespace URI for the extension
215 * @data: the stylesheet data to free up
217 * Shutdown the EXSLT - Function module
218 * Called at compile-time.
221 exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED
,
222 const xmlChar
*URI ATTRIBUTE_UNUSED
,
223 xmlHashTablePtr data
) {
224 xmlHashFree(data
, (xmlHashDeallocator
) xmlFree
);
228 * exsltFuncNewFunctionData:
230 * Allocates an #exslFuncFunctionData object
232 * Returns the new structure
234 static exsltFuncFunctionData
*
235 exsltFuncNewFunctionData (void) {
236 exsltFuncFunctionData
*ret
;
238 ret
= (exsltFuncFunctionData
*) xmlMalloc (sizeof(exsltFuncFunctionData
));
240 xsltGenericError(xsltGenericErrorContext
,
241 "exsltFuncNewFunctionData: not enough memory\n");
244 memset(ret
, 0, sizeof(exsltFuncFunctionData
));
253 * exsltFreeFuncResultPreComp:
254 * @comp: the #exsltFuncResultPreComp to free up
256 * Deallocates an #exsltFuncResultPreComp
259 exsltFreeFuncResultPreComp (exsltFuncResultPreComp
*comp
) {
263 if (comp
->select
!= NULL
)
264 xmlXPathFreeCompExpr (comp
->select
);
265 if (comp
->nsList
!= NULL
)
266 xmlFree(comp
->nsList
);
271 * exsltFuncFunctionFunction:
272 * @ctxt: an XPath parser context
273 * @nargs: the number of arguments
275 * Evaluates the func:function element that defines the called function.
278 exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt
, int nargs
) {
279 xmlXPathObjectPtr oldResult
, ret
;
281 exsltFuncFunctionData
*func
;
282 xmlNodePtr paramNode
, oldInsert
, fake
;
284 xsltStackElemPtr params
= NULL
, param
;
285 xsltTransformContextPtr tctxt
= xsltXPathGetTransformContext(ctxt
);
288 struct objChain
*next
;
289 xmlXPathObjectPtr obj
;
291 struct objChain
*savedObjChain
= NULL
, *savedObj
;
294 * retrieve func:function template
296 data
= (exsltFuncData
*) xsltGetExtData (tctxt
,
297 EXSLT_FUNCTIONS_NAMESPACE
);
298 oldResult
= data
->result
;
301 func
= (exsltFuncFunctionData
*) xmlHashLookup2 (data
->funcs
,
302 ctxt
->context
->functionURI
,
303 ctxt
->context
->function
);
308 if (nargs
> func
->nargs
) {
309 xsltGenericError(xsltGenericErrorContext
,
310 "{%s}%s: called with too many arguments\n",
311 ctxt
->context
->functionURI
, ctxt
->context
->function
);
312 ctxt
->error
= XPATH_INVALID_ARITY
;
315 if (func
->content
!= NULL
) {
316 paramNode
= func
->content
->prev
;
320 if ((paramNode
== NULL
) && (func
->nargs
!= 0)) {
321 xsltGenericError(xsltGenericErrorContext
,
322 "exsltFuncFunctionFunction: nargs != 0 and "
326 if (tctxt
->funcLevel
> MAX_FUNC_RECURSION
) {
327 xsltGenericError(xsltGenericErrorContext
,
328 "{%s}%s: detected a recursion\n",
329 ctxt
->context
->functionURI
, ctxt
->context
->function
);
330 ctxt
->error
= XPATH_MEMORY_ERROR
;
336 * We have a problem with the evaluation of function parameters.
337 * The original library code did not evaluate XPath expressions until
338 * the last moment. After version 1.1.17 of the libxslt, the logic
339 * of other parts of the library was changed, and the evaluation of
340 * XPath expressions within parameters now takes place as soon as the
341 * parameter is parsed/evaluated (xsltParseStylesheetCallerParam).
342 * This means that the parameters need to be evaluated in lexical
343 * order (since a variable is "in scope" as soon as it is declared).
344 * However, on entry to this routine, the values (from the caller) are
345 * in reverse order (held on the XPath context variable stack). To
346 * accomplish what is required, I have added code to pop the XPath
347 * objects off of the stack at the beginning and save them, then use
348 * them (in the reverse order) as the params are evaluated. This
349 * requires an xmlMalloc/xmlFree for each param set by the caller,
350 * which is not very nice. There is probably a much better solution
351 * (like change other code to delay the evaluation).
354 * In order to give the function params and variables a new 'scope'
355 * we change varsBase in the context.
357 oldBase
= tctxt
->varsBase
;
358 tctxt
->varsBase
= tctxt
->varsNr
;
359 /* If there are any parameters */
360 if (paramNode
!= NULL
) {
361 /* Fetch the stored argument values from the caller */
362 for (i
= 0; i
< nargs
; i
++) {
363 savedObj
= xmlMalloc(sizeof(struct objChain
));
364 savedObj
->next
= savedObjChain
;
365 savedObj
->obj
= valuePop(ctxt
);
366 savedObjChain
= savedObj
;
370 * Prepare to process params in reverse order. First, go to
371 * the beginning of the param chain.
373 for (i
= 1; i
<= func
->nargs
; i
++) {
374 if (paramNode
->prev
== NULL
)
376 paramNode
= paramNode
->prev
;
379 * i has total # params found, nargs is number which are present
380 * as arguments from the caller
381 * Calculate the number of un-set parameters
383 notSet
= func
->nargs
- nargs
;
385 param
= xsltParseStylesheetCallerParam (tctxt
, paramNode
);
386 if (i
> notSet
) { /* if parameter value set */
388 if (param
->value
!= NULL
)
389 xmlXPathFreeObject(param
->value
);
390 savedObj
= savedObjChain
; /* get next val from chain */
391 param
->value
= savedObj
->obj
;
392 savedObjChain
= savedObjChain
->next
;
395 xsltLocalVariablePush(tctxt
, param
, -1);
396 param
->next
= params
;
398 paramNode
= paramNode
->next
;
404 fake
= xmlNewDocNode(tctxt
->output
, NULL
,
405 (const xmlChar
*)"fake", NULL
);
406 oldInsert
= tctxt
->insert
;
407 tctxt
->insert
= fake
;
408 xsltApplyOneTemplate (tctxt
, xmlXPathGetContextNode(ctxt
),
409 func
->content
, NULL
, NULL
);
410 xsltLocalVariablePop(tctxt
, tctxt
->varsBase
, -2);
411 tctxt
->insert
= oldInsert
;
412 tctxt
->varsBase
= oldBase
; /* restore original scope */
414 xsltFreeStackElemList(params
);
416 if (data
->error
!= 0)
419 if (data
->result
!= NULL
) {
422 ret
= xmlXPathNewCString("");
424 data
->result
= oldResult
;
427 * It is an error if the instantiation of the template results in
428 * the generation of result nodes.
430 if (fake
->children
!= NULL
) {
431 #ifdef LIBXML_DEBUG_ENABLED
432 xmlDebugDumpNode (stderr
, fake
, 1);
434 xsltGenericError(xsltGenericErrorContext
,
435 "{%s}%s: cannot write to result tree while "
436 "executing a function\n",
437 ctxt
->context
->functionURI
, ctxt
->context
->function
);
442 valuePush(ctxt
, ret
);
446 * IMPORTANT: This enables previously tree fragments marked as
447 * being results of a function, to be garbage-collected after
448 * the calling process exits.
450 xsltExtensionInstructionResultFinalize(tctxt
);
456 exsltFuncFunctionComp (xsltStylesheetPtr style
, xmlNodePtr inst
) {
457 xmlChar
*name
, *prefix
;
459 xmlHashTablePtr data
;
460 exsltFuncFunctionData
*func
;
462 if ((style
== NULL
) || (inst
== NULL
) || (inst
->type
!= XML_ELEMENT_NODE
))
468 qname
= xmlGetProp(inst
, (const xmlChar
*) "name");
469 name
= xmlSplitQName2 (qname
, &prefix
);
472 if ((name
== NULL
) || (prefix
== NULL
)) {
473 xsltGenericError(xsltGenericErrorContext
,
474 "func:function: not a QName\n");
479 /* namespace lookup */
480 ns
= xmlSearchNs (inst
->doc
, inst
, prefix
);
482 xsltGenericError(xsltGenericErrorContext
,
483 "func:function: undeclared prefix %s\n",
491 xsltParseTemplateContent(style
, inst
);
494 * Create function data
496 func
= exsltFuncNewFunctionData();
497 func
->content
= inst
->children
;
498 while (IS_XSLT_ELEM(func
->content
) &&
499 IS_XSLT_NAME(func
->content
, "param")) {
500 func
->content
= func
->content
->next
;
505 * Register the function data such that it can be retrieved
506 * by exslFuncFunctionFunction
508 #ifdef XSLT_REFACTORED
510 * Ensure that the hash table will be stored in the *current*
511 * stylesheet level in order to correctly evaluate the
514 data
= (xmlHashTablePtr
)
515 xsltStyleStylesheetLevelGetExtData(style
,
516 EXSLT_FUNCTIONS_NAMESPACE
);
518 data
= (xmlHashTablePtr
)
519 xsltStyleGetExtData (style
, EXSLT_FUNCTIONS_NAMESPACE
);
522 xsltGenericError(xsltGenericErrorContext
,
523 "exsltFuncFunctionComp: no stylesheet data\n");
528 if (xmlHashAddEntry2 (data
, ns
->href
, name
, func
) < 0) {
529 xsltTransformError(NULL
, style
, inst
,
530 "Failed to register function {%s}%s\n",
534 xsltGenericDebug(xsltGenericDebugContext
,
535 "exsltFuncFunctionComp: register {%s}%s\n",
541 static xsltElemPreCompPtr
542 exsltFuncResultComp (xsltStylesheetPtr style
, xmlNodePtr inst
,
543 xsltTransformFunction function
) {
546 exsltFuncResultPreComp
*ret
;
548 if ((style
== NULL
) || (inst
== NULL
) || (inst
->type
!= XML_ELEMENT_NODE
))
552 * "Validity" checking
554 /* it is an error to have any following sibling elements aside
555 * from the xsl:fallback element.
557 for (test
= inst
->next
; test
!= NULL
; test
= test
->next
) {
558 if (test
->type
!= XML_ELEMENT_NODE
)
560 if (IS_XSLT_ELEM(test
) && IS_XSLT_NAME(test
, "fallback"))
562 xsltGenericError(xsltGenericErrorContext
,
563 "exsltFuncResultElem: only xsl:fallback is "
564 "allowed to follow func:result\n");
568 /* it is an error for a func:result element to not be a descendant
570 * it is an error if a func:result occurs within a func:result
572 * it is an error if instanciating the content of a variable
573 * binding element (i.e. xsl:variable, xsl:param) results in the
574 * instanciation of a func:result element.
576 for (test
= inst
->parent
; test
!= NULL
; test
= test
->parent
) {
577 if (IS_XSLT_ELEM(test
) &&
578 IS_XSLT_NAME(test
, "stylesheet")) {
579 xsltGenericError(xsltGenericErrorContext
,
580 "func:result element not a descendant "
581 "of a func:function\n");
585 if ((test
->ns
!= NULL
) &&
586 (xmlStrEqual(test
->ns
->href
, EXSLT_FUNCTIONS_NAMESPACE
))) {
587 if (xmlStrEqual(test
->name
, (const xmlChar
*) "function")) {
590 if (xmlStrEqual(test
->name
, (const xmlChar
*) "result")) {
591 xsltGenericError(xsltGenericErrorContext
,
592 "func:result element not allowed within"
593 " another func:result element\n");
598 if (IS_XSLT_ELEM(test
) &&
599 (IS_XSLT_NAME(test
, "variable") ||
600 IS_XSLT_NAME(test
, "param"))) {
601 xsltGenericError(xsltGenericErrorContext
,
602 "func:result element not allowed within"
603 " a variable binding element\n");
612 ret
= (exsltFuncResultPreComp
*)
613 xmlMalloc (sizeof(exsltFuncResultPreComp
));
615 xsltPrintErrorContext(NULL
, NULL
, NULL
);
616 xsltGenericError(xsltGenericErrorContext
,
617 "exsltFuncResultComp : malloc failed\n");
621 memset(ret
, 0, sizeof(exsltFuncResultPreComp
));
623 xsltInitElemPreComp ((xsltElemPreCompPtr
) ret
, style
, inst
, function
,
624 (xsltElemPreCompDeallocator
) exsltFreeFuncResultPreComp
);
628 * Precompute the select attribute
630 sel
= xmlGetNsProp(inst
, (const xmlChar
*) "select", NULL
);
632 ret
->select
= xmlXPathCompile (sel
);
636 * Precompute the namespace list
638 ret
->nsList
= xmlGetNsList(inst
->doc
, inst
);
639 if (ret
->nsList
!= NULL
) {
641 while (ret
->nsList
[i
] != NULL
)
645 return ((xsltElemPreCompPtr
) ret
);
649 exsltFuncResultElem (xsltTransformContextPtr ctxt
,
650 xmlNodePtr node ATTRIBUTE_UNUSED
, xmlNodePtr inst
,
651 exsltFuncResultPreComp
*comp
) {
653 xmlXPathObjectPtr ret
;
656 /* It is an error if instantiating the content of the
657 * func:function element results in the instantiation of more than
658 * one func:result elements.
660 data
= (exsltFuncData
*) xsltGetExtData (ctxt
, EXSLT_FUNCTIONS_NAMESPACE
);
662 xsltGenericError(xsltGenericErrorContext
,
663 "exsltFuncReturnElem: data == NULL\n");
666 if (data
->result
!= NULL
) {
667 xsltGenericError(xsltGenericErrorContext
,
668 "func:result already instanciated\n");
675 if (comp
->select
!= NULL
) {
676 xmlNsPtr
*oldXPNsList
;
678 xmlNodePtr oldXPContextNode
;
679 /* If the func:result element has a select attribute, then the
680 * value of the attribute must be an expression and the
681 * returned value is the object that results from evaluating
682 * the expression. In this case, the content must be empty.
684 if (inst
->children
!= NULL
) {
685 xsltGenericError(xsltGenericErrorContext
,
686 "func:result content must be empty if"
687 " the function has a select attribute\n");
691 oldXPNsList
= ctxt
->xpathCtxt
->namespaces
;
692 oldXPNsNr
= ctxt
->xpathCtxt
->nsNr
;
693 oldXPContextNode
= ctxt
->xpathCtxt
->node
;
695 ctxt
->xpathCtxt
->namespaces
= comp
->nsList
;
696 ctxt
->xpathCtxt
->nsNr
= comp
->nsNr
;
698 ret
= xmlXPathCompiledEval(comp
->select
, ctxt
->xpathCtxt
);
700 ctxt
->xpathCtxt
->node
= oldXPContextNode
;
701 ctxt
->xpathCtxt
->nsNr
= oldXPNsNr
;
702 ctxt
->xpathCtxt
->namespaces
= oldXPNsList
;
705 xsltGenericError(xsltGenericErrorContext
,
706 "exsltFuncResultElem: ret == NULL\n");
710 * Mark it as a function result in order to avoid garbage
711 * collecting of tree fragments before the function exits.
713 xsltExtensionInstructionResultRegister(ctxt
, ret
);
714 } else if (inst
->children
!= NULL
) {
715 /* If the func:result element does not have a select attribute
716 * and has non-empty content (i.e. the func:result element has
717 * one or more child nodes), then the content of the
718 * func:result element specifies the value.
720 xmlNodePtr oldInsert
;
723 container
= xsltCreateRVT(ctxt
);
724 if (container
== NULL
) {
725 xsltGenericError(xsltGenericErrorContext
,
726 "exsltFuncResultElem: out of memory\n");
730 xsltRegisterLocalRVT(ctxt
, container
);
732 oldInsert
= ctxt
->insert
;
733 ctxt
->insert
= (xmlNodePtr
) container
;
734 xsltApplyOneTemplate (ctxt
, ctxt
->xpathCtxt
->node
,
735 inst
->children
, NULL
, NULL
);
736 ctxt
->insert
= oldInsert
;
738 ret
= xmlXPathNewValueTree((xmlNodePtr
) container
);
740 xsltGenericError(xsltGenericErrorContext
,
741 "exsltFuncResultElem: ret == NULL\n");
744 ret
->boolval
= 0; /* Freeing is not handled there anymore */
746 * Mark it as a function result in order to avoid garbage
747 * collecting of tree fragments before the function exits.
749 xsltExtensionInstructionResultRegister(ctxt
, ret
);
752 /* If the func:result element has empty content and does not
753 * have a select attribute, then the returned value is an
756 ret
= xmlXPathNewCString("");
764 * Registers the EXSLT - Functions module
767 exsltFuncRegister (void) {
768 xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE
,
769 (xsltExtInitFunction
) exsltFuncInit
,
770 (xsltExtShutdownFunction
) exsltFuncShutdown
,
771 (xsltStyleExtInitFunction
) exsltFuncStyleInit
,
772 (xsltStyleExtShutdownFunction
) exsltFuncStyleShutdown
);
774 xsltRegisterExtModuleTopLevel ((const xmlChar
*) "function",
775 EXSLT_FUNCTIONS_NAMESPACE
,
776 exsltFuncFunctionComp
);
777 xsltRegisterExtModuleElement ((const xmlChar
*) "result",
778 EXSLT_FUNCTIONS_NAMESPACE
,
779 (xsltPreComputeFunction
)exsltFuncResultComp
,
780 (xsltTransformFunction
) exsltFuncResultElem
);