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'.
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>
35 #include <gnm-plugin.h>
36 #include <goffice/utils/go-glib-extras.h>
38 #include <glib/gstdio.h>
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
84 #if defined( WIN32 ) || defined( WIN64 )
87 #include "win32replacements.h"
92 /* All bits that are mutually exclusive in the type field of an xloper. */
93 #define xltypeType 0x0FFF
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);
108 GnmFuncDescriptor gnm_func_descriptor
;
109 guint number_of_arguments
;
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
145 gnm_value_type_name (const GnmValue
*g
)
148 switch (g
->v_any
.type
) {
154 CASE(VALUE_CELLRANGE
);
156 default: return "<unknown>";
164 xloper_type_name (const XLOPER
*x
)
167 switch (x
->xltype
& xltypeType
) {
179 default: return "<unknown>";
186 unsupported_xloper_type (const XLOPER
*x
)
188 g_warning ("Unsupported xloper type \"%s\"",
189 xloper_type_name (x
));
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
,
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
;
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 * ------------------------+------------------------------+------------------------------------------------------------+
240 pascal_string_from_c_string (const char *s
)
244 guint l
= strlen (s
);
245 g_return_val_if_fail (l
<(UINT_MAX
-2U),NULL
);
247 g_strlcpy (o
+1,s
,l
+1);
250 o
[0] = (unsigned char)l
;
256 c_string_from_pascal_string (const char *s
)
259 const guint m
= ((unsigned char)s
[0])+1U;
260 char * o
= g_malloc (m
);
268 copy_construct_xloper_from_gnm_value (XLOPER
*out
, const GnmValue
*in
,
271 g_return_if_fail (NULL
!= out
);
273 out
->xltype
= xltypeMissing
;
277 switch (in
->v_any
.type
) {
279 out
->xltype
= xltypeNil
;
282 out
->xltype
= xltypeBool
;
283 out
->val
.boolean
= (WORD
)value_get_as_checked_bool (in
);
286 out
->xltype
= xltypeNum
;
287 out
->val
.num
= in
->v_float
.val
;
290 out
->xltype
= xltypeErr
;
291 out
->val
.err
= xloper_error_code_from_gnm_value (in
);
294 out
->xltype
= xltypeStr
;
295 out
->val
.str
= pascal_string_from_c_string (value_peek_string (in
));
297 case VALUE_CELLRANGE
: {
299 GnmRangeRef
const *rr
= value_get_rangeref (in
);
300 Sheet
*end_sheet
= NULL
;
301 GnmValue
*cell_value
;
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."));
308 int m
= sr
.range
.end
.col
-sr
.range
.start
.col
+1;
309 int n
= sr
.range
.end
.row
-sr
.range
.start
.row
+1;
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
);
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
);
331 int m
= in
->v_array
.x
;
332 int n
= in
->v_array
.y
;
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
);
347 g_warning (_("Unsupported GnmValue type (%d)"),in
->v_any
.type
);
353 delete_string (gchar
**s
)
362 destruct_xloper (XLOPER
*x
)
365 switch (x
->xltype
& xltypeType
) {
369 delete_string (&x
->val
.str
);
374 if (NULL
!= x
->val
.mref
.lpmref
&&
375 x
->val
.mref
.lpmref
->count
!= 1) {
376 unsupported_xloper_type (x
);
378 if (NULL
!= x
->val
.mref
.lpmref
)
379 FREE_ARRAY (x
->val
.mref
.lpmref
, 1);
380 x
->val
.mref
.lpmref
= NULL
;
386 unsupported_xloper_type (x
);
389 int n
= x
->val
.array
.rows
*x
->val
.array
.columns
;
391 for (i
= 0; i
< n
; ++i
) {
392 destruct_xloper (x
->val
.array
.lparray
+i
);
394 FREE_ARRAY (x
->val
.array
.lparray
,n
);
402 unsupported_xloper_type (x
);
407 unsupported_xloper_type (x
);
409 x
->xltype
= xltypeNil
;
414 new_gnm_value_from_xloper (const XLOPER
*x
)
418 switch (x
->xltype
& xltypeType
) {
420 g
= value_new_float (x
->val
.num
);
424 const char *s
= x
->val
.str
;
426 guint m
= ((unsigned char)s
[0]) + 1U;
428 g_strlcpy (o
, s
+ 1, m
);
430 g
= value_new_string_nocopy (o
);
434 g
= value_new_bool (x
->val
.boolean
);
437 unsupported_xloper_type (x
);
440 g
= value_new_error_std (NULL
, gnm_value_error_from_xloper (x
));
443 unsupported_xloper_type (x
);
446 guint m
= x
->val
.array
.columns
;
447 guint n
= x
->val
.array
.rows
;
448 if (m
> 0 && n
> 0) {
450 g
= value_new_array_empty (m
,n
);
451 for (i
= 0; i
< m
; ++i
) {
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
);
459 g
= value_new_error_std (NULL
, GNM_ERROR_VALUE
);
466 g
= value_new_empty ();
469 unsupported_xloper_type (x
);
472 g
= value_new_int (x
->val
.w
);
475 unsupported_xloper_type (x
);
478 g
= value_new_error_std (NULL
, GNM_ERROR_NUM
);
484 genericXLLFunction (GnmFuncEvalInfo
*ei
, GnmValue
const * const *argv
)
486 XLOPER x
[MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS
], *r
= NULL
;
487 XLLFunctionWithVarArgs func
= NULL
;
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
);
501 m
= info
->number_of_arguments
;
502 m
= MAX (m
, MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS
);
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
;
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
);
562 /***************************************************************************/
565 gnm_func_help_entries (int number_of_arguments
)
567 /* NAME,DESCRIPTION,EXCEL,ARG1,...ARGn,END */
568 return number_of_arguments
+ 4;
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
;
598 typedef int WINAPI (*XLAutoOpenFunc
)(void);
599 typedef int WINAPI (*XLAutoCloseFunc
)(void);
602 free_XLL (gpointer 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
;
611 currently_called_xll
= NULL
;
613 if (!g_module_close (xll
->handle
))
614 g_warning (_("%s: %s"), xll
->name
, g_module_error ());
617 delete_string (&xll
->name
);
622 g_strcmp0_with_ignored_data (gconstpointer str1
, gconstpointer str2
,
625 return g_strcmp0 (str1
, str2
);
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
);
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
;
649 g_warning (_("Failed to find function \"%s\" in XLL/DLL/SO %s .\n"),info
->gnm_func_descriptor
.name
,info
->xll
->name
);
655 actual_Excel4v (int xlfn
, XLOPER
* operRes
, int count
, XLOPER
** opers
)
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
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
;
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.
677 * opers[ 1] : exported function symbol in XLL/DLL (string). To be located with g_module_symbol ().
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).
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.
685 * opers[ 5] : function type as xltypeNum. 0.=hidden, 1.=function, 2.=command.
687 * opers[ 6] : function category as string.
688 DEFAULTS to "XLL functions".
689 * opers[ 7] : short cut text as string.
691 * opers[ 8] : help topic as string.
693 * opers[ 9] : function description as string.
695 * opers[10 .. 10+n-1] : Help on the n arguments of actual function that is being registered.
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." */
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]) {
718 g_string_append_c (argument_specifications
,'?');
719 ++number_of_arguments
;
721 case '\r': /* Various junk we may as well ignore. */
727 case '#': /* This signals a request for this function or macro to have access to macro specific call back functions. Ignored. */
730 flags
|= GNM_FUNC_VOLATILE
;
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
);
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
);
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
);
765 n
=gnm_func_help_entries (number_of_arguments
);
766 help
=ALLOC_ARRAY (GnmFuncHelp
,n
);
768 help
[m
].type
=GNM_FUNC_HELP_NAME
;
769 help
[m
].text
=g_strdup (info
->gnm_func_descriptor
.name
);
772 help
[m
].type
=GNM_FUNC_HELP_DESCRIPTION
;
773 help
[m
].text
=function_description
; /* Memory ownership handed over. */
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. */
782 i
< count
&& i
- 10 < (int)number_of_arguments
;
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
);
790 if (NULL
!= arg_name
) {
792 arg_help
= g_strconcat (arg_name
,":",arg_help
,NULL
);
793 delete_string (&tmp
);
795 help
[m
].text
= arg_help
;
800 help
[m
].type
=GNM_FUNC_HELP_END
;
801 if (NULL
!= arg_names
) {
802 g_strfreev (arg_names
);
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
);
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. */
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." */
826 destruct_xloper (opers
[count
]);
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
);
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
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
;
845 if (NULL
!= current_eval_info
) {
846 operRes
->xltype
= xltypeInt
;
847 operRes
->val
.w
= (short int) current_eval_info
->pos
->eval
.row
;
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
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
;
861 if (NULL
!= current_eval_info
) {
862 operRes
->xltype
= xltypeInt
;
863 operRes
->val
.w
= (short int) current_eval_info
->pos
->eval
.col
;
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
;
886 return xlretInvXloper
;
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
;
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
);
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;
921 case xlcMessage
: /* Set message bar. Expects one argument but no return value. */
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." */
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."));
938 full_module_file_name
= g_build_filename (go_plugin_get_dir_name (plugin
), "xlcall32", NULL
);
940 SetErrorMode (SEM_FAILCRITICALERRORS
); /* avoid message box if library not found */
942 xlcall32_handle
= g_module_open (full_module_file_name
, G_MODULE_BIND_LAZY
);
946 if (xlcall32_handle
== NULL
) {
947 g_warning (_("Unable to open module file \"%s\"."),full_module_file_name
);
950 g_module_symbol (xlcall32_handle
, "register_actual_excel4v", (gpointer
) ®ister_actual_excel4v
);
951 if (register_actual_excel4v
== NULL
) {
952 g_warning (_("Module \"%s\" doesn't contain (\"register_actual_excel4v\" symbol)."),full_module_file_name
);
955 register_actual_excel4v (actual_Excel4v
);
956 g_free (full_module_file_name
);
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
;
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
);
978 SetErrorMode (SEM_FAILCRITICALERRORS
); /* avoid message box if library not found */
980 handle
= g_module_open (full_entry_name
, G_MODULE_BIND_LAZY
);
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
;
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
);
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
) {
1012 g_free (full_entry_name
);
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. */
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
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
);
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 /***************************************************************************/