9f92914c18bb2f3e17f43ee7574fdd088ac64357
[platform/upstream/libxslt.git] / libexslt / functions.c
1 #include "libexslt/libexslt.h"
2
3 #if defined(WIN32) && !defined (__CYGWIN__)
4 #include <win32config.h>
5 #else
6 #include "config.h"
7 #endif
8
9 #include <string.h>
10
11 #include <libxml/tree.h>
12 #include <libxml/xpath.h>
13 #include <libxml/xpathInternals.h>
14 #include <libxml/hash.h>
15 #include <libxml/debugXML.h>
16
17 #include <libxslt/xsltutils.h>
18 #include <libxslt/variables.h>
19 #include <libxslt/xsltInternals.h>
20 #include <libxslt/extensions.h>
21 #include <libxslt/transform.h>
22
23 #include "exslt.h"
24
25 typedef struct _exsltFuncFunctionData exsltFuncFunctionData;
26 struct _exsltFuncFunctionData {
27     int nargs;                  /* number of arguments to the function */
28     xmlNodePtr content;         /* the func:fuction template content */
29 };
30
31 typedef struct _exsltFuncData exsltFuncData;
32 struct _exsltFuncData {
33     xmlHashTablePtr funcs;      /* pointer to the stylesheet module data */
34     xmlXPathObjectPtr result;   /* returned by func:result */
35     int error;                  /* did an error occur? */
36 };
37
38 typedef struct _exsltFuncResultPreComp exsltFuncResultPreComp;
39 struct _exsltFuncResultPreComp {
40     xsltElemPreComp comp;
41     xmlXPathCompExprPtr select;
42 };
43
44 static void exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt,
45                                        int nargs);
46
47 /**
48  * exsltFuncRegisterFunc:
49  * @func:  the #exsltFuncFunctionData for the function
50  * @ctxt:  an XSLT transformation context
51  * @URI:  the function namespace URI
52  * @name: the function name
53  *
54  * Registers a function declared by a func:function element
55  */
56 static void
57 exsltFuncRegisterFunc (exsltFuncFunctionData *data,
58                        xsltTransformContextPtr ctxt,
59                        const xmlChar *URI, const xmlChar *name) {
60     if ((data == NULL) || (ctxt == NULL) || (URI == NULL) || (name == NULL))
61         return;
62
63     xsltGenericDebug(xsltGenericDebugContext,
64                      "exsltFuncRegisterFunc: register {%s}%s\n",
65                      URI, name);
66     xsltRegisterExtFunction(ctxt, name, URI,
67                             exsltFuncFunctionFunction);
68 }
69
70 /**
71  * exsltFuncInit:
72  * @ctxt: an XSLT transformation context
73  * @URI: the namespace URI for the extension
74  *
75  * Initializes the EXSLT - Functions module.
76  *
77  * Returns the data for this transformation
78  */
79 static exsltFuncData *
80 exsltFuncInit (xsltTransformContextPtr ctxt, const xmlChar *URI) {
81     xmlHashTablePtr hash;
82     exsltFuncData *ret;
83
84     ret = (exsltFuncData *) xmlMalloc (sizeof(exsltFuncData));
85     if (ret == NULL) {
86         xsltGenericError(xsltGenericErrorContext,
87                          "exsltFuncInit: not enough memory\n");
88         return(NULL);
89     }
90     memset(ret, 0, sizeof(exsltFuncData));
91
92     ret->result = NULL;
93     ret->error = 0;
94
95     hash = (xmlHashTablePtr) xsltStyleGetExtData(ctxt->style, URI);
96     xmlHashScanFull(hash, (xmlHashScannerFull) exsltFuncRegisterFunc, ctxt);
97
98     ret->funcs = hash;
99
100     return(ret);
101 }
102
103 /**
104  * exsltFuncShutdown:
105  * @ctxt: an XSLT transformation context
106  * @URI: the namespace URI for the extension
107  * @data: the module data to free up
108  *
109  * Shutdown the EXSLT - Functions module
110  */
111 static void
112 exsltFuncShutdown (xsltTransformContextPtr ctxt ATTRIBUTE_UNUSED,
113                    const xmlChar *URI ATTRIBUTE_UNUSED,
114                    exsltFuncData *data) {
115     if (data->result != NULL)
116         xmlXPathFreeObject(data->result);
117     xmlFree(data);
118 }
119
120 /**
121  * exsltFuncStyleInit:
122  * @style: an XSLT stylesheet
123  * @URI: the namespace URI for the extension
124  *
125  * Allocates the styleshet data for EXSLT - Function
126  *
127  * Returns the allocated data
128  */
129 static xmlHashTablePtr
130 exsltFuncStyleInit (xsltStylesheetPtr style ATTRIBUTE_UNUSED,
131                     const xmlChar *URI ATTRIBUTE_UNUSED) {
132     return xmlHashCreate(1);
133 }
134
135 /**
136  * exsltFuncStyleShutdown:
137  * @style: an XSLT stylesheet
138  * @URI: the namespace URI for the extension
139  * @data: the stylesheet data to free up
140  *
141  * Shutdown the EXSLT - Function module
142  */
143 static void
144 exsltFuncStyleShutdown (xsltStylesheetPtr style ATTRIBUTE_UNUSED,
145                         const xmlChar *URI ATTRIBUTE_UNUSED,
146                         xmlHashTablePtr data) {
147     xmlHashFree(data, (xmlHashDeallocator) xmlFree);
148 }
149
150 /**
151  * exsltFuncNewFunctionData:
152  *
153  * Allocates an #exslFuncFunctionData object
154  *
155  * Returns the new structure
156  */
157 static exsltFuncFunctionData *
158 exsltFuncNewFunctionData (void) {
159     exsltFuncFunctionData *ret;
160
161     ret = (exsltFuncFunctionData *) xmlMalloc (sizeof(exsltFuncFunctionData));
162     if (ret == NULL) {
163         xsltGenericError(xsltGenericErrorContext,
164                          "exsltFuncNewFunctionData: not enough memory\n");
165         return (NULL);
166     }
167     memset(ret, 0, sizeof(exsltFuncFunctionData));
168
169     ret->nargs = 0;
170     ret->content = NULL;
171
172     return(ret);
173 }
174
175 /**
176  * exsltFreeFuncResultPreComp:
177  * @comp:  the #exsltFuncResultPreComp to free up
178  *
179  * Deallocates an #exsltFuncResultPreComp
180  */
181 static void
182 exsltFreeFuncResultPreComp (exsltFuncResultPreComp *comp) {
183     if (comp == NULL)
184         return;
185
186     if (comp->select != NULL)
187         xmlXPathFreeCompExpr (comp->select);
188     xmlFree(comp);
189 }
190
191 /**
192  * exsltFuncFunctionFunction:
193  * @ctxt:  an XPath parser context
194  * @nargs:  the number of arguments
195  *
196  * Evaluates the func:function element that defines the called function.
197  */
198 static void
199 exsltFuncFunctionFunction (xmlXPathParserContextPtr ctxt, int nargs) {
200     xmlXPathObjectPtr obj, oldResult, ret;
201     exsltFuncData *data;
202     exsltFuncFunctionData *func;
203     xmlNodePtr paramNode, oldInsert, fake;
204     xsltStackElemPtr params = NULL, param;
205     xsltTransformContextPtr tctxt = xsltXPathGetTransformContext(ctxt);
206     int i;
207
208     /*
209      * retrieve func:function template
210      */
211     data = (exsltFuncData *) xsltGetExtData (tctxt,
212                                              EXSLT_FUNCTIONS_NAMESPACE);
213     oldResult = data->result;
214     data->result = NULL;
215
216     func = (exsltFuncFunctionData*) xmlHashLookup2 (data->funcs,
217                                                     ctxt->context->functionURI,
218                                                     ctxt->context->function);
219
220     /*
221      * params handling
222      */
223     if (nargs > func->nargs) {
224         xsltGenericError(xsltGenericErrorContext,
225                          "{%s}%s: called with too many arguments\n",
226                          ctxt->context->functionURI, ctxt->context->function);
227         ctxt->error = XPATH_INVALID_ARITY;
228         return;
229     }
230     if (func->content != NULL)
231         paramNode = func->content->prev;
232     else
233         paramNode = NULL;
234     if ((paramNode == NULL) && (func->nargs != 0)) {
235         xsltGenericError(xsltGenericErrorContext,
236                          "exsltFuncFunctionFunction: nargs != 0 and "
237                          "param == NULL\n");
238         return;
239     }
240     /* defaulted params */
241     for (i = func->nargs; (i > nargs) && (paramNode != NULL); i--) {
242         param = xsltParseStylesheetCallerParam (tctxt, paramNode);
243         param->next = params;
244         params = param;
245         paramNode = paramNode->prev;
246     }
247     /* set params */
248     while ((i-- > 0) && (paramNode != NULL)) {
249         obj = valuePop(ctxt);
250         /* FIXME: this is a bit hackish */
251         param = xsltParseStylesheetCallerParam (tctxt, paramNode);
252         param->computed = 1;
253         if (param->value != NULL)
254             xmlXPathFreeObject(param->value);
255         param->value = obj;
256         param->next = params;
257         params = param;
258         paramNode = paramNode->prev;
259     }
260
261     /*
262      * actual processing
263      */
264     fake = xmlNewDocNode(tctxt->output, NULL,
265                          (const xmlChar *)"fake", NULL);
266     oldInsert = tctxt->insert;
267     tctxt->insert = fake;
268     xsltApplyOneTemplate (tctxt, xmlXPathGetContextNode(ctxt),
269                           func->content, NULL, params);
270     tctxt->insert = oldInsert;
271     if (params != NULL)
272         xsltFreeStackElemList(params);
273
274     if (data->error != 0)
275         return;
276
277     if (data->result != NULL)
278         ret = data->result;
279     else
280         ret = xmlXPathNewCString("");
281
282     data->result = oldResult;
283
284     /*
285      * It is an error if the instantiation of the template results in
286      * the generation of result nodes.
287      */
288     if (fake->children != NULL) {
289 #ifdef LIBXML_DEBUG_ENABLED
290         xmlDebugDumpNode (stderr, fake, 1);
291 #endif
292         xsltGenericError(xsltGenericErrorContext,
293                          "{%s}%s: cannot write to result tree while "
294                          "executing a function\n",
295                          ctxt->context->functionURI, ctxt->context->function);
296         xmlFreeNode(fake);
297         return;
298     }
299     xmlFreeNode(fake);
300     valuePush(ctxt, ret);
301 }
302
303
304 static void
305 exsltFuncFunctionComp (xsltStylesheetPtr style, xmlNodePtr inst) {
306     xmlChar *name, *prefix;
307     xmlNsPtr ns;
308     xmlHashTablePtr data;
309     exsltFuncFunctionData *func;
310
311     if ((style == NULL) || (inst == NULL))
312         return;
313
314
315     {
316         xmlChar *qname;
317
318         qname = xmlGetProp(inst, (const xmlChar *) "name");
319         name = xmlSplitQName2 (qname, &prefix);
320         xmlFree(qname);
321     }
322     if ((name == NULL) || (prefix == NULL)) {
323         xsltGenericError(xsltGenericErrorContext,
324                          "func:function: not a QName\n");
325         if (name != NULL)
326             xmlFree(name);
327         return;
328     }
329     /* namespace lookup */
330     ns = xmlSearchNs (inst->doc, inst, prefix);
331     if (ns == NULL) {
332         xsltGenericError(xsltGenericErrorContext,
333                          "func:function: undeclared prefix %s\n",
334                          prefix);
335         xmlFree(name);
336         xmlFree(prefix);
337         return;
338     }
339     xmlFree(prefix);
340
341     /*
342      * Create function data
343      */
344     func = exsltFuncNewFunctionData();
345     func->content = inst->children;
346     while (IS_XSLT_ELEM(func->content) &&
347            IS_XSLT_NAME(func->content, "param")) {
348         func->content = func->content->next;
349         func->nargs++;
350     }
351
352     xsltParseTemplateContent(style, inst);
353
354     /*
355      * Register the function data such that it can be retrieved
356      * by exslFuncFunctionFunction
357      */
358     data = (xmlHashTablePtr) xsltStyleGetExtData (style,
359                                                   EXSLT_FUNCTIONS_NAMESPACE);
360     if (data == NULL) {
361         xsltGenericError(xsltGenericErrorContext,
362                          "exsltFuncFunctionComp: no stylesheet data\n");
363         xmlFree(name);
364         return;
365     }
366
367     if (xmlHashAddEntry2 (data, ns->href, name, func) < 0) {
368         xsltGenericError(xsltGenericErrorContext,
369                          "Failed to register function {%s}%s\n",
370                          ns->href, name);
371     } else {
372         xsltGenericDebug(xsltGenericDebugContext,
373                          "exsltFuncFunctionComp: register {%s}%s\n",
374                          ns->href, name);
375     }
376     xmlFree(name);
377 }
378
379 static xsltElemPreCompPtr
380 exsltFuncResultComp (xsltStylesheetPtr style, xmlNodePtr inst,
381                      xsltTransformFunction function) {
382     xmlNodePtr test;
383     xmlChar *select;
384     exsltFuncResultPreComp *ret;
385
386     /*
387      * "Validity" checking
388      */
389     /* it is an error to have any following sibling elements aside
390      * from the xsl:fallback element.
391      */
392     for (test = inst->next; test != NULL; test = test->next) {
393         if (test->type != XML_ELEMENT_NODE)
394             continue;
395         if (IS_XSLT_ELEM(test) && IS_XSLT_NAME(test, "fallback"))
396             continue;
397         xsltGenericError(xsltGenericErrorContext,
398                          "exsltFuncResultElem: only xsl:fallback is "
399                          "allowed to follow func:result\n");
400         return (NULL);
401     }
402     /* it is an error for a func:result element to not be a descendant
403      * of func:function.
404      * it is an error if a func:result occurs within a func:result
405      * element.
406      * it is an error if instanciating the content of a variable
407      * binding element (i.e. xsl:variable, xsl:param) results in the 
408      * instanciation of a func:result element.
409      */
410     for (test = inst->parent; test != NULL; test = test->parent) {
411         if ((test->ns != NULL) &&
412             (xmlStrEqual(test->ns->href, EXSLT_FUNCTIONS_NAMESPACE))) {
413             if (xmlStrEqual(test->name, (const xmlChar *) "function")) {
414                 break;
415             }
416             if (xmlStrEqual(test->name, (const xmlChar *) "result")) {
417                 xsltGenericError(xsltGenericErrorContext,
418                                  "func:result element not allowed within"
419                                  " another func:result element\n");
420                 return (NULL);
421             }
422         }
423         if (IS_XSLT_ELEM(test) &&
424             (IS_XSLT_NAME(test, "variable") ||
425              IS_XSLT_NAME(test, "param"))) {
426             xsltGenericError(xsltGenericErrorContext,
427                              "func:result element not allowed within"
428                              " a variable binding element\n");
429             return (NULL);
430         }
431     }
432
433     /*
434      * Precomputation
435      */
436     ret = (exsltFuncResultPreComp *)
437         xmlMalloc (sizeof(exsltFuncResultPreComp));
438     if (ret == NULL) {
439         xsltPrintErrorContext(NULL, NULL, NULL);
440         xsltGenericError(xsltGenericErrorContext,
441                          "exsltFuncResultComp : malloc failed\n");
442         return (NULL);
443     }
444     memset(ret, 0, sizeof(exsltFuncResultPreComp));
445
446     xsltInitElemPreComp ((xsltElemPreCompPtr) ret, style, inst, function,
447                  (xsltElemPreCompDeallocator) exsltFreeFuncResultPreComp);
448     ret->select = NULL;
449
450     /*
451      * Precompute the select attribute
452      */
453     select = xmlGetNsProp(inst, (const xmlChar *) "select", NULL);
454     if (select != NULL) {
455         ret->select = xmlXPathCompile (select);
456         xmlFree(select);
457     }
458
459     return ((xsltElemPreCompPtr) ret);
460 }
461
462 static void
463 exsltFuncResultElem (xsltTransformContextPtr ctxt,
464                      xmlNodePtr node ATTRIBUTE_UNUSED, xmlNodePtr inst,
465                      exsltFuncResultPreComp *comp) {
466     exsltFuncData *data;
467     xmlXPathObjectPtr ret;
468
469     /* It is an error if instantiating the content of the
470      * func:function element results in the instantiation of more than
471      * one func:result elements.
472      */
473     data = (exsltFuncData *) xsltGetExtData (ctxt, EXSLT_FUNCTIONS_NAMESPACE);
474     if (data == NULL) {
475         xsltGenericError(xsltGenericErrorContext,
476                          "exsltFuncReturnElem: data == NULL\n");
477         return;
478     }
479     if (data->result != NULL) {
480         xsltGenericError(xsltGenericErrorContext,
481                          "func:result already instanciated\n");
482         data->error = 1;
483         return;
484     }
485     /*
486      * Processing
487      */
488     if (comp->select != NULL) {
489         /* If the func:result element has a select attribute, then the
490          * value of the attribute must be an expression and the
491          * returned value is the object that results from evaluating
492          * the expression. In this case, the content must be empty.
493          */
494         if (inst->children != NULL) {
495             xsltGenericError(xsltGenericErrorContext,
496                              "func:result content must be empty if it"
497                              " has a select attribute\n");
498             data->error = 1;
499             return;
500         }
501         ret = xmlXPathCompiledEval(comp->select, ctxt->xpathCtxt);
502         if (ret == NULL) {
503             xsltGenericError(xsltGenericErrorContext,
504                              "exsltFuncResultElem: ret == NULL\n");
505             return;
506         }
507     } else if (inst->children != NULL) {
508         /* If the func:result element does not have a select attribute
509          * and has non-empty content (i.e. the func:result element has
510          * one or more child nodes), then the content of the
511          * func:result element specifies the value.
512          */
513         xmlNodePtr container, oldInsert;
514
515         container = xmlNewDocNode (ctxt->output, NULL,
516                                    (const xmlChar *) "fake", NULL);
517         oldInsert = ctxt->insert;
518         ctxt->insert = container;
519         xsltApplyOneTemplate (ctxt, ctxt->xpathCtxt->node,
520                               inst->children, NULL, NULL);
521         ctxt->insert = oldInsert;
522
523         ret = xmlXPathNewValueTree(container);
524         if (ret == NULL) {
525             xsltGenericError(xsltGenericErrorContext,
526                              "exsltFuncResultElem: ret == NULL\n");
527             data->error = 1;
528         }
529     } else {
530         /* If the func:result element has empty content and does not
531          * have a select attribute, then the returned value is an
532          * empty string.
533          */
534         ret = xmlXPathNewCString("");
535     }
536     data->result = ret;
537 }
538
539 /**
540  * exsltFuncRegister:
541  *
542  * Registers the EXSLT - Functions module
543  */
544 void
545 exsltFuncRegister (void) {
546     xsltRegisterExtModuleFull (EXSLT_FUNCTIONS_NAMESPACE,
547                        (xsltExtInitFunction) exsltFuncInit,
548                        (xsltExtShutdownFunction) exsltFuncShutdown,
549                        (xsltStyleExtInitFunction) exsltFuncStyleInit,
550                        (xsltStyleExtShutdownFunction) exsltFuncStyleShutdown);
551
552     xsltRegisterExtModuleTopLevel ((const xmlChar *) "function",
553                                    EXSLT_FUNCTIONS_NAMESPACE,
554                                    exsltFuncFunctionComp);
555     xsltRegisterExtModuleElement ((const xmlChar *) "result",
556                           EXSLT_FUNCTIONS_NAMESPACE,
557                           (xsltPreComputeFunction)exsltFuncResultComp,
558                           (xsltTransformFunction) exsltFuncResultElem);
559 }