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
))
469 qname
= xmlGetProp(inst
, (const xmlChar
*) "name");
470 name
= xmlSplitQName2 (qname
, &prefix
);
473 if ((name
== NULL
) || (prefix
== NULL
)) {
474 xsltGenericError(xsltGenericErrorContext
,
475 "func:function: not a QName\n");
480 /* namespace lookup */
481 ns
= xmlSearchNs (inst
->doc
, inst
, prefix
);
483 xsltGenericError(xsltGenericErrorContext
,
484 "func:function: undeclared prefix %s\n",
493 * Create function data
495 func
= exsltFuncNewFunctionData();
496 func
->content
= inst
->children
;
497 while (IS_XSLT_ELEM(func
->content
) &&
498 IS_XSLT_NAME(func
->content
, "param")) {
499 func
->content
= func
->content
->next
;
503 xsltParseTemplateContent(style
, inst
);
506 * Register the function data such that it can be retrieved
507 * by exslFuncFunctionFunction
509 #ifdef XSLT_REFACTORED
511 * Ensure that the hash table will be stored in the *current*
512 * stylesheet level in order to correctly evaluate the
515 data
= (xmlHashTablePtr
)
516 xsltStyleStylesheetLevelGetExtData(style
,
517 EXSLT_FUNCTIONS_NAMESPACE
);
519 data
= (xmlHashTablePtr
)
520 xsltStyleGetExtData (style
, EXSLT_FUNCTIONS_NAMESPACE
);
523 xsltGenericError(xsltGenericErrorContext
,
524 "exsltFuncFunctionComp: no stylesheet data\n");
529 if (xmlHashAddEntry2 (data
, ns
->href
, name
, func
) < 0) {
530 xsltTransformError(NULL
, style
, inst
,
531 "Failed to register function {%s}%s\n",
535 xsltGenericDebug(xsltGenericDebugContext
,
536 "exsltFuncFunctionComp: register {%s}%s\n",
542 static xsltElemPreCompPtr
543 exsltFuncResultComp (xsltStylesheetPtr style
, xmlNodePtr inst
,
544 xsltTransformFunction function
) {
547 exsltFuncResultPreComp
*ret
;
550 * "Validity" checking
552 /* it is an error to have any following sibling elements aside
553 * from the xsl:fallback element.
555 for (test
= inst
->next
; test
!= NULL
; test
= test
->next
) {
556 if (test
->type
!= XML_ELEMENT_NODE
)
558 if (IS_XSLT_ELEM(test
) && IS_XSLT_NAME(test
, "fallback"))
560 xsltGenericError(xsltGenericErrorContext
,
561 "exsltFuncResultElem: only xsl:fallback is "
562 "allowed to follow func:result\n");
565 /* it is an error for a func:result element to not be a descendant
567 * it is an error if a func:result occurs within a func:result
569 * it is an error if instanciating the content of a variable
570 * binding element (i.e. xsl:variable, xsl:param) results in the
571 * instanciation of a func:result element.
573 for (test
= inst
->parent
; test
!= NULL
; test
= test
->parent
) {
574 if (IS_XSLT_ELEM(test
) &&
575 IS_XSLT_NAME(test
, "stylesheet")) {
576 xsltGenericError(xsltGenericErrorContext
,
577 "func:result element not a descendant "
578 "of a func:function\n");
581 if ((test
->ns
!= NULL
) &&
582 (xmlStrEqual(test
->ns
->href
, EXSLT_FUNCTIONS_NAMESPACE
))) {
583 if (xmlStrEqual(test
->name
, (const xmlChar
*) "function")) {
586 if (xmlStrEqual(test
->name
, (const xmlChar
*) "result")) {
587 xsltGenericError(xsltGenericErrorContext
,
588 "func:result element not allowed within"
589 " another func:result element\n");
593 if (IS_XSLT_ELEM(test
) &&
594 (IS_XSLT_NAME(test
, "variable") ||
595 IS_XSLT_NAME(test
, "param"))) {
596 xsltGenericError(xsltGenericErrorContext
,
597 "func:result element not allowed within"
598 " a variable binding element\n");
606 ret
= (exsltFuncResultPreComp
*)
607 xmlMalloc (sizeof(exsltFuncResultPreComp
));
609 xsltPrintErrorContext(NULL
, NULL
, NULL
);
610 xsltGenericError(xsltGenericErrorContext
,
611 "exsltFuncResultComp : malloc failed\n");
614 memset(ret
, 0, sizeof(exsltFuncResultPreComp
));
616 xsltInitElemPreComp ((xsltElemPreCompPtr
) ret
, style
, inst
, function
,
617 (xsltElemPreCompDeallocator
) exsltFreeFuncResultPreComp
);
621 * Precompute the select attribute
623 sel
= xmlGetNsProp(inst
, (const xmlChar
*) "select", NULL
);
625 ret
->select
= xmlXPathCompile (sel
);
629 * Precompute the namespace list
631 ret
->nsList
= xmlGetNsList(inst
->doc
, inst
);
632 if (ret
->nsList
!= NULL
) {
634 while (ret
->nsList
[i
] != NULL
)
638 return ((xsltElemPreCompPtr
) ret
);
642 exsltFuncResultElem (xsltTransformContextPtr ctxt
,
643 xmlNodePtr node ATTRIBUTE_UNUSED
, xmlNodePtr inst
,
644 exsltFuncResultPreComp
*comp
) {
646 xmlXPathObjectPtr ret
;
649 /* It is an error if instantiating the content of the
650 * func:function element results in the instantiation of more than
651 * one func:result elements.
653 data
= (exsltFuncData
*) xsltGetExtData (ctxt
, EXSLT_FUNCTIONS_NAMESPACE
);
655 xsltGenericError(xsltGenericErrorContext
,
656 "exsltFuncReturnElem: data == NULL\n");
659 if (data
->result
!= NULL
) {
660 xsltGenericError(xsltGenericErrorContext
,
661 "func:result already instanciated\n");
668 if (comp
->select
!= NULL
) {
669 xmlNsPtr
*oldXPNsList
;
671 xmlNodePtr oldXPContextNode
;
672 /* If the func:result element has a select attribute, then the
673 * value of the attribute must be an expression and the
674 * returned value is the object that results from evaluating
675 * the expression. In this case, the content must be empty.
677 if (inst
->children
!= NULL
) {
678 xsltGenericError(xsltGenericErrorContext
,
679 "func:result content must be empty if"
680 " the function has a select attribute\n");
684 oldXPNsList
= ctxt
->xpathCtxt
->namespaces
;
685 oldXPNsNr
= ctxt
->xpathCtxt
->nsNr
;
686 oldXPContextNode
= ctxt
->xpathCtxt
->node
;
688 ctxt
->xpathCtxt
->namespaces
= comp
->nsList
;
689 ctxt
->xpathCtxt
->nsNr
= comp
->nsNr
;
691 ret
= xmlXPathCompiledEval(comp
->select
, ctxt
->xpathCtxt
);
693 ctxt
->xpathCtxt
->node
= oldXPContextNode
;
694 ctxt
->xpathCtxt
->nsNr
= oldXPNsNr
;
695 ctxt
->xpathCtxt
->namespaces
= oldXPNsList
;
698 xsltGenericError(xsltGenericErrorContext
,
699 "exsltFuncResultElem: ret == NULL\n");
703 * Mark it as a function result in order to avoid garbage
704 * collecting of tree fragments before the function exits.
706 xsltExtensionInstructionResultRegister(ctxt
, ret
);
707 } else if (inst
->children
!= NULL
) {
708 /* If the func:result element does not have a select attribute
709 * and has non-empty content (i.e. the func:result element has
710 * one or more child nodes), then the content of the
711 * func:result element specifies the value.
713 xmlNodePtr oldInsert
;
716 container
= xsltCreateRVT(ctxt
);
717 if (container
== NULL
) {
718 xsltGenericError(xsltGenericErrorContext
,
719 "exsltFuncResultElem: out of memory\n");
723 xsltRegisterLocalRVT(ctxt
, container
);
725 oldInsert
= ctxt
->insert
;
726 ctxt
->insert
= (xmlNodePtr
) container
;
727 xsltApplyOneTemplate (ctxt
, ctxt
->xpathCtxt
->node
,
728 inst
->children
, NULL
, NULL
);
729 ctxt
->insert
= oldInsert
;
731 ret
= xmlXPathNewValueTree((xmlNodePtr
) container
);
733 xsltGenericError(xsltGenericErrorContext
,
734 "exsltFuncResultElem: ret == NULL\n");
737 ret
->boolval
= 0; /* Freeing is not handled there anymore */
739 * Mark it as a function result in order to avoid garbage
740 * collecting of tree fragments before the function exits.
742 xsltExtensionInstructionResultRegister(ctxt
, ret
);
745 /* If the func:result element has empty content and does not
746 * have a select attribute, then the returned value is an
749 ret
= xmlXPathNewCString("");
757 * Registers the EXSLT - Functions module
760 exsltFuncRegister (void) {
761 xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE
,
762 (xsltExtInitFunction
) exsltFuncInit
,
763 (xsltExtShutdownFunction
) exsltFuncShutdown
,
764 (xsltStyleExtInitFunction
) exsltFuncStyleInit
,
765 (xsltStyleExtShutdownFunction
) exsltFuncStyleShutdown
);
767 xsltRegisterExtModuleTopLevel ((const xmlChar
*) "function",
768 EXSLT_FUNCTIONS_NAMESPACE
,
769 exsltFuncFunctionComp
);
770 xsltRegisterExtModuleElement ((const xmlChar
*) "result",
771 EXSLT_FUNCTIONS_NAMESPACE
,
772 (xsltPreComputeFunction
)exsltFuncResultComp
,
773 (xsltTransformFunction
) exsltFuncResultElem
);