GnmFunc: make this a GObject.
[gnumeric.git] / plugins / excelplugins / excelplugins.c
blobc21e161145ff1f12a6489afc626b8e376aecb0d2
1 /*
2 * excelplugins.c:
4 * Adapter interface to load worksheet functions defined in Excel
5 * plugins (also known as XLLs). Note that this adapter interface
6 * only works for XLL worksheet functions that expect all their
7 * arguments to be of type OPER (type 'P' in the Excel SDK
8 * documentation) or of type eXtended exceL OPER (type 'R' in the
9 * Excel SDK documentation known as XLOPER) and that return their
10 * results as an OPER (type 'P'). Note that type 'R' can give rise to
11 * sheet references to be passed in which this code here cannot handle
12 * whence it is best to only use type 'P'.
14 * Author:
15 * Peter Jaeckel (peter.jaeckel@gmail.com)
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, see <https://www.gnu.org/licenses/>.
30 #include <gnumeric-config.h>
31 #include <gnumeric.h>
32 #include <func.h>
33 #include <value.h>
34 #include <gnm-i18n.h>
35 #include <gnm-plugin.h>
36 #include <goffice/utils/go-glib-extras.h>
37 #include <stdint.h>
38 #include <glib/gstdio.h>
39 #include <workbook.h>
40 #include <sheet.h>
41 #include <cell.h>
42 #include <string.h>
44 #ifdef WIN32
45 #include <windows.h>
46 #else
47 #include <sys/mman.h>
48 #endif
50 #define g_slice_new_array0(T,c) ((T*) g_slice_alloc0 ((sizeof (T))*(c)))
51 #define ALLOC_ARRAY(T,c) ( g_slice_new_array0 (T,c) )
52 #define FREE_ARRAY(p,c) ( g_slice_free1 (sizeof(*p)*(c),(p)) )
54 GNM_PLUGIN_MODULE_HEADER;
56 /***************************************************************************/
58 /* When Gnumeric calls a worksheet function, it directly calls into
59 * the given entry point, and passes in data in Gnumeric style. Since
60 * an external XLL's exposed functions expect data in Excel format,
61 * those functions' entry points cannot simply be forwarded to
62 * Gnumeric. Instead, a conversion function is invoked. Luckily, the
63 * Gnumeric infrastructure provides EvalFunctionInfo pointer at the
64 * time of call. Given the EvalFunctionInfo *ei, we have the name of
65 * the function we are calling in ei->func_call->func->name. We can
66 * use this, to look up which function is actually to be called. For
67 * the look-up, we create a global function map (name->some structure)
68 * at the time of go_plugin_init that we peruse later. At the heart of
69 * the communication between Gnumeric and the XLL is a little adapter
70 * function, sometimes called a "shim". This function is the one that
71 * is actually called by Gnumeric when it tries to call one of the
72 * functions that actually live in the XLL. The shim is called
74 * genericXLLFunction () .
76 * Once it has looked up the function name in its information
77 * database, it knows how many arguments of what type the external XLL
78 * function expects, and can convert argument types, call the actual
79 * XLL function, convert the returned value, clean up memory, and
80 * return to Gnumeric.
84 #if defined( WIN32 ) || defined( WIN64 )
85 #include <windef.h>
86 #else
87 #include "win32replacements.h"
88 #endif
90 #include "xlcall.h"
92 /* All bits that are mutually exclusive in the type field of an xloper. */
93 #define xltypeType 0x0FFF
95 struct _XLL {
96 gchar * name;
97 GModule * handle;
98 void (*xlAutoFree)(XLOPER*);
99 unsigned long number_of_functions;
102 typedef struct _XLL XLL;
104 struct _XLLFunctionInfo {
105 XLL* xll; /* Not owned. Included for availability of information during callbacks, and for access to xlAutoFree. */
106 XLOPER* (*xll_function)(void);
107 gchar* category;
108 GnmFuncDescriptor gnm_func_descriptor;
109 guint number_of_arguments;
110 GnmFunc* gnm_func;
113 typedef struct _XLLFunctionInfo XLLFunctionInfo;
115 static GModule *xlcall32_handle = NULL;
116 static void (*register_actual_excel4v)(void*p) = NULL;
118 static GSList* XLLs = NULL;
120 /* This balanced tree maps from function name (gchar*) to
121 XLLFunctionInfo*. It is consulted when gnumeric attempts to call
122 one of the registered functions. Note that the memory of any key is
123 handled as part of the memory of its associated value since the key
124 is the same as the element gnm_func_descriptor.name. */
125 static GTree* xll_function_info_map = NULL;
127 static GnmFuncEvalInfo * current_eval_info = NULL;
128 static XLL * currently_called_xll = NULL;
130 /* The limit of 30 arguments is hardwired in Excel. We take advantage of this below when we convert from argv[] calling
131 convention to vararg calling convention (...) */
132 enum { MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS = 30 };
134 /* If data types other than XLOPER* are to be allowed, then something
135 like the Foreign Function Interface library (libffi) would have to
136 be used in order to get the arguments pushed on the stack prior to
137 calling the XLL function. As long as all data are XLOPER*, we can
138 use this one function type to call all XLL functions. */
139 typedef XLOPER*(*XLLFunctionWithVarArgs)(XLOPER* first, ...);
141 #define CASE(x) case x: return #x
143 #ifdef THIS_IS_HERE_FOR_DEBUG_PURPOSES
144 static const char *
145 gnm_value_type_name (const GnmValue*g)
147 if (NULL != g) {
148 switch (g->v_any.type) {
149 CASE(VALUE_EMPTY);
150 CASE(VALUE_BOOLEAN);
151 CASE(VALUE_FLOAT);
152 CASE(VALUE_ERROR);
153 CASE(VALUE_STRING);
154 CASE(VALUE_CELLRANGE);
155 CASE(VALUE_ARRAY);
156 default: return "<unknown>";
159 return "(nil)";
161 #endif
163 static const char *
164 xloper_type_name (const XLOPER*x)
166 if (NULL != x) {
167 switch (x->xltype & xltypeType) {
168 CASE(xltypeNum);
169 CASE(xltypeStr);
170 CASE(xltypeBool);
171 CASE(xltypeRef);
172 CASE(xltypeErr);
173 CASE(xltypeFlow);
174 CASE(xltypeMulti);
175 CASE(xltypeMissing);
176 CASE(xltypeNil);
177 CASE(xltypeSRef);
178 CASE(xltypeInt);
179 default: return "<unknown>";
182 return "(nil)";
185 static void
186 unsupported_xloper_type (const XLOPER*x)
188 g_warning ("Unsupported xloper type \"%s\"",
189 xloper_type_name (x));
192 static GnmStdError
193 gnm_value_error_from_xloper (const XLOPER *x)
195 g_return_val_if_fail (NULL != x, GNM_ERROR_UNKNOWN);
196 g_return_val_if_fail ((x->xltype & xltypeType)==xltypeErr,
197 GNM_ERROR_UNKNOWN);
199 switch (x->val.err) {
200 case xlerrNull: return GNM_ERROR_NULL;
201 case xlerrDiv0: return GNM_ERROR_DIV0;
202 case xlerrValue: return GNM_ERROR_VALUE;
203 case xlerrRef: return GNM_ERROR_REF;
204 case xlerrName: return GNM_ERROR_NAME;
205 case xlerrNum: return GNM_ERROR_NUM;
206 case xlerrNA: return GNM_ERROR_NA;
207 default: return GNM_ERROR_UNKNOWN;
211 static WORD
212 xloper_error_code_from_gnm_value (const GnmValue* g)
214 switch (value_error_classify (g)) {
215 case GNM_ERROR_NULL: return xlerrNull;
216 case GNM_ERROR_DIV0: return xlerrDiv0;
217 case GNM_ERROR_VALUE: return xlerrValue;
218 case GNM_ERROR_REF: return xlerrRef;
219 case GNM_ERROR_NAME: return xlerrName;
220 case GNM_ERROR_NUM: return xlerrNum;
221 case GNM_ERROR_NA: return xlerrNA;
222 default: return xlerrValue;
227 * For most standard types, there is a natural mapping between XLOPER and GnmValue.
228 * In addition, we use the following correspondence:
230 * | GnmValue * v | XLOPER * x |
231 * ------------------------+------------------------------+------------------------------------------------------------+
232 * | v == NULL <-> x->xltype == xltypeMissing |
233 * | v->type == VALUE_EMPTY <-> x->xltype == xltypeNil |
234 * | v->type == VALUE_ERROR <-> x->xltype == xltypeErr |
235 * ------------------------+------------------------------+------------------------------------------------------------+
239 static char *
240 pascal_string_from_c_string (const char *s)
242 char *o = NULL;
243 if (NULL != s) {
244 guint l = strlen (s);
245 g_return_val_if_fail (l<(UINT_MAX-2U),NULL);
246 o = g_malloc (l+2U);
247 g_strlcpy (o+1,s,l+1);
248 if (l>UCHAR_MAX)
249 l=UCHAR_MAX;
250 o[0] = (unsigned char)l;
252 return o;
255 static char *
256 c_string_from_pascal_string (const char *s)
258 if (NULL != s) {
259 const guint m = ((unsigned char)s[0])+1U;
260 char * o = g_malloc (m);
261 g_strlcpy (o,s+1,m);
262 return o;
264 return NULL;
267 static void
268 copy_construct_xloper_from_gnm_value (XLOPER*out, const GnmValue*in,
269 GnmFuncEvalInfo *ei)
271 g_return_if_fail (NULL != out);
273 out->xltype = xltypeMissing;
274 out->val.num = 0;
276 if (NULL != in) {
277 switch (in->v_any.type) {
278 case VALUE_EMPTY:
279 out->xltype = xltypeNil;
280 break;
281 case VALUE_BOOLEAN:
282 out->xltype = xltypeBool;
283 out->val.boolean = (WORD)value_get_as_checked_bool (in);
284 break;
285 case VALUE_FLOAT:
286 out->xltype = xltypeNum;
287 out->val.num = in->v_float.val;
288 break;
289 case VALUE_ERROR:
290 out->xltype = xltypeErr;
291 out->val.err = xloper_error_code_from_gnm_value (in);
292 break;
293 case VALUE_STRING:
294 out->xltype = xltypeStr;
295 out->val.str = pascal_string_from_c_string (value_peek_string (in));
296 break;
297 case VALUE_CELLRANGE: {
298 GnmSheetRange sr;
299 GnmRangeRef const *rr = value_get_rangeref (in);
300 Sheet *end_sheet = NULL;
301 GnmValue *cell_value;
302 GnmCell *cell;
303 gnm_rangeref_normalize (rr, ei->pos, &sr.sheet, &end_sheet, &sr.range);
304 if (sr.sheet != end_sheet) {
305 /* We don't attempt to flatten a 3D range to an array. */
306 g_warning (_("Cannot convert 3D cell range to XLOPER."));
307 } else {
308 int m = sr.range.end.col-sr.range.start.col+1;
309 int n = sr.range.end.row-sr.range.start.row+1;
310 int i, j;
312 out->xltype = xltypeMulti;
313 out->val.array.lparray = ALLOC_ARRAY (XLOPER,m*n);
314 out->val.array.columns = m;
315 out->val.array.rows = n;
316 for (i = 0; i < m; ++i) {
317 for (j = 0;j<n; ++j) {
318 cell = sheet_cell_get (sr.sheet,sr.range.start.col+i,sr.range.start.row+j);
319 cell_value = NULL;
320 if (NULL != cell) {
321 gnm_cell_eval (cell);
322 cell_value=cell->value;
324 copy_construct_xloper_from_gnm_value (out->val.array.lparray+i+j*m,cell_value,ei);
328 break;
330 case VALUE_ARRAY: {
331 int m = in->v_array.x;
332 int n = in->v_array.y;
333 int i, j;
335 out->xltype = xltypeMulti;
336 out->val.array.lparray = ALLOC_ARRAY (XLOPER,m*n);
337 out->val.array.columns = m;
338 out->val.array.rows = n;
339 for (i = 0; i < m; ++i) {
340 for (j = 0;j < n; ++j) {
341 copy_construct_xloper_from_gnm_value (out->val.array.lparray+i+j*m,in->v_array.vals[i][j],ei);
344 break;
346 default:;
347 g_warning (_("Unsupported GnmValue type (%d)"),in->v_any.type);
352 static void
353 delete_string (gchar **s)
355 if (s) {
356 g_free (*s);
357 *s = NULL;
361 static void
362 destruct_xloper (XLOPER*x)
364 if (NULL != x) {
365 switch (x->xltype & xltypeType) {
366 case xltypeNum:
367 break;
368 case xltypeStr:
369 delete_string (&x->val.str);
370 break;
371 case xltypeBool:
372 break;
373 case xltypeRef:
374 if (NULL != x->val.mref.lpmref &&
375 x->val.mref.lpmref->count != 1) {
376 unsupported_xloper_type (x);
377 } else {
378 if (NULL != x->val.mref.lpmref)
379 FREE_ARRAY (x->val.mref.lpmref, 1);
380 x->val.mref.lpmref = NULL;
382 break;
383 case xltypeErr:
384 break;
385 case xltypeFlow:
386 unsupported_xloper_type (x);
387 break;
388 case xltypeMulti: {
389 int n = x->val.array.rows*x->val.array.columns;
390 int i;
391 for (i = 0; i < n; ++i) {
392 destruct_xloper (x->val.array.lparray+i);
394 FREE_ARRAY (x->val.array.lparray,n);
395 break;
397 case xltypeMissing:
398 break;
399 case xltypeNil:
400 break;
401 case xltypeSRef:
402 unsupported_xloper_type (x);
403 break;
404 case xltypeInt:
405 break;
406 default:
407 unsupported_xloper_type (x);
409 x->xltype = xltypeNil;
413 static GnmValue *
414 new_gnm_value_from_xloper (const XLOPER*x)
416 GnmValue * g = NULL;
417 if (NULL != x) {
418 switch (x->xltype & xltypeType) {
419 case xltypeNum:
420 g = value_new_float (x->val.num);
421 break;
422 case xltypeStr: {
423 char *o = NULL;
424 const char *s = x->val.str;
425 if (NULL != s) {
426 guint m = ((unsigned char)s[0]) + 1U;
427 o = g_new (char, m);
428 g_strlcpy (o, s + 1, m);
430 g = value_new_string_nocopy (o);
431 break;
433 case xltypeBool:
434 g = value_new_bool (x->val.boolean);
435 break;
436 case xltypeRef:
437 unsupported_xloper_type (x);
438 break;
439 case xltypeErr:
440 g = value_new_error_std (NULL, gnm_value_error_from_xloper (x));
441 break;
442 case xltypeFlow:
443 unsupported_xloper_type (x);
444 break;
445 case xltypeMulti: {
446 guint m = x->val.array.columns;
447 guint n = x->val.array.rows;
448 if (m > 0 && n > 0) {
449 guint i;
450 g = value_new_array_empty (m,n);
451 for (i = 0; i < m; ++i) {
452 guint j;
453 for (j = 0; j < n; ++j) {
454 g->v_array.vals[i][j] =
455 new_gnm_value_from_xloper (x->val.array.lparray + i + j * m);
458 } else {
459 g = value_new_error_std (NULL, GNM_ERROR_VALUE);
461 break;
463 case xltypeMissing:
464 break;
465 case xltypeNil:
466 g = value_new_empty ();
467 break;
468 case xltypeSRef:
469 unsupported_xloper_type (x);
470 break;
471 case xltypeInt:
472 g = value_new_int (x->val.w);
473 break;
474 default:
475 unsupported_xloper_type (x);
477 } else {
478 g = value_new_error_std (NULL, GNM_ERROR_NUM);
480 return g;
483 static GnmValue *
484 genericXLLFunction (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
486 XLOPER x[MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS], *r = NULL;
487 XLLFunctionWithVarArgs func = NULL;
488 GnmValue *g = NULL;
489 guint i,m;
490 const XLLFunctionInfo*info = NULL;
491 GnmFunc const *gfunc = gnm_eval_info_get_func (ei);
493 g_assert (NULL != xll_function_info_map);
494 info=g_tree_lookup (xll_function_info_map, gfunc->name);
495 g_assert (NULL != info);
496 m = gnm_eval_info_get_arg_count (ei);
497 m = MAX (m, MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS);
498 for (i = 0; i < m; ++i)
499 copy_construct_xloper_from_gnm_value (x+i,argv[i],ei);
500 #if 0
501 m = info->number_of_arguments;
502 m = MAX (m, MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS);
503 #else
504 // MW 20180515: it's unclear to me what is needed, but the above
505 // cannot be right. This should be safe.
506 m = MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS;
507 #endif
508 for (; i < m; ++i)
509 x[i].xltype=xltypeMissing;
510 func = (XLLFunctionWithVarArgs)info->xll_function;
511 g_assert (NULL != func);
512 currently_called_xll = info->xll;
513 current_eval_info = ei;
514 switch (info->number_of_arguments) {
515 default: g_assert_not_reached ();
516 case 0: r=info->xll_function (); break;
518 bash script to generate code below
519 n=0; while [ $n -le 30 ] ; do echo -n " case "; if [ $n -lt 10 ] ; then echo -n " "; fi; echo -n "$n: r=func ("; j = 0; while [ $j -lt $n ]; do echo -n "x+$j"; if [ $[ $j + 1] -lt $n ] ; then echo -n "," ; fi ; j=$[ $j + 1 ] ; done ; echo "); break;" ; n=$[ $n + 1 ] ; done
521 case 1: r=func (x+0); break;
522 case 2: r=func (x+0,x+1); break;
523 case 3: r=func (x+0,x+1,x+2); break;
524 case 4: r=func (x+0,x+1,x+2,x+3); break;
525 case 5: r=func (x+0,x+1,x+2,x+3,x+4); break;
526 case 6: r=func (x+0,x+1,x+2,x+3,x+4,x+5); break;
527 case 7: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6); break;
528 case 8: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7); break;
529 case 9: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8); break;
530 case 10: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9); break;
531 case 11: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10); break;
532 case 12: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11); break;
533 case 13: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12); break;
534 case 14: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13); break;
535 case 15: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14); break;
536 case 16: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15); break;
537 case 17: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16); break;
538 case 18: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17); break;
539 case 19: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18); break;
540 case 20: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19); break;
541 case 21: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20); break;
542 case 22: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21); break;
543 case 23: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22); break;
544 case 24: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22,x+23); break;
545 case 25: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22,x+23,x+24); break;
546 case 26: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22,x+23,x+24,x+25); break;
547 case 27: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22,x+23,x+24,x+25,x+26); break;
548 case 28: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22,x+23,x+24,x+25,x+26,x+27); break;
549 case 29: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22,x+23,x+24,x+25,x+26,x+27,x+28); break;
550 case 30: r=func (x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8,x+9,x+10,x+11,x+12,x+13,x+14,x+15,x+16,x+17,x+18,x+19,x+20,x+21,x+22,x+23,x+24,x+25,x+26,x+27,x+28,x+29); break;
552 g=new_gnm_value_from_xloper (r);
553 if ( r && (r->xltype & xlbitDLLFree) && (NULL != info->xll->xlAutoFree) )
554 info->xll->xlAutoFree ( r );
555 currently_called_xll = NULL;
556 current_eval_info = NULL;
557 for (i = 0; i < info->number_of_arguments; ++i)
558 destruct_xloper (x+i);
559 return g;
562 /***************************************************************************/
564 static int
565 gnm_func_help_entries (int number_of_arguments)
567 /* NAME,DESCRIPTION,EXCEL,ARG1,...ARGn,END */
568 return number_of_arguments + 4;
571 static void
572 free_xll_function_info (gpointer data)
574 XLLFunctionInfo *info= (XLLFunctionInfo *)data;
575 const guint n = info->number_of_arguments;
576 if (NULL != info->gnm_func) {
577 g_object_unref (info->gnm_func);
578 info->gnm_func = NULL;
580 delete_string (&info->category);
581 delete_string ((gchar**)&info->gnm_func_descriptor.name);
582 delete_string ((gchar**)&info->gnm_func_descriptor.arg_spec);
583 if (NULL != info->gnm_func_descriptor.help) {
584 guint i, m=gnm_func_help_entries (n);
585 for (i = 0; i < m; ++i) {
586 delete_string ((gchar**)&info->gnm_func_descriptor.help[i].text);
588 FREE_ARRAY ((GnmFuncHelp*)info->gnm_func_descriptor.help,m);
589 info->gnm_func_descriptor.help = NULL;
591 info->gnm_func_descriptor.fn_args = NULL;
592 info->number_of_arguments = 0;
593 info->xll_function = NULL;
594 info->gnm_func_descriptor.fn_args = NULL;
595 FREE_ARRAY (info,1);
598 typedef int WINAPI (*XLAutoOpenFunc)(void);
599 typedef int WINAPI (*XLAutoCloseFunc)(void);
601 static void
602 free_XLL (gpointer data)
604 XLL*xll=(XLL*)data;
605 if (NULL != xll->handle) {
606 XLAutoCloseFunc xlAutoCloseFunc = NULL;
607 g_module_symbol (xll->handle, "xlAutoClose", (gpointer) &xlAutoCloseFunc);
608 if (NULL != xlAutoCloseFunc) {
609 currently_called_xll = xll;
610 xlAutoCloseFunc ();
611 currently_called_xll = NULL;
613 if (!g_module_close (xll->handle))
614 g_warning (_("%s: %s"), xll->name, g_module_error ());
615 xll->handle = NULL;
617 delete_string (&xll->name);
618 FREE_ARRAY (xll,1);
621 static gint
622 g_strcmp0_with_ignored_data (gconstpointer str1, gconstpointer str2,
623 gpointer user_data)
625 return g_strcmp0 (str1, str2);
628 static gboolean
629 add_xll_function (const char *exported_function_symbol, XLLFunctionInfo *info)
631 g_module_symbol (info->xll->handle, exported_function_symbol, (gpointer) &info->xll_function);
632 if (NULL != info->xll_function) {
633 XLLFunctionInfo* info_in_map = NULL;
634 GnmFunc *gnm_func = NULL;
635 if (NULL == xll_function_info_map)
636 xll_function_info_map = g_tree_new_full (g_strcmp0_with_ignored_data,NULL,NULL,free_xll_function_info);
637 info_in_map = g_tree_lookup (xll_function_info_map,info->gnm_func_descriptor.name);
638 if (NULL != info_in_map)
639 g_warning (_("Overriding function %s from XLL/DLL/SO file %s with function of the same name from XLL/DLL/SO file %s."),
640 info->gnm_func_descriptor.name,info_in_map->xll->name,info->xll->name);
641 gnm_func = gnm_func_add (gnm_func_group_fetch (info->category,NULL),&info->gnm_func_descriptor,NULL);
642 if (gnm_func) {
643 info->gnm_func=gnm_func;
644 g_tree_insert (xll_function_info_map,(gpointer)info->gnm_func_descriptor.name,info);
645 ++info->xll->number_of_functions;
646 return TRUE;
648 } else {
649 g_warning (_("Failed to find function \"%s\" in XLL/DLL/SO %s .\n"),info->gnm_func_descriptor.name,info->xll->name);
651 return FALSE;
654 static int
655 actual_Excel4v (int xlfn, XLOPER* operRes, int count, XLOPER** opers)
657 switch (xlfn) {
658 case xlfRegister: {
659 XLLFunctionInfo* info = NULL;
660 GnmFuncHelp * help = NULL;
661 GnmFuncFlags flags = GNM_FUNC_SIMPLE; /* We may have to include GNM_FUNC_RETURNS_NON_SCALAR here
662 since all Excel functions may return an array. Having said
663 that, all array functions seem to work fine without this
664 flag. */
665 GString * argument_specifications = g_string_sized_new (21);
666 gchar ** arg_names = NULL, *exported_function_symbol = NULL, *function_description = NULL;
667 guint number_of_arguments = 0;
668 gboolean success = FALSE;
669 int i,j,m,n;
671 * Check http://msdn.microsoft.com/en-us/library/bb687900.aspx for details.
673 * opers[ 0] : DLL name (string). Might have been queried by the XLL by calling in here as xlGetName prior to registering (that's how it's usually done).
674 * This feature allows, in principle, for one XLL (the one calling Excel4v) to register functions that physically reside in another DLL.
675 We do not allow for this feature here.
676 IGNORED HERE.
677 * opers[ 1] : exported function symbol in XLL/DLL (string). To be located with g_module_symbol ().
678 MANDATORY.
679 * opers[ 2] : return and argument types string. Should be a string of only 'P's or 'R's in this context (apart from volatile markers etc).
680 MANDATORY.
681 * opers[ 3] : function name that is to be shown as the effective, user-visible, work sheet function.
682 DEFAULTS to opers[1].
683 * opers[ 4] : string of comma separated argument names.
684 DEFAULTS to NULL.
685 * opers[ 5] : function type as xltypeNum. 0.=hidden, 1.=function, 2.=command.
686 IGNORED HERE.
687 * opers[ 6] : function category as string.
688 DEFAULTS to "XLL functions".
689 * opers[ 7] : short cut text as string.
690 IGNORED HERE.
691 * opers[ 8] : help topic as string.
692 IGNORED HERE.
693 * opers[ 9] : function description as string.
694 DEFAULTS to NULL.
695 * opers[10 .. 10+n-1] : Help on the n arguments of actual function that is being registered.
696 DEFAULT to NULL.
698 if (count<3) {
699 g_warning (_("Excel plugin loader / xlfRegister: at least three XLOPER arguments must be provided (DLL name[ignored],exported name[mandatory],types string[mandatory]). You supplied %d in some function loaded from XLL/DLL/SO file %s."),count,currently_called_xll->name);
700 return xlretInvCount; /* "An invalid number of arguments was entered. In versions up to Excel
701 2003, the maximum number of arguments any function can take is 30. In
702 Excel 2007, the maximum number is 255. Some require a fixed or minimum
703 number of arguments." */
705 if ( xltypeStr != (opers[1]->xltype & xltypeType) || xltypeStr != (opers[2]->xltype & xltypeType)) {
706 g_warning (_("Excel plugin loader / xlfRegister: the second and third argument must be strings (DLL name[ignored],exported name[mandatory],types string[mandatory])."));
707 return xlretInvXloper; /* "An invalid XLOPER or XLOPER12 was passed to the function, or an argument of the wrong type was used." */
709 m=0;
710 if (opers[2]->val.str)
711 m = (unsigned char)opers[2]->val.str[0];
712 for (i = 0; i < m; ++i) {
713 switch (opers[2]->val.str[i+1]) {
714 case 'p':
715 case 'P':
716 case 'r':
717 case 'R':
718 g_string_append_c (argument_specifications,'?');
719 ++number_of_arguments;
720 break;
721 case '\r': /* Various junk we may as well ignore. */
722 case '\n':
723 case '\t':
724 case ',':
725 case ';':
726 case ' ':
727 case '#': /* This signals a request for this function or macro to have access to macro specific call back functions. Ignored. */
728 break;
729 case '!':
730 flags |= GNM_FUNC_VOLATILE;
731 break;
732 default:;
735 exported_function_symbol=c_string_from_pascal_string (opers[1]->val.str);
736 g_assert (argument_specifications->len==number_of_arguments);
737 if (number_of_arguments>0) {
738 --number_of_arguments; /* Subtract the return type count. The if statement is only to protect against sloppy XLLs. */
739 argument_specifications->str[0] = '|'; /* All arguments are optional to an Excel function that accepts arguments type 'P'. */
741 info=ALLOC_ARRAY (XLLFunctionInfo,1);
742 info->xll=currently_called_xll;
743 info->number_of_arguments = number_of_arguments;
744 info->gnm_func_descriptor.arg_spec=g_strdup (argument_specifications->str);
745 info->gnm_func_descriptor.flags = flags;
746 if (count>3 && (opers[3]->xltype & xltypeType)==xltypeStr) {
747 info->gnm_func_descriptor.name = c_string_from_pascal_string (opers[3]->val.str);
748 } else {
749 info->gnm_func_descriptor.name = g_strdup (exported_function_symbol);
751 if (count>4 && (opers[4]->xltype & xltypeType)==xltypeStr) {
752 gchar* xll_arg_names = c_string_from_pascal_string (opers[4]->val.str);
753 arg_names = g_strsplit (xll_arg_names,",",number_of_arguments);
754 delete_string (&xll_arg_names);
756 if (count>6 && (opers[6]->xltype & xltypeType)==xltypeStr) {
757 info->category = c_string_from_pascal_string (opers[6]->val.str);
758 } else {
759 info->category = g_strdup ("XLL functions");
761 if (count>9 && (opers[9]->xltype & xltypeType)==xltypeStr) {
762 function_description = c_string_from_pascal_string (opers[9]->val.str);
764 m=0;
765 n=gnm_func_help_entries (number_of_arguments);
766 help=ALLOC_ARRAY (GnmFuncHelp,n);
767 g_assert (m<n);
768 help[m].type=GNM_FUNC_HELP_NAME;
769 help[m].text=g_strdup (info->gnm_func_descriptor.name);
770 ++m;
771 g_assert (m<n);
772 help[m].type=GNM_FUNC_HELP_DESCRIPTION;
773 help[m].text=function_description; /* Memory ownership handed over. */
774 ++m;
775 g_assert (m<n);
776 help[m].type=GNM_FUNC_HELP_EXCEL;
777 help[m].text=g_strdup ("This function has been loaded from an Excel-compatible plugin (XLL). It is NOT a built-in function of Excel or Gnumeric.\n");
778 /* We limit the number of argument strings we copy to the minimum of the number of arguments indicated
779 in the types string and the number of arguments strings given. We always provide enough space for as
780 many as indicated in the types string. */
781 for (i = 10, j = 0;
782 i < count && i - 10 < (int)number_of_arguments;
783 ++i) {
784 ++m;
785 help[m].type=GNM_FUNC_HELP_ARG;
786 if ((opers[i]->xltype & xltypeType) == xltypeStr) {
787 gchar * arg_name = (NULL != arg_names && NULL != arg_names[j]) ? arg_names[j++] : NULL;
788 gchar * arg_help = c_string_from_pascal_string (opers[i]->val.str);
789 g_assert (m < n);
790 if (NULL != arg_name) {
791 gchar *tmp=arg_help;
792 arg_help = g_strconcat (arg_name,":",arg_help,NULL);
793 delete_string (&tmp);
795 help[m].text = arg_help;
798 ++m;
799 g_assert (m < n);
800 help[m].type=GNM_FUNC_HELP_END;
801 if (NULL != arg_names) {
802 g_strfreev (arg_names);
803 arg_names = NULL;
805 info->gnm_func_descriptor.help = help;
806 info->gnm_func_descriptor.fn_args = genericXLLFunction;
807 info->gnm_func_descriptor.impl_status = GNM_FUNC_IMPL_STATUS_COMPLETE;
808 info->gnm_func_descriptor.test_status = GNM_FUNC_TEST_STATUS_BASIC;
810 success = add_xll_function (exported_function_symbol,info);
812 delete_string (&exported_function_symbol);
814 if (success) {
815 if (NULL != operRes) {
816 operRes->xltype=xltypeNum;
817 operRes->val.num=(unsigned long)info; /* This should be set to the resulting registration id. We use the info pointer as a proxy here. */
819 return xlretSuccess;
821 free_xll_function_info (info);
822 return xlretInvXloper; /* "An invalid XLOPER or XLOPER12 was passed to the function, or an argument of the wrong type was used." */
824 case xlFree:
825 while (count--) {
826 destruct_xloper (opers[count]);
828 return xlretSuccess;
829 case xlGetName: /* The name of the DLL that is calling */
830 if (NULL != operRes) {
831 operRes->xltype=xltypeStr;
832 operRes->val.str=pascal_string_from_c_string (currently_called_xll->name);
833 return xlretSuccess;
835 return xlretInvXloper;
836 case xlfRow: /* Pass in the output of xlfCaller to get the row of the calling worksheet cell as an XLOPER of
837 type integer or number. Note that the output may be an array similar to the output from
838 xlSheetNm. */
839 if (NULL != operRes) {
840 if ( count>0 && xltypeRef==(opers[0]->xltype & xltypeType) && NULL != operRes->val.mref.lpmref) {
841 operRes->xltype = xltypeInt;
842 operRes->val.w = operRes->val.mref.lpmref->reftbl[1].rwFirst;
843 return xlretSuccess;
845 if (NULL != current_eval_info) {
846 operRes->xltype = xltypeInt;
847 operRes->val.w = (short int) current_eval_info->pos->eval.row;
848 return xlretSuccess;
851 return xlretInvXloper;
852 case xlfColumn: /* Pass in the output of xlfCaller to get the column of the calling worksheet cell as an XLOPER of
853 type integer or number. Note that the output may be an array similar to the output from
854 xlSheetNm. */
855 if (NULL != operRes) {
856 if ( count>0 && xltypeRef==(opers[0]->xltype & xltypeType) && NULL != operRes->val.mref.lpmref) {
857 operRes->xltype = xltypeInt;
858 operRes->val.w = operRes->val.mref.lpmref->reftbl[1].colFirst;
859 return xlretSuccess;
861 if (NULL != current_eval_info) {
862 operRes->xltype = xltypeInt;
863 operRes->val.w = (short int) current_eval_info->pos->eval.col;
864 return xlretSuccess;
867 return xlretInvXloper;
870 /* Commonly called functions. None of these are fully implemented. */
871 case xlfCaller: /* Information about the location that is calling a worksheet function. The result of this can
872 be scalar XLOPER or a range, depending on whether a function is called as an array function,
873 or as a single cell function call. The result should be passed back in when calling
874 xlSheetNm, xlfRow, or xlfColumn. */
875 if (NULL != operRes && NULL != current_eval_info) {
876 operRes->xltype = xltypeRef;
877 operRes->val.mref.idSheet = current_eval_info->pos->sheet->index_in_wb; /* This is not globally unique but better than nothing. */
878 operRes->val.mref.lpmref = ALLOC_ARRAY (XLMREF,1);
879 operRes->val.mref.lpmref->count=1;
880 operRes->val.mref.lpmref->reftbl[1].rwFirst = current_eval_info->pos->eval.row;
881 operRes->val.mref.lpmref->reftbl[1].rwLast = current_eval_info->pos->eval.row;
882 operRes->val.mref.lpmref->reftbl[1].colFirst = current_eval_info->pos->eval.col;
883 operRes->val.mref.lpmref->reftbl[1].colLast = current_eval_info->pos->eval.col;
884 return xlretSuccess;
886 return xlretInvXloper;
887 case xlSheetNm:
888 /* Pass in the output of xlfCaller to get the name of
889 * the calling worksheet as an XLOPER of type
890 * string. Note that the output of this can be an
891 * array if the output of xlfCaller was an array. To
892 * play safe, always check if it is an array, and use
893 * element (0,0) if it is, else use the scalar. */
894 if (NULL != operRes && NULL != current_eval_info) {
895 Sheet *sheet = current_eval_info->pos->sheet;
896 int index_in_wb = sheet->index_in_wb;
897 int row = current_eval_info->pos->eval.row;
898 int col = current_eval_info->pos->eval.col;
899 Workbook *workbook = sheet->workbook;
900 if (count > 0 &&
901 xltypeRef == (opers[0]->xltype & xltypeType)) {
902 index_in_wb = opers[0]->val.mref.idSheet;
903 if (NULL != operRes->val.mref.lpmref) {
904 row = operRes->val.mref.lpmref->reftbl[1].rwFirst;
905 col = operRes->val.mref.lpmref->reftbl[1].colFirst;
907 sheet=workbook_sheet_by_index (workbook, index_in_wb);
909 operRes->xltype = xltypeStr;
910 operRes->val.str = pascal_string_from_c_string (sheet->name_unquoted);
911 return xlretSuccess;
913 return xlretInvXloper;
914 case xlGetHwnd: /* The WIN32 window handle of the "Excel" window */
915 case xlAbort: /* Query if the user hammered escape. May take one argument if the calling XLL wants to tell us that we are to continue.*/
916 if (NULL != operRes) {
917 operRes->xltype=xltypeBool;
918 operRes->val.boolean=0;
920 return xlretSuccess;
921 case xlcMessage: /* Set message bar. Expects one argument but no return value. */
922 return xlretSuccess;
923 default:;
925 return xlretInvXlfn; /* "An invalid function number was supplied. If you are using constants from XLCALL.H, this
926 should not occur unless you are calling something that is not supported in the version
927 of Excel you are running." */
930 static void
931 load_xlcall32 (GOPlugin *plugin)
933 gchar *full_module_file_name;
934 if (!g_module_supported ()) {
935 g_warning (_("Dynamic module loading is not supported on this system."));
936 return;
938 full_module_file_name = g_build_filename (go_plugin_get_dir_name (plugin), "xlcall32", NULL);
939 #ifdef WIN32
940 SetErrorMode (SEM_FAILCRITICALERRORS); /* avoid message box if library not found */
941 #endif
942 xlcall32_handle = g_module_open (full_module_file_name, G_MODULE_BIND_LAZY);
943 #ifdef WIN32
944 SetErrorMode (0);
945 #endif
946 if (xlcall32_handle == NULL) {
947 g_warning (_("Unable to open module file \"%s\"."),full_module_file_name);
948 return;
950 g_module_symbol (xlcall32_handle, "register_actual_excel4v", (gpointer) &register_actual_excel4v);
951 if (register_actual_excel4v == NULL) {
952 g_warning (_("Module \"%s\" doesn't contain (\"register_actual_excel4v\" symbol)."),full_module_file_name);
953 return;
955 register_actual_excel4v (actual_Excel4v);
956 g_free (full_module_file_name);
959 static void
960 scan_for_XLLs_and_register_functions (const gchar *dir_name)
962 GDir *dir = g_dir_open (dir_name, 0, NULL);
963 gchar *full_entry_name;
964 const gchar *d_name;
965 struct stat d_info;
966 GModule *handle;
967 int stat_success;
968 if (NULL != dir) {
969 while ((d_name = g_dir_read_name (dir)) != NULL) {
970 if ( ! (strcmp (d_name, ".") == 0 || strcmp (d_name, "..") == 0)) {
971 full_entry_name = g_build_filename (dir_name, d_name, NULL);
972 stat_success = g_stat (full_entry_name,&d_info);
973 if (0 == stat_success) {
974 if (S_ISDIR (d_info.st_mode)) {
975 scan_for_XLLs_and_register_functions (full_entry_name);
976 } else {
977 #ifdef WIN32
978 SetErrorMode (SEM_FAILCRITICALERRORS); /* avoid message box if library not found */
979 #endif
980 handle = g_module_open (full_entry_name, G_MODULE_BIND_LAZY);
981 #ifdef WIN32
982 SetErrorMode (0);
983 #endif
984 if (NULL != handle) {
985 XLL*xll = ALLOC_ARRAY (XLL,1);
986 XLAutoOpenFunc xlAutoOpenFunc = NULL;
987 xll->name = g_strdup (full_entry_name);
988 xll->handle = handle;
989 g_module_symbol (xll->handle, "xlAutoFree", (gpointer) &xll->xlAutoFree);
990 xlAutoOpenFunc = NULL;
991 if (g_module_symbol (xll->handle, "xlAutoOpen", (gpointer) &xlAutoOpenFunc) && xlAutoOpenFunc) {
992 currently_called_xll = xll;
993 xlAutoOpenFunc ();
994 currently_called_xll = NULL;
995 if (0 == xll->number_of_functions) {
996 g_warning (_("No loadable worksheet functions found in XLL/DLL/SO file %s."),full_entry_name);
997 } else {
998 GO_SLIST_PREPEND (XLLs,xll);
999 /* xgettext : %lu gives the number of functions. This is input to ngettext. */
1000 g_message (ngettext("Loaded %lu function from XLL/DLL/SO %s.",
1001 "Loaded %lu functions from XLL/DLL/SO %s.",
1002 xll->number_of_functions),
1003 xll->number_of_functions, full_entry_name);
1006 if (0 == xll->number_of_functions) {
1007 free_XLL (xll);
1012 g_free (full_entry_name);
1015 g_dir_close (dir);
1019 G_MODULE_EXPORT void
1020 go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
1022 load_xlcall32 (plugin);
1024 if (NULL == xlcall32_handle) /* If we couldn't load the helper dll, there is no point in continuing. */
1025 return;
1028 * Find all external XLL functions that are to be adapted into
1029 * Gnumeric. We scan for all shared libraries that expose
1030 * xlAutoOpen, in this directory, and all sub
1031 * directories. Find them, load them, find xlAutoFree if
1032 * present, call xlAutoOpen, and record via the exposed
1033 * actual_Excel4v function what worksheet functions they want
1034 * to register with Excel.
1036 * We search recursively the directory in which this plugin
1037 * resides and all of its subdirectories. This is not for any
1038 * philosophical reasons and can be changed as desired.
1041 scan_for_XLLs_and_register_functions (go_plugin_get_dir_name (plugin));
1044 G_MODULE_EXPORT void
1045 go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
1048 * To be formally correct, we should unregister all of the
1049 * loaded XLL functions. To write this code is a lot of
1050 * effort, and in all likelihood, it is never
1051 * needed. Postponed until it becomes really necessary. In
1052 * practice, XLL writers usually don't call the xlfUnregister
1053 * procedure in Excel for each of the registered functions
1054 * either.
1057 if (NULL != xll_function_info_map) {
1058 g_tree_destroy (xll_function_info_map);
1059 xll_function_info_map = NULL;
1062 g_slist_free_full (XLLs, free_XLL);
1063 XLLs = NULL;
1065 if (register_actual_excel4v)
1066 register_actual_excel4v (NULL);
1067 register_actual_excel4v = NULL;
1069 if (NULL != xlcall32_handle)
1070 g_module_close (xlcall32_handle);
1071 xlcall32_handle = NULL;
1074 /***************************************************************************/