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