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