1#define IN_LIBEXSLT
2#include "libexslt/libexslt.h"
3
4#if defined(WIN32) && !defined (__CYGWIN__) && (!__MINGW32__)
5#include <win32config.h>
6#else
7#include "config.h"
8#endif
9
10#include <string.h>
11
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>
17
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>
24
25#include "exslt.h"
26
27typedef struct _exsltFuncFunctionData exsltFuncFunctionData;
28struct _exsltFuncFunctionData {
29    int nargs;			/* number of arguments to the function */
30    xmlNodePtr content;		/* the func:fuction template content */
31};
32
33typedef struct _exsltFuncData exsltFuncData;
34struct _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};
39
40typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp;
41struct _exsltFuncResultPreComp {
42    xsltElemPreComp comp;
43    xmlXPathCompExprPtr select;
44    xmlNsPtr *nsList;
45    int nsNr;
46};
47
48/* Used for callback function in exsltInitFunc */
49typedef struct _exsltFuncImportRegData exsltFuncImportRegData;
50struct _exsltFuncImportRegData {
51    xsltTransformContextPtr ctxt;
52    xmlHashTablePtr hash;
53};
54
55static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt,
56				       int nargs);
57static exsltFuncFunctionData *exsltFuncNewFunctionData(void);
58
59/**
60 * exsltFuncRegisterFunc:
61 * @func:  the #exsltFuncFunctionData for the function
62 * @ctxt:  an XSLT transformation context
63 * @URI:  the function namespace URI
64 * @name: the function name
65 *
66 * Registers a function declared by a func:function element
67 */
68static void
69exsltFuncRegisterFunc (exsltFuncFunctionData *data,
70		       xsltTransformContextPtr ctxt,
71		       const xmlChar *URI, const xmlChar *name,
72		       ATTRIBUTE_UNUSED const xmlChar *ignored) {
73    if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL))
74	return;
75
76    xsltGenericDebug(xsltGenericDebugContext,
77		     "exsltFuncRegisterFunc: register {%s}%s\n",
78		     URI, name);
79    xsltRegisterExtFunction(ctxt, name, URI,
80			    exsltFuncFunctionFunction);
81}
82
83/*
84 * exsltFuncRegisterImportFunc
85 * @data:    the exsltFuncFunctionData for the function
86 * @ch:	     structure containing context and hash table
87 * @URI:     the function namespace URI
88 * @name:    the function name
89 *
90 * Checks if imported function is already registered in top-level
91 * stylesheet.  If not, copies function data and registers function
92 */
93static void
94exsltFuncRegisterImportFunc (exsltFuncFunctionData *data,
95			     exsltFuncImportRegData *ch,
96			     const xmlChar *URI, const xmlChar *name,
97			     ATTRIBUTE_UNUSED const xmlChar *ignored) {
98    exsltFuncFunctionData *func=NULL;
99
100    if ((data == NULL) || (ch == NULL) || (URI == NULL) || (name == NULL))
101            return;
102
103    if (ch->ctxt == NULL || ch->hash == NULL)
104    	return;
105
106    /* Check if already present */
107    func = (exsltFuncFunctionData*)xmlHashLookup2(ch->hash, URI, name);
108    if (func == NULL) {		/* Not yet present - copy it in */
109    	func = exsltFuncNewFunctionData();
110	memcpy(func, data, sizeof(exsltFuncFunctionData));
111	if (xmlHashAddEntry2(ch->hash, URI, name, func) < 0) {
112	    xsltGenericError(xsltGenericErrorContext,
113	    	    "Failed to register function {%s}%s\n",
114		    URI, name);
115	} else {		/* Do the registration */
116	    xsltGenericDebug(xsltGenericDebugContext,
117	            "exsltFuncRegisterImportFunc: register {%s}%s\n",
118		    URI, name);
119	    xsltRegisterExtFunction(ch->ctxt, name, URI,
120		    exsltFuncFunctionFunction);
121	}
122    }
123}
124
125/**
126 * exsltFuncInit:
127 * @ctxt: an XSLT transformation context
128 * @URI: the namespace URI for the extension
129 *
130 * Initializes the EXSLT - Functions module.
131 * Called at transformation-time; merges all
132 * functions declared in the import tree taking
133 * import precedence into account, i.e. overriding
134 * functions with lower import precedence.
135 *
136 * Returns the data for this transformation
137 */
138static exsltFuncData *
139exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) {
140    exsltFuncData *ret;
141    xsltStylesheetPtr tmp;
142    exsltFuncImportRegData ch;
143    xmlHashTablePtr hash;
144
145    ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData));
146    if (ret == NULL) {
147	xsltGenericError(xsltGenericErrorContext,
148			 "exsltFuncInit: not enough memory\n");
149	return(NULL);
150    }
151    memset(ret, 0, sizeof(exsltFuncData));
152
153    ret->result = NULL;
154    ret->error = 0;
155
156    ch.hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI);
157    ret->funcs = ch.hash;
158    xmlHashScanFull(ch.hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt);
159    tmp = ctxt->style;
160    ch.ctxt = ctxt;
161    while ((tmp=xsltNextImport(tmp))!=NULL) {
162	hash = xsltGetExtInfo(tmp, URI);
163	if (hash != NULL) {
164	    xmlHashScanFull(hash,
165		    (xmlHashScannerFull) exsltFuncRegisterImportFunc, &ch);
166	}
167    }
168
169    return(ret);
170}
171
172/**
173 * exsltFuncShutdown:
174 * @ctxt: an XSLT transformation context
175 * @URI: the namespace URI for the extension
176 * @data: the module data to free up
177 *
178 * Shutdown the EXSLT - Functions module
179 * Called at transformation-time.
180 */
181static void
182exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
183		   const xmlChar *URI ATTRIBUTE_UNUSED,
184		   exsltFuncData *data) {
185    if (data->result != NULL)
186	xmlXPathFreeObject(data->result);
187    xmlFree(data);
188}
189
190/**
191 * exsltFuncStyleInit:
192 * @style: an XSLT stylesheet
193 * @URI: the namespace URI for the extension
194 *
195 * Allocates the stylesheet data for EXSLT - Function
196 * Called at compile-time.
197 *
198 * Returns the allocated data
199 */
200static xmlHashTablePtr
201exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED,
202		    const xmlChar *URI ATTRIBUTE_UNUSED) {
203    return xmlHashCreate(1);
204}
205
206/**
207 * exsltFuncStyleShutdown:
208 * @style: an XSLT stylesheet
209 * @URI: the namespace URI for the extension
210 * @data: the stylesheet data to free up
211 *
212 * Shutdown the EXSLT - Function module
213 * Called at compile-time.
214 */
215static void
216exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED,
217			const xmlChar *URI ATTRIBUTE_UNUSED,
218			xmlHashTablePtr data) {
219    xmlHashFree(data, (xmlHashDeallocator) xmlFree);
220}
221
222/**
223 * exsltFuncNewFunctionData:
224 *
225 * Allocates an #exslFuncFunctionData object
226 *
227 * Returns the new structure
228 */
229static exsltFuncFunctionData *
230exsltFuncNewFunctionData (void) {
231    exsltFuncFunctionData *ret;
232
233    ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData));
234    if (ret == NULL) {
235	xsltGenericError(xsltGenericErrorContext,
236			 "exsltFuncNewFunctionData: not enough memory\n");
237	return (NULL);
238    }
239    memset(ret, 0, sizeof(exsltFuncFunctionData));
240
241    ret->nargs = 0;
242    ret->content = NULL;
243
244    return(ret);
245}
246
247/**
248 * exsltFreeFuncResultPreComp:
249 * @comp:  the #exsltFuncResultPreComp to free up
250 *
251 * Deallocates an #exsltFuncResultPreComp
252 */
253static void
254exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) {
255    if (comp == NULL)
256	return;
257
258    if (comp->select != NULL)
259	xmlXPathFreeCompExpr (comp->select);
260    if (comp->nsList != NULL)
261        xmlFree(comp->nsList);
262    xmlFree(comp);
263}
264
265/**
266 * exsltFuncFunctionFunction:
267 * @ctxt:  an XPath parser context
268 * @nargs:  the number of arguments
269 *
270 * Evaluates the func:function element that defines the called function.
271 */
272static void
273exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
274    xmlXPathObjectPtr obj, oldResult, ret;
275    exsltFuncData *data;
276    exsltFuncFunctionData *func;
277    xmlNodePtr paramNode, oldInsert, fake, content = NULL;
278    int oldBase;
279    xsltStackElemPtr params = NULL, param;
280    xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
281    int i;
282
283    /*
284     * retrieve func:function template
285     */
286    data = (exsltFuncData *) xsltGetExtData (tctxt,
287					     EXSLT_FUNCTIONS_NAMESPACE);
288    oldResult = data->result;
289    data->result = NULL;
290
291    func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs,
292						    ctxt->context->functionURI,
293						    ctxt->context->function);
294
295    /*
296     * params handling
297     */
298    if (nargs > func->nargs) {
299	xsltGenericError(xsltGenericErrorContext,
300			 "{%s}%s: called with too many arguments\n",
301			 ctxt->context->functionURI, ctxt->context->function);
302	ctxt->error = XPATH_INVALID_ARITY;
303	return;
304    }
305    if (func->content != NULL) {
306	paramNode = func->content->prev;
307	content = func->content;
308    }
309    else
310	paramNode = NULL;
311    if ((paramNode == NULL) && (func->nargs != 0)) {
312	xsltGenericError(xsltGenericErrorContext,
313			 "exsltFuncFunctionFunction: nargs != 0 and "
314			 "param == NULL\n");
315	return;
316    }
317
318    /* set params */
319    for (i = func->nargs; (i > nargs) && (paramNode != NULL); i--) {
320	paramNode = paramNode->prev;
321	if (content != NULL)
322	    content = content->prev;
323    }
324    while ((i-- > 0) && (paramNode != NULL)) {
325	obj = valuePop(ctxt);
326	/* FIXME: this is a bit hackish */
327	param = xsltParseStylesheetCallerParam (tctxt, paramNode);
328	param->computed = 1;
329	if (param->value != NULL)
330	    xmlXPathFreeObject(param->value);
331	param->value = obj;
332	param->next = params;
333	params = param;
334	paramNode = paramNode->prev;
335    }
336
337    /*
338     * actual processing
339     */
340    fake = xmlNewDocNode(tctxt->output, NULL,
341			 (const xmlChar *)"fake", NULL);
342    oldInsert = tctxt->insert;
343    tctxt->insert = fake;
344    /*
345     * In order to give the function variables a new 'scope' we
346     * change varsBase in the context.
347     */
348    oldBase = tctxt->varsBase;
349    tctxt->varsBase = tctxt->varsNr;
350    xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt),
351			  content, NULL, params);
352    tctxt->insert = oldInsert;
353    tctxt->varsBase = oldBase;	/* restore original scope */
354    if (params != NULL)
355	xsltFreeStackElemList(params);
356
357    if (data->error != 0)
358	return;
359
360    if (data->result != NULL)
361	ret = data->result;
362    else
363	ret = xmlXPathNewCString("");
364
365    data->result = oldResult;
366
367    /*
368     * It is an error if the instantiation of the template results in
369     * the generation of result nodes.
370     */
371    if (fake->children != NULL) {
372#ifdef LIBXML_DEBUG_ENABLED
373	xmlDebugDumpNode (stderr, fake, 1);
374#endif
375	xsltGenericError(xsltGenericErrorContext,
376			 "{%s}%s: cannot write to result tree while "
377			 "executing a function\n",
378			 ctxt->context->functionURI, ctxt->context->function);
379	xmlFreeNode(fake);
380	return;
381    }
382    xmlFreeNode(fake);
383    valuePush(ctxt, ret);
384}
385
386
387static void
388exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) {
389    xmlChar *name, *prefix;
390    xmlNsPtr ns;
391    xmlHashTablePtr data;
392    exsltFuncFunctionData *func;
393
394    if ((style == NULL) || (inst == NULL))
395	return;
396
397
398    {
399	xmlChar *qname;
400
401	qname = xmlGetProp(inst, (const xmlChar *) "name");
402	name = xmlSplitQName2 (qname, &prefix);
403	xmlFree(qname);
404    }
405    if ((name == NULL) || (prefix == NULL)) {
406	xsltGenericError(xsltGenericErrorContext,
407			 "func:function: not a QName\n");
408	if (name != NULL)
409	    xmlFree(name);
410	return;
411    }
412    /* namespace lookup */
413    ns = xmlSearchNs (inst->doc, inst, prefix);
414    if (ns == NULL) {
415	xsltGenericError(xsltGenericErrorContext,
416			 "func:function: undeclared prefix %s\n",
417			 prefix);
418	xmlFree(name);
419	xmlFree(prefix);
420	return;
421    }
422    xmlFree(prefix);
423
424    /*
425     * Create function data
426     */
427    func = exsltFuncNewFunctionData();
428    func->content = inst->children;
429    while (IS_XSLT_ELEM(func->content) &&
430	   IS_XSLT_NAME(func->content, "param")) {
431	func->content = func->content->next;
432	func->nargs++;
433    }
434
435    xsltParseTemplateContent(style, inst);
436
437    /*
438     * Register the function data such that it can be retrieved
439     * by exslFuncFunctionFunction
440     */
441#ifdef XSLT_REFACTORED
442    /*
443    * Ensure that the hash table will be stored in the *current*
444    * stylesheet level in order to correctly evaluate the
445    * import precedence.
446    */
447    data = (xmlHashTablePtr)
448	xsltStyleStylesheetLevelGetExtData(style,
449	    EXSLT_FUNCTIONS_NAMESPACE);
450#else
451    data = (xmlHashTablePtr)
452	xsltStyleGetExtData (style, EXSLT_FUNCTIONS_NAMESPACE);
453#endif
454    if (data == NULL) {
455	xsltGenericError(xsltGenericErrorContext,
456			 "exsltFuncFunctionComp: no stylesheet data\n");
457	xmlFree(name);
458	return;
459    }
460
461    if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) {
462	xsltTransformError(NULL, style, inst,
463	    "Failed to register function {%s}%s\n",
464			 ns->href, name);
465	style->errors++;
466    } else {
467	xsltGenericDebug(xsltGenericDebugContext,
468			 "exsltFuncFunctionComp: register {%s}%s\n",
469			 ns->href, name);
470    }
471    xmlFree(name);
472}
473
474static xsltElemPreCompPtr
475exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst,
476		     xsltTransformFunction function) {
477    xmlNodePtr test;
478    xmlChar *sel;
479    exsltFuncResultPreComp *ret;
480
481    /*
482     * "Validity" checking
483     */
484    /* it is an error to have any following sibling elements aside
485     * from the xsl:fallback element.
486     */
487    for (test = inst->next; test != NULL; test = test->next) {
488	if (test->type != XML_ELEMENT_NODE)
489	    continue;
490	if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback"))
491	    continue;
492	xsltGenericError(xsltGenericErrorContext,
493			 "exsltFuncResultElem: only xsl:fallback is "
494			 "allowed to follow func:result\n");
495	return (NULL);
496    }
497    /* it is an error for a func:result element to not be a descendant
498     * of func:function.
499     * it is an error if a func:result occurs within a func:result
500     * element.
501     * it is an error if instanciating the content of a variable
502     * binding element (i.e. xsl:variable, xsl:param) results in the
503     * instanciation of a func:result element.
504     */
505    for (test = inst->parent; test != NULL; test = test->parent) {
506	if ((test->ns != NULL) &&
507	    (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) {
508	    if (xmlStrEqual(test->name, (const xmlChar *) "function")) {
509		break;
510	    }
511	    if (xmlStrEqual(test->name, (const xmlChar *) "result")) {
512		xsltGenericError(xsltGenericErrorContext,
513				 "func:result element not allowed within"
514				 " another func:result element\n");
515		return (NULL);
516	    }
517	}
518	if (IS_XSLT_ELEM(test) &&
519	    (IS_XSLT_NAME(test, "variable") ||
520	     IS_XSLT_NAME(test, "param"))) {
521	    xsltGenericError(xsltGenericErrorContext,
522			     "func:result element not allowed within"
523			     " a variable binding element\n");
524	    return (NULL);
525	}
526    }
527
528    /*
529     * Precomputation
530     */
531    ret = (exsltFuncResultPreComp *)
532	xmlMalloc (sizeof(exsltFuncResultPreComp));
533    if (ret == NULL) {
534	xsltPrintErrorContext(NULL, NULL, NULL);
535        xsltGenericError(xsltGenericErrorContext,
536                         "exsltFuncResultComp : malloc failed\n");
537        return (NULL);
538    }
539    memset(ret, 0, sizeof(exsltFuncResultPreComp));
540
541    xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function,
542		 (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp);
543    ret->select = NULL;
544
545    /*
546     * Precompute the select attribute
547     */
548    sel = xmlGetNsProp(inst, (const xmlChar *) "select", NULL);
549    if (sel != NULL) {
550	ret->select = xmlXPathCompile (sel);
551	xmlFree(sel);
552    }
553    /*
554     * Precompute the namespace list
555     */
556    ret->nsList = xmlGetNsList(inst->doc, inst);
557    if (ret->nsList != NULL) {
558        int i = 0;
559        while (ret->nsList[i] != NULL)
560	    i++;
561	ret->nsNr = i;
562    }
563    return ((xsltElemPreCompPtr) ret);
564}
565
566static void
567exsltFuncResultElem (xsltTransformContextPtr ctxt,
568	             xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
569		     exsltFuncResultPreComp *comp) {
570    exsltFuncData *data;
571    xmlXPathObjectPtr ret;
572    xmlNsPtr *oldNsList;
573    int oldNsNr;
574
575    /* It is an error if instantiating the content of the
576     * func:function element results in the instantiation of more than
577     * one func:result elements.
578     */
579    data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE);
580    if (data == NULL) {
581	xsltGenericError(xsltGenericErrorContext,
582			 "exsltFuncReturnElem: data == NULL\n");
583	return;
584    }
585    if (data->result != NULL) {
586	xsltGenericError(xsltGenericErrorContext,
587			 "func:result already instanciated\n");
588	data->error = 1;
589	return;
590    }
591    /*
592     * Processing
593     */
594    if (comp->select != NULL) {
595	/* If the func:result element has a select attribute, then the
596	 * value of the attribute must be an expression and the
597	 * returned value is the object that results from evaluating
598	 * the expression. In this case, the content must be empty.
599	 */
600	if (inst->children != NULL) {
601	    xsltGenericError(xsltGenericErrorContext,
602			     "func:result content must be empty if it"
603			     " has a select attribute\n");
604	    data->error = 1;
605	    return;
606	}
607	oldNsList = ctxt->xpathCtxt->namespaces;
608	oldNsNr = ctxt->xpathCtxt->nsNr;
609	ctxt->xpathCtxt->namespaces = comp->nsList;
610	ctxt->xpathCtxt->nsNr = comp->nsNr;
611	ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt);
612	ctxt->xpathCtxt->nsNr = oldNsNr;
613	ctxt->xpathCtxt->namespaces = oldNsList;
614	if (ret == NULL) {
615	    xsltGenericError(xsltGenericErrorContext,
616			     "exsltFuncResultElem: ret == NULL\n");
617	    return;
618	}
619    } else if (inst->children != NULL) {
620	/* If the func:result element does not have a select attribute
621	 * and has non-empty content (i.e. the func:result element has
622	 * one or more child nodes), then the content of the
623	 * func:result element specifies the value.
624	 */
625	xmlNodePtr oldInsert;
626	xmlDocPtr container;
627
628	container = xsltCreateRVT(ctxt);
629	if (container == NULL) {
630	    xsltGenericError(xsltGenericErrorContext,
631			     "exsltFuncResultElem: out of memory\n");
632	    data->error = 1;
633	    return;
634	}
635	xsltRegisterTmpRVT(ctxt, container);
636	oldInsert = ctxt->insert;
637	ctxt->insert = (xmlNodePtr) container;
638	xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node,
639			      inst->children, NULL, NULL);
640	ctxt->insert = oldInsert;
641
642	ret = xmlXPathNewValueTree((xmlNodePtr) container);
643	if (ret == NULL) {
644	    xsltGenericError(xsltGenericErrorContext,
645			     "exsltFuncResultElem: ret == NULL\n");
646	    data->error = 1;
647	} else {
648	    ret->boolval = 0; /* Freeing is not handled there anymore */
649	}
650    } else {
651	/* If the func:result element has empty content and does not
652	 * have a select attribute, then the returned value is an
653	 * empty string.
654	 */
655	ret = xmlXPathNewCString("");
656    }
657    data->result = ret;
658}
659
660/**
661 * exsltFuncRegister:
662 *
663 * Registers the EXSLT - Functions module
664 */
665void
666exsltFuncRegister (void) {
667    xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE,
668		       (xsltExtInitFunction) exsltFuncInit,
669		       (xsltExtShutdownFunction) exsltFuncShutdown,
670		       (xsltStyleExtInitFunction) exsltFuncStyleInit,
671		       (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown);
672
673    xsltRegisterExtModuleTopLevel ((const xmlChar *) "function",
674				   EXSLT_FUNCTIONS_NAMESPACE,
675				   exsltFuncFunctionComp);
676    xsltRegisterExtModuleElement ((const xmlChar *) "result",
677			  EXSLT_FUNCTIONS_NAMESPACE,
678			  (xsltPreComputeFunction)exsltFuncResultComp,
679			  (xsltTransformFunction) exsltFuncResultElem);
680}
681