diff --git a/gnumeric/configure.in b/gnumeric/configure.in
index 0b10cef..94e3242 100644
--- a/gnumeric/configure.in
+++ b/gnumeric/configure.in
@@ -1064,10 +1064,11 @@ plugins/fn-random/Makefile
 plugins/fn-tsa/Makefile
 plugins/applix/Makefile
 plugins/corba/Makefile
 plugins/dif/Makefile
 plugins/excel/Makefile
+plugins/excelplugins/Makefile
 plugins/gda/Makefile
 plugins/gnome-db/Makefile
 plugins/gnome-glossary/Makefile
 plugins/html/Makefile
 plugins/lotus-123/Makefile
diff --git a/gnumeric/doc/developer/Design b/gnumeric/doc/developer/Design
index 66a57ec..d3ac9c1 100644
--- a/gnumeric/doc/developer/Design
+++ b/gnumeric/doc/developer/Design
@@ -274,11 +274,11 @@
 	<title>Plugins</title>
 
 	<para>
 	  Gnumeric's plugin API is very simple. Every plugin must have
 	  one function: init_plugin see
-	  <filename>gnumeric/src/plugin.h</filename> for the full details.
+	  <filename>gnumeric/src/gnm-plugin.h</filename> for the full details.
 	</para>
  
       </sect1>
   </chapter>
 </book>
diff --git a/gnumeric/plugins/Makefile.am b/gnumeric/plugins/Makefile.am
index 8f0dea1..cb20731 100644
--- a/gnumeric/plugins/Makefile.am
+++ b/gnumeric/plugins/Makefile.am
@@ -21,11 +21,11 @@ if WITH_GDA
 endif
 if WITH_GNOMEDB
   SUBDIRS_FUNCTIONS += gnome-db
 endif
 
-SUBDIRS_LOADERS =
+SUBDIRS_LOADERS = excelplugins
 SUBDIRS_EXAMPLES = uihello
 
 # Do not build by default until the libtool problems with
 # a static libpython are resolved.
 if WITH_PYTHON
diff --git a/gnumeric/plugins/excelplugins/.gitignore b/gnumeric/plugins/excelplugins/.gitignore
new file mode 100644
index 0000000..e64b392
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/.gitignore
@@ -0,0 +1,9 @@
+Makefile.in
+Makefile
+.deps
+.libs
+*.lo
+*.la
+plugin.xml
+*.loT
+*.sw*
diff --git a/gnumeric/plugins/excelplugins/ExcelTestModule.c b/gnumeric/plugins/excelplugins/ExcelTestModule.c
new file mode 100644
index 0000000..55e1d14
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/ExcelTestModule.c
@@ -0,0 +1,293 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * ExcelTestModule.c: Standalone XLL code for test purposes.
+ *
+ * Author:
+ *   Peter Jaeckel (peter.jaeckel@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gmodule.h>
+
+#if defined( WIN32 ) || defined( WIN64 )
+#include <windef.h>
+#else
+#include "win32replacements.h"
+#endif
+
+#include "xlcall.h"
+
+/* All bits that are mutually exclusive in the type field of an xloper. */
+#define xltypeType       0x0FFF
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_G_SLICE_ALLOC
+#define g_slice_new_array0(T,c) ((T*) g_slice_alloc0 ((sizeof (T))*(c)))
+#define ALLOC_ARRAY(T,c) (g_slice_new_array0 (T,c))
+#define FREE_ARRAY(p,c) (g_slice_free1 (sizeof(*p)*(c),(p)))
+#else
+#define ALLOC_ARRAY(T,c) (g_new0 (T,c))
+#define FREE_ARRAY(p,c)  (g_free ((p)))
+#endif
+
+static char * pascal_string_from_c_string(const char *s){
+	char *o=NULL;
+	if (NULL!=s){
+		guint l = strlen(s);
+		g_assert(l<(UINT_MAX-2U));
+		o = g_malloc(l+2U);
+		g_strlcpy(o+1,s,l+1);
+		if (l>UCHAR_MAX)
+			l=UCHAR_MAX;
+		o[0]=(unsigned char)l;
+	}
+	return o;
+}
+
+;
+
+static char * duplicate_pascal_string(const char *s){
+	return NULL==s?NULL:g_memdup(s,s[0]+1);
+}
+
+#define CASE( x ) case (x): return #x
+
+static const char * xloper_type_name(const XLOPER*x){
+	if (NULL!=x){
+		switch(x->xltype & xltypeType){
+		CASE(xltypeNum);
+		CASE(xltypeStr);
+		CASE(xltypeBool);
+		CASE(xltypeRef);
+		CASE(xltypeErr);
+		CASE(xltypeFlow);
+		CASE(xltypeMulti);
+		CASE(xltypeMissing);
+		CASE(xltypeNil);
+		CASE(xltypeSRef);
+		CASE(xltypeInt);
+		default: return "<unknown>";
+		}
+	}
+	return "(nil)";
+}
+
+static void unsupported_xloper_type(const XLOPER*x){
+	g_warning(("Unsupported xloper type \"%s\""),xloper_type_name(x));
+}
+
+static void destruct_xloper(XLOPER*x){
+	int i,n;
+	if (NULL!=x){
+		switch(x->xltype & xltypeType){
+		case xltypeNum:							break;
+		case xltypeStr:		g_free(x->val.str); x->val.str=0;	break;
+		case xltypeBool:						break;
+		case xltypeRef:		unsupported_xloper_type(x);		break;
+		case xltypeErr:							break;
+		case xltypeFlow:	unsupported_xloper_type(x);		break;
+		case xltypeMulti:
+			n=x->val.array.rows*x->val.array.columns;
+			for (i=0;i<n;++i){
+				destruct_xloper(x->val.array.lparray+i);
+			}
+			FREE_ARRAY(x->val.array.lparray,n);
+			break;
+		case xltypeMissing:						break;
+		case xltypeNil:							break;
+		case xltypeSRef:	unsupported_xloper_type(x);		break;
+		case xltypeInt:							break;
+		default:		unsupported_xloper_type(x);
+		}
+	}
+	x->xltype=xltypeNil;
+}
+
+static void copy_construct_xloper(XLOPER*x,const XLOPER*y){
+	int i,n;
+	if (NULL!=x){
+		x->xltype=xltypeMissing;
+		if(NULL!=y){
+			g_memmove(x,y,sizeof(XLOPER));
+			switch(y->xltype & xltypeType){
+			case xltypeStr:
+				x->val.str=duplicate_pascal_string(y->val.str);
+				break;
+			case xltypeMulti:
+				n=y->val.array.rows*y->val.array.columns;
+				x->val.array.lparray=ALLOC_ARRAY(XLOPER,n);
+				for (i=0;i<n;++i){
+					copy_construct_xloper(x->val.array.lparray+i,y->val.array.lparray+i);
+				}
+				break;
+			default:;
+			}
+			x->xltype = (y->xltype & xltypeType);
+		}
+
+	}
+}
+
+G_MODULE_EXPORT BOOL APIENTRY DllMain( HANDLE hDLL, DWORD dwReason, LPVOID lpReserved ) {
+  return TRUE;
+}
+
+G_MODULE_EXPORT const XLOPER* hello(){
+	XLOPER*r=ALLOC_ARRAY(XLOPER,1);
+	r->xltype=xltypeStr|xlbitDLLFree;
+	r->val.str=pascal_string_from_c_string("Hello!");
+	return r;
+}
+
+G_MODULE_EXPORT const XLOPER* helloWorld(){
+	XLOPER*r=ALLOC_ARRAY(XLOPER,1);
+	r->xltype=xltypeStr|xlbitDLLFree;
+	r->val.str=pascal_string_from_c_string("Hello World!");
+	return r;
+}
+
+G_MODULE_EXPORT const XLOPER* convertAstronomicalUnitsToKilometers(const XLOPER*x){
+	XLOPER*r=NULL;
+	if ( NULL!=x && (x->xltype&xltypeType)==xltypeNum ) {
+		r=ALLOC_ARRAY(XLOPER,1);
+		r->xltype=xltypeNum|xlbitDLLFree;
+		r->val.num=x->val.num*149600000.;
+	}
+	return r;
+}
+
+G_MODULE_EXPORT const XLOPER* multiplyTwoNumbers(const XLOPER*x,const XLOPER*y){
+	XLOPER*r=NULL;
+	if ( NULL!=x && (x->xltype&xltypeType)==xltypeNum && NULL!=y && (y->xltype&xltypeType)==xltypeNum ) {
+		r=ALLOC_ARRAY(XLOPER,1);
+		r->xltype=xltypeNum|xlbitDLLFree;
+		r->val.num=x->val.num*y->val.num;
+	}
+	return r;
+}
+
+G_MODULE_EXPORT const XLOPER* arrangeInSquareMatrix(const XLOPER*a,const XLOPER*b,const XLOPER*c,const XLOPER*d){
+	XLOPER*r=NULL;
+	if ( NULL!=a && NULL!=b && NULL!=c && NULL!=d ) {
+		r=ALLOC_ARRAY(XLOPER,1);
+		r->xltype=xltypeMulti|xlbitDLLFree;
+		r->val.array.rows=2;
+		r->val.array.columns=2;
+		r->val.array.lparray=ALLOC_ARRAY(XLOPER,4);
+		copy_construct_xloper(r->val.array.lparray  ,a);
+		copy_construct_xloper(r->val.array.lparray+1,b);
+		copy_construct_xloper(r->val.array.lparray+2,c);
+		copy_construct_xloper(r->val.array.lparray+3,d);
+
+	}
+	return r;
+}
+
+G_MODULE_EXPORT const XLOPER* sumAndProduct(const XLOPER*m){
+	XLOPER*r=NULL;
+	if ( NULL!=m ) {
+		r=ALLOC_ARRAY(XLOPER,1);
+		r->xltype=xltypeMulti|xlbitDLLFree;
+		r->val.array.rows=1;
+		r->val.array.columns=2;
+		r->val.array.lparray=ALLOC_ARRAY(XLOPER,2);
+		r->val.array.lparray[0].xltype=xltypeNum;
+		r->val.array.lparray[0].val.num = 0;
+		r->val.array.lparray[1].xltype=xltypeNum;
+		r->val.array.lparray[1].val.num = 1;
+		if ((m->xltype&xltypeType)==xltypeMulti){
+			int i,j;
+			for (i=0;i<m->val.array.columns;++i){
+				for (j=0;j<m->val.array.rows;++j){
+					const double x = m->val.array.lparray[i*m->val.array.rows+j].val.num;
+					r->val.array.lparray[0].val.num += x;
+					r->val.array.lparray[1].val.num *= x;
+				}
+			}
+		}
+
+	}
+	return r;
+}
+
+typedef const char * Excel4RegistrationInfo[30];
+
+static Excel4RegistrationInfo registration_info[] = {
+  {
+    "helloWorld", "P", "helloWorld", "", "1", "Friendly", "", "", "helloWorld() returns \"Hello World!\".",
+    NULL
+  },
+  {
+    "hello", "P", "hello",      "", "1", "Friendly", "", "", "hello() returns \"Hello\".",
+    NULL
+  },
+  {
+    "convertAstronomicalUnitsToKilometers", "PP", "convertAstronomicalUnitsToKilometers", "au", "1", "Astronomical", "", "", "convertAstronomicalUnitsToKilometers(au) returns au * 149600000.",
+    "astronomical units", NULL
+  },
+  {
+    "multiplyTwoNumbers", "PPP", "multiplyTwoNumbers", "x,y", "1", "Mathematics", "", "", "multiplyTwoNumbers(X,Y) returns X*Y.",
+    "first multiplicand", "second multiplicand", NULL
+  },
+  {
+    "arrangeInSquareMatrix", "PPPPP", "arrangeInSquareMatrix", "a,b,c,d", "1", "Mathematics", "", "", "arrangeInSquareMatrix(a,b,c,d) returns a square matrix {{a,b},{c,d}}.",
+    "top left entry", "top right entry", "bottom left entry", "bottom right entry", NULL
+  },
+  {
+    "sumAndProduct", "PP", "sumAndProduct", "M", "1", "Mathematics", "", "", "sumAndProduct(M) returns the sum and the product of the matrix M.",
+    "a matrix", NULL
+  }
+};
+
+static void registerAllFunctions(void){
+  XLOPER xlopers[30];
+  XLOPER *excel4vArgs[30];
+  XLOPER xlRet;
+  unsigned long i,j;
+  const unsigned long n = sizeof(registration_info)/sizeof(Excel4RegistrationInfo);
+  for (i=0;i<30;++i){
+    excel4vArgs[i]=xlopers+i;
+  }
+  Excel4(xlGetName, excel4vArgs[0], 0);
+  for (i=0;i<n;++i){
+    for (j=0;j<29&&NULL!=registration_info[i][j];++j){
+      excel4vArgs[1+j]->xltype=xltypeStr;
+      excel4vArgs[1+j]->val.str=pascal_string_from_c_string(registration_info[i][j]);
+    }
+    Excel4v(xlfRegister, &xlRet, j+1, excel4vArgs);
+    for (j=0;j<29&&NULL!=registration_info[i][j];++j){
+      g_free(excel4vArgs[1+j]->val.str);
+    }
+  }
+}
+
+G_MODULE_EXPORT int xlAutoOpen() {
+  registerAllFunctions();
+  return 1;
+}
+
+G_MODULE_EXPORT int xlAutoClose() {
+  return 1;
+}
+
+G_MODULE_EXPORT void xlAutoFree(XLOPER*p) {
+  destruct_xloper(p);
+  FREE_ARRAY(p,1);
+  return;
+}
diff --git a/gnumeric/plugins/excelplugins/Makefile.am b/gnumeric/plugins/excelplugins/Makefile.am
new file mode 100644
index 0000000..d300b32
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/Makefile.am
@@ -0,0 +1,20 @@
+AM_CPPFLAGS = \
+    -DGNOMELOCALEDIR=\""$(datadir)/locale"\" 	\
+    -I$(top_srcdir)/src	-I$(top_builddir)/src	\
+    $(GNUMERIC_CFLAGS)
+
+gnumeric_plugin_excelpluginsdir = $(gnumeric_plugindir)/excelplugins
+xmldir = $(gnumeric_plugin_excelpluginsdir)
+gnumeric_plugin_excelplugins_LTLIBRARIES = plugin.la xlcall32.la
+plugin_la_LDFLAGS = -module $(GNUMERIC_PLUGIN_LDFLAGS)
+plugin_la_SOURCES = excelplugins.c
+xlcall32_la_LDFLAGS = -module $(GNUMERIC_PLUGIN_LDFLAGS)
+xlcall32_la_SOURCES = xlcall32_emulation.c
+
+xml_in_files = plugin.xml.in
+xml_DATA = $(xml_in_files:.xml.in=.xml)
+
+@INTLTOOL_XML_RULE@
+
+EXTRA_DIST = $(xml_in_files)
+DISTCLEANFILES = $(xml_DATA)
diff --git a/gnumeric/plugins/excelplugins/Makefile_ExcelTestModule.g++ b/gnumeric/plugins/excelplugins/Makefile_ExcelTestModule.g++
new file mode 100644
index 0000000..43d00c2
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/Makefile_ExcelTestModule.g++
@@ -0,0 +1,15 @@
+CC       = gcc
+CXX      = g++
+CFLAGS   = -Wall -fPIC -I. -O3 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
+CXXFLAGS = $(CFLAGS)
+LD      = $(CC)
+LDFLAGS =
+LIBS    = ./.libs/xlcall32.so
+TARGET  = ExcelTestModule.so
+Default: $(TARGET)
+.SUFFIXES : .o .c .cpp .l
+.c.o:;		$(CC) $(CFLAGS) -c $<
+.cpp.o:;	$(CXX) $(CXXFLAGS) -c $<
+OBJS    = ExcelTestModule.o
+$(OBJS):  $(lastword $(MAKEFILE_LIST))
+$(TARGET): $(OBJS) $(LIBS);	$(LD) -shared -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
diff --git a/gnumeric/plugins/excelplugins/Makefile_ExcelTestModule.i586-mingw32msvc-g++ b/gnumeric/plugins/excelplugins/Makefile_ExcelTestModule.i586-mingw32msvc-g++
new file mode 100644
index 0000000..f881b24
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/Makefile_ExcelTestModule.i586-mingw32msvc-g++
@@ -0,0 +1,17 @@
+CC       = i586-mingw32msvc-gcc
+CXX      = i586-mingw32msvc-g++
+PREFIX   = /gnome/win32
+CFLAGS   = -Wall -I. -g -O0 -mms-bitfields -I$(PREFIX)/include/glib-2.0 -I$(PREFIX)/lib/glib-2.0/include
+CXXFLAGS = $(CFLAGS)
+LD      = $(CC)
+LDFLAGS = -L$(PREFIX)/lib -lglib-2.0 -lintl
+# -liconv
+LIBS    = ./.libs/xlcall32.dll
+TARGET  = ExcelTestModule.xll
+Default: $(TARGET)
+.SUFFIXES : .o .c .cpp .l
+.c.o:;		$(CC) $(CFLAGS) -c $<
+.cpp.o:;	$(CXX) $(CXXFLAGS) -c $<
+OBJS    = ExcelTestModule.o
+$(OBJS):  $(lastword $(MAKEFILE_LIST))
+$(TARGET): $(OBJS) $(LIBS);	$(LD) -shared -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
diff --git a/gnumeric/plugins/excelplugins/excelplugins.c b/gnumeric/plugins/excelplugins/excelplugins.c
new file mode 100644
index 0000000..36acb30
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/excelplugins.c
@@ -0,0 +1,1413 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * excelplugins.c:  Adapter interface to load worksheet functions defined in Excel plugins (also known as XLLs).
+ *                  Note that this adapter interface only works for XLL worksheet functions that expect all their
+ *                  arguments to be of type OPER (type 'P' in the Excel SDK documentation) or of type eXtended exceL
+ *                  OPER (type 'R' in the Excel SDK documentation) and that return their results as an OPER (type 'P').
+ *
+ * Author:
+ *   Peter Jaeckel (peter.jaeckel@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <gnumeric-config.h>
+#include <gnumeric.h>
+#include <func.h>
+#include <value.h>
+#include <gnm-i18n.h>
+#include <gnm-plugin.h>
+#include <goffice/utils/go-glib-extras.h>
+#include <stdint.h>
+#include <glib/gstdio.h>
+#include <workbook.h>
+#include <sheet.h>
+#include <cell.h>
+#include <expr-impl.h>
+
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#endif
+
+#if defined( HAVE_G_SLICE_ALLOC )
+#define g_slice_new_array0(T,c) ((T*) g_slice_alloc0 ((sizeof (T))*(c)))
+#define ALLOC_ARRAY(T,c) ( g_slice_new_array0 (T,c) )
+#define FREE_ARRAY(p,c)  ( g_slice_free1 (sizeof(*p)*(c),(p)) )
+#else
+#define ALLOC_ARRAY(T,c) ( g_new0 (T,c) )
+#define FREE_ARRAY(p,c)  ( g_free ((p)) )
+#endif
+
+GNM_PLUGIN_MODULE_HEADER;
+
+/***************************************************************************/
+
+/*
+ *
+ * Standard gnumeric worksheet function plugins are comprised of two parts.
+ *
+ *  1) a file called plugin.xml that contains information about the shared library and the functions to be loaded.
+ *
+ *  2) the shared library file such as plugin.so or plugin.dll.
+ *
+ * The file plugin.xml is autogenerated during the build process from plugin.xml.in.
+ * The file plugin.xml.in typically has the following structure:
+ *
+ *	  <?xml version="1.0" encoding="UTF-8"?>
+ *	  <plugin id="Gnumeric_astronomical">
+ *	  	<information>
+ *	  		<_name>Astronomical plugin</_name>
+ *	  		<_description>A Gnumeric plugin of astronomical signifcance.</_description>
+ *	  	</information>
+ *	  	<loader type="Gnumeric_Builtin:module">
+ *	  		<attribute name="module_file" value="plugin"/>
+ *	  	</loader>
+ *	  	<services>
+ *	  		<service type="function_group" id="astronomical">
+ *	  			<_category>Astronomical Functions</_category>
+ *	  			<functions>
+ *	  				<function name="convertAstronomicalUnitsToKilometers"/>
+ *	  			</functions>
+ *	  		</service>
+ *	  	</services>
+ *	  </plugin>
+ *
+ * The mentioned function corresponds to an implementation such as shown below.
+ */
+
+/* The code below is part of the documentation. It is deliberately inactive but you can try it out or use it as a
+ * template for other plugins.
+ *
+ *  static GnmValue * convertAstronomicalUnitsToKilometers (GnmFuncEvalInfo *ei, GnmValue const * const *argv) {
+ *          gnm_float au = value_get_as_float (argv[0]);
+ *          return value_new_float (au*149600000.);
+ *  }
+ *  static GnmFuncHelp const function_help[] = {
+ *  	{ GNM_FUNC_HELP_OLD,
+ *  	F_("@FUNCTION=convertAstronomicalUnitsToKilometers\n"
+ *  	   "@SYNTAX=convertAstronomicalUnitsToKilometers (au)\n"
+ *  	   "@DESCRIPTION=convertAstronomicalUnitsToKilometers(AU) returns AU*149600000.\n\n"
+ *  	   "* This function is NOT Excel compatible.\n\n"
+ *  	   "@EXAMPLES=\nconvertAstronomicalUnitsToKilometers(1) returns 149600000.")
+ *  	},
+ *  	{ GNM_FUNC_HELP_END }
+ *  };
+ *  GnmFuncDescriptor const astronomical_functions[] = {
+ *  	/ * See gnumeric/trunk/src/func.h for the meaning of the type strings. * /
+ *  	{ "convertAstronomicalUnitsToKilometers", "f", "au", function_help, convertAstronomicalUnitsToKilometers,
+ *  	  NULL, NULL, NULL, NULL, GNM_FUNC_SIMPLE, GNM_FUNC_IMPL_STATUS_COMPLETE, GNM_FUNC_TEST_STATUS_BASIC },
+ *          {NULL}
+ *  };
+ */
+
+/*
+ *
+ * Note, taken from http://www.nabble.com/Plugin-API-td25443811.html :
+ *
+ *  Here's a sample of how things are done now:
+ *
+ *  static GnmFuncHelp const help_lcm[] = {
+ *         { GNM_FUNC_HELP_NAME, F_("LCM:the least common multiple")},
+ *         { GNM_FUNC_HELP_ARG, F_("n0:positive integer")},
+ *         { GNM_FUNC_HELP_ARG, F_("n1:positive integer")},
+ *         { GNM_FUNC_HELP_DESCRIPTION, F_("LCM calculates the least common multiple of the given numbers @{n0},@{n1},..., the smallest integer that is a multiple of each argment.")},
+ *         { GNM_FUNC_HELP_NOTE, F_("If any of the arguments is not an integer, it is truncated.")},
+ *         { GNM_FUNC_HELP_EXCEL, F_("This function is Excel compatible.") },
+ *         { GNM_FUNC_HELP_EXAMPLES, "=LCM(2,13)" },
+ *         { GNM_FUNC_HELP_EXAMPLES, "=LCM(4,7,5)" },
+ *         { GNM_FUNC_HELP_SEEALSO, "GCD"},
+ *         { GNM_FUNC_HELP_END}
+ *  };
+ *
+ */
+
+/*
+ * Note that the identifier id="astronomical" mentioned in the <service> of type="function_group" has to correspond
+ * exactly to the non-static, i.e., externally accessible, array astronomical_functions. Gnumeric relies on that
+ * identifier id="XXXXX" to correspond exactly to such an array of name XXXXX_functions being exposed by the shared
+ * library. Whilst this may be very workable, it is not the common usage of spreadsheet application add-ins with
+ * Excel. There, users are familiar with the fact that one can drop a prebuilt add-in of type XLL into one of the
+ * startup directories Excel scans at load time, such as the XLSTART directory (which is part of Excel's installation
+ * directory), and no other files are needed.
+ *
+ * In order to make a similarly convenient drop-in behaviour available to Gnumeric users, Gnumeric has to provide a
+ * mechanism that allows for the list of functions and their details to be given dynamically at load time, without any
+ * explicit listing of the functions in a secondary file (such as plugin.xml). This generic mechanism is not
+ * implemented. Having said that, part of this development here was to make available an extension to the Gnumeric
+ * plugin loading mechanism that requires only minimal content in plugin.xml, namely the mentioning of a function that
+ * can be called to retrieve information about the function collection provided by this shared library. The syntax is as
+ * follows:
+ *
+ *	<?xml version="1.0" encoding="UTF-8"?>
+ *	<plugin id="Gnumeric_generic">
+ *		<information>
+ *			<name>Astronomical plugin</name>
+ *		</information>
+ *		<loader type="Gnumeric_Builtin:module">
+ *			<attribute name="module_file" value="plugin"/>
+ *		</loader>
+ *		<services>
+ *			<service type="function_group" id="generic">
+ *				<category>gnmAstronomicalFuncCollectionProviderFunc</category>
+ *			</service>
+ *		</services>
+ *	</plugin>
+ *
+ * If no "translated_category" is given, and no functions are specified, but a category is given, then the plugin loader
+ * will attempt to find a function in the shared library whose name is that of the category. In the above example, the
+ * code below suffices.
+ *
+ * The code below is part of the documentation. It is deliberately inactive but you can try it out or use it as a
+ * template for other plugins.
+ *
+ *  const GnmFuncCollection * gnmAstronomicalFuncCollectionProviderFunc(void);
+ *  const GnmFuncCollection * gnmAstronomicalFuncCollectionProviderFunc(void){
+ *  	static GnmFuncCollection theCollection[] = {
+ *  		{ "Astronomical Functions", astronomical_functions },
+ *  		{ NULL }
+ *  	};
+ *  	return theCollection;
+ *  }
+ *
+ *
+ * Note that in this case the function group identifier is of no relevance to the loading process. The function that
+ * provides all information is explicitly specified in plugin.xml as the category. Note that go_plugin_init(), if it
+ * exists in this shared library, is executed before the collection provider function is called.
+ *
+ * This may not appear as a major advantage per se. However, this auto-loading mechanism opens the door for this shared
+ * library doing whatever it pleases to build the function collection information it provides to the calling Gnumeric
+ * plugin loader. An obvious application of this is a generic XLL loader. This shared library can scan this directory
+ * and all subdirectories for other shared libraries that provide a mechanism for self-registering function definitions,
+ * such as the Excel SDK xlAutoOpen function. Once all functions that are to be forwarded to Gnumeric have been found
+ * and tabulated, the worksheet function collection provider function can put the data into an array, and pass this
+ * information back to Gnumeric.
+ *
+ * In essence, this makes it possible to have XLLs automatically loaded and run-time adapted. Clearly, more
+ * functionality is needed since when Gnumeric calls a worksheet function, it directly calls into that entry point, and
+ * passes it data in Gnumeric style. Since an external XLL's exposed functions expect data in Excel format, those
+ * functions' entry points cannot simply be forwarded. Instead, a conversion function is invoked. Luckily, the Gnumeric
+ * infrastructure provides EvalFunctionInfo pointer at the time of call. Given the EvalFunctionInfo *ei, we have the
+ * name of the function we are callin in ei->func_call->func->name. We can use this, to look up which function is
+ * actually to be called. For the look-up, we create a global function map (name->some structure) at the time of
+ * go_plugin_init that we peruse later.
+ *
+ *
+ * An alternative, when no information is available at the time of call as to which function is intended, is to create
+ * the code of a forwarding function dynamically at run time. This approach is fully implemented below, but inactive. It
+ * becomes active when the macro GENERATE_FORWARDING_CODE is defined at compile time.
+ *
+ * NOTE: the discussion below is largely on the subject of how to generate code dynamically at run time in a
+ * most-portable manner. IT IS INACTIVE.
+ *
+ * ---------------------------------------------------------------------------------------------------------------------
+ *
+ * If a forwarding function has to be generated at run time, placed in memory, its code has to be marked as executable
+ * (using mprotect/VirtualProtect), and its address is to be given to Gnumeric. In principle, this forwarding logic
+ * could be done by one and the same function if just one additional parameter could be inserted that contains the
+ * address of a bundle of information of the function that is to be forwarded to/from. In other words, we could use a
+ * scheme as follows:
+ *
+ *  Gnumeric --> calls what it believes is plugin function xyz() --> dynamic entry point code
+ *
+ *    dynamic entry point code (generated at run time and marked executable using mprotect/VirtualProtect)
+ *    pushes one pointer onto the stack. Behind this point is information specifically for calling xyz().
+ *
+ *    dynamic entry point code executes a jump (jmp) or similar to a generic forwarding function genericExcelFunction().
+ *
+ *    ----> genericExcelFunction() can be a standard C function. Its first argument is the information inserted by the
+ *                                 dynamic entry point. the genericExcelFunction() uses this information to determine how
+ *                                 many arguments it expects, what types the external XLL function expects as its
+ *                                 arguments, and what actual XLL function is to be called. It converts arguments, calls
+ *                                 the external function, receives a result, converts a result, and returns it.
+ *
+ *          Note that the genericExcelFunction() need not do anything when it returns if the dynamic entry point code
+ *          transferred program execution from it to the genericExcelFunction() using a jump (jmp), not a call, whence
+ *          the frame stack remains consistent with directly returning to Gnumeric. In other words, at the time of
+ *          return to Gnumeric, all is as if the dynamic entry point code was never involved.
+ *
+ * Unfortunately, this approach requires assembly and memory page status modifications and thus is not particularly
+ * portable. Having said that, the push and the jmp instruction probably exists in all assembler that Gnumeric may be
+ * ported to. Equally, the concept of marking memory pages as executable probably also exists on all platforms that
+ * Gnumeric may be ported to.
+ *
+ * Most importantly, though, push, jmp, and VirtualProtect() are available for WIN32/WIN64, and the whole point of this
+ * plugin was to provide automatic XLL loading for Gnumeric, so the fact that there are platforms for which this may
+ * prove to be too difficult is of no significance.
+ *
+ * The path eventually taken below is very close to the above outline with the exception that explicit assembler was
+ * avoided by the aid of some small helper functions (see excelPluginFunctionForwardPoint1,
+ * excelPluginFunctionForwardPoint2, amd excelPluginFunctionForwardPoint3). These functions contain essentially all that
+ * the forwarding function has to do, apart from that the pointer of the jump address, genericExcelFunction(), is
+ * replaced by an invalid numerical marker value, and likewise for the function information pointer. At run time, we use
+ * heuristics to guess whether the compiler has put the text for those three functions in forward or reverse order,
+ * guess the length of the code text, and guess the location of the two markers by binary comparison of the code text of
+ * excelPluginFunctionForwardPoint1 and excelPluginFunctionForwardPoint2, taking into account that we expect the markers
+ * to show up explicitly in the executable code. If any of the heuristics fail, we abort. This hack is not guaranteed to
+ * work for all compilers under all circumstances, but it is a start.
+ *
+ */
+
+#if defined( WIN32 ) || defined( WIN64 )
+#include <windef.h>
+#else
+#include "win32replacements.h"
+#endif
+
+#include "xlcall.h"
+
+/* All bits that are mutually exclusive in the type field of an xloper. */
+#define xltypeType       0x0FFF
+
+struct _XLL {
+	gchar * name;
+	GModule * handle;
+	void (*xlAutoFree)(XLOPER*);
+	GSList *excel_function_info_list;
+};
+
+typedef struct _XLL XLL;
+
+static GOPlugin *this_plugin=NULL;
+static GModule *xlcall32_handle=NULL;
+static void (*register_actual_excel4v)(void*p) = NULL;
+
+static GSList* XLLs=NULL;
+
+/* This balanced tree maps from function name to ExcelFunctionInfo. It is consulted when gnumeric attempts to call one
+   of the registered functions. */
+static GTree* excel_function_info_map=NULL;
+
+static GnmFuncEvalInfo * current_eval_info=NULL;
+static XLL * currently_called_xll = NULL;
+static unsigned long number_of_XLL_functions=0;
+
+/* The limit of 30 arguments is hardwired in Excel. We take advantage of this below when we convert from argv[] calling
+   convention to vararg calling convention (...) */
+enum { MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS = 30 };
+
+struct _ExcelFunctionInfo {
+	XLOPER* (*xll_function)(void);
+	GnmFuncArgs gnm_func_args; /* This is the function pointer gnumeric calls directly. */
+	XLL * xll; /* Not owned. Included for availability of information during callbacks, and for access to xlAutoFree. */
+	gchar * exported_function_symbol;
+	guint number_of_arguments;
+	GnmFuncFlags flags;
+	gchar * name;
+	gchar * argument_specifications;
+	gchar * argument_names;
+	gchar * category;
+	gchar * function_description;
+	gchar ** arguments_help;
+	GnmFuncHelp * gnm_func_help;
+};
+
+typedef struct _ExcelFunctionInfo ExcelFunctionInfo;
+
+typedef XLOPER*(*ExcelFunctionWithVarArgs)(XLOPER* first, ...);
+
+#define CASE( x ) case (x): return #x
+
+#ifdef WORK_IN_PROGRESS
+static const char * gnm_value_type_name(const GnmValue*g){
+	if (NULL!=g){
+		switch (g->type){
+		CASE(VALUE_EMPTY);
+		CASE(VALUE_BOOLEAN);
+		CASE(VALUE_INTEGER);
+		CASE(VALUE_FLOAT);
+		CASE(VALUE_ERROR);
+		CASE(VALUE_STRING);
+		CASE(VALUE_CELLRANGE);
+		CASE(VALUE_ARRAY);
+		default: return "<unknown>";
+		}
+	}
+	return "(nil)";
+}
+#endif
+
+static const char * xloper_type_name(const XLOPER*x){
+	if (NULL!=x){
+		switch(x->xltype & xltypeType){
+		CASE(xltypeNum);
+		CASE(xltypeStr);
+		CASE(xltypeBool);
+		CASE(xltypeRef);
+		CASE(xltypeErr);
+		CASE(xltypeFlow);
+		CASE(xltypeMulti);
+		CASE(xltypeMissing);
+		CASE(xltypeNil);
+		CASE(xltypeSRef);
+		CASE(xltypeInt);
+		default: return "<unknown>";
+		}
+	}
+	return "(nil)";
+}
+
+static void unsupported_xloper_type(const XLOPER*x){
+	g_warning(_("Unsupported xloper type \"%s\""),xloper_type_name(x));
+}
+
+static GnmValue * gnm_value_error_std[GNM_ERROR_UNKNOWN];
+
+static GnmStdError gnm_value_error_from_xloper(const XLOPER*x){
+	g_assert(NULL!=x);
+	g_assert((x->xltype&xltypeType)==xltypeErr);
+	switch(x->val.err){
+	case xlerrNull:  return GNM_ERROR_NULL;
+	case xlerrDiv0:  return GNM_ERROR_DIV0;
+	case xlerrValue: return GNM_ERROR_VALUE;
+	case xlerrRef:   return GNM_ERROR_REF;
+	case xlerrName:  return GNM_ERROR_NAME;
+	case xlerrNum:   return GNM_ERROR_NUM;
+	case xlerrNA:    return GNM_ERROR_NA;
+	default:;
+	}
+	return GNM_ERROR_UNKNOWN;
+}
+
+static WORD xloper_error_code_from_gnm_value(const GnmValue*g){
+	guint i;
+	g_assert(NULL!=g);
+	g_assert(VALUE_IS_ERROR(g));
+	if (VALUE_TERMINATE!=g){
+		for (i=0;i<GNM_ERROR_UNKNOWN;++i){
+			if (g->v_err.mesg==gnm_value_error_std[i]->v_err.mesg){
+				switch (i){
+				case GNM_ERROR_NULL:  return xlerrNull;
+				case GNM_ERROR_DIV0:  return xlerrDiv0;
+				case GNM_ERROR_VALUE: return xlerrValue;
+				case GNM_ERROR_REF:   return xlerrRef;
+				case GNM_ERROR_NAME:  return xlerrName;
+				case GNM_ERROR_NUM:   return xlerrNum;
+				case GNM_ERROR_NA:    return xlerrNA;
+				default:;
+				}
+			}
+		}
+	}
+	return xlerrValue;
+}
+
+/*
+ *     For most standard types, there is a natural mapping between XLOPER and GnmValue.
+ *     In addition, we use the following correspondence:
+ *
+ *                              |   GnmValue * v               |    XLOPER * x                                              |
+ *      ------------------------+------------------------------+------------------------------------------------------------+
+ *                              |   v       == NULL           <->   x->xltype == xltypeMissing                              |
+ *                              |   v->type == VALUE_EMPTY    <->   x->xltype == xltypeNil                                  |
+ *                              |   v->type == VALUE_ERROR    <->   x->xltype == xltypeErr                                  |
+ *      ------------------------+------------------------------+------------------------------------------------------------+
+ *
+ */
+
+static char * pascal_string_from_c_string(const char *s){
+	char *o=NULL;
+	if (NULL!=s){
+		guint l = strlen(s);
+		g_return_val_if_fail (l<(UINT_MAX-2U),NULL);
+		o = g_malloc(l+2U);
+		g_strlcpy(o+1,s,l+1);
+		if (l>UCHAR_MAX)
+			l=UCHAR_MAX;
+		o[0]=(unsigned char)l;
+	}
+	return o;
+}
+
+static char * c_string_from_pascal_string(const char *s){
+	if (NULL!=s){
+		const guint m = ((unsigned char)s[0])+1U;
+		char * o = g_malloc(m);
+		g_strlcpy(o,s+1,m);
+		return o;
+	}
+	return NULL;
+}
+
+static void copy_construct_xloper_from_gnm_value(XLOPER*out, const GnmValue*in, GnmFuncEvalInfo *ei){
+	int i,j,m,n;
+	g_assert(NULL!=out);
+	out->xltype = xltypeMissing;
+	out->val.num = 0.;
+	if (NULL!=in){
+		switch(in->type){
+		case VALUE_EMPTY:	out->xltype=xltypeNil;							break;
+		case VALUE_BOOLEAN:	out->xltype=xltypeBool;	out->val.boolean=(WORD)in->v_bool.val;		break;
+		case VALUE_INTEGER:
+		case VALUE_FLOAT:	out->xltype=xltypeNum;	out->val.num=in->v_float.val;			break;
+		case VALUE_ERROR:
+			out->xltype	=	xltypeErr;
+			out->val.err	=	xloper_error_code_from_gnm_value(in);
+			break;
+		case VALUE_STRING:	out->xltype=xltypeStr;	out->val.str=pascal_string_from_c_string(in->v_str.val->str);	break;
+		case VALUE_CELLRANGE:
+			{
+				GnmSheetRange sr;
+				GnmRangeRef const *rr = value_get_rangeref (in);
+				Sheet *end_sheet=NULL;
+				GnmValue *cell_value;
+				GnmCell  *cell;
+				gnm_rangeref_normalize (rr, ei->pos, &sr.sheet, &end_sheet, &sr.range);
+				if (sr.sheet != end_sheet){
+					/* We don't attempt to flatten a 3D range to an array. */
+					g_warning(_("Cannot convert 3D cell range to XLOPER."));
+				} else {
+					m			=	sr.range.end.col-sr.range.start.col+1;
+					n			=	sr.range.end.row-sr.range.start.row+1;
+					out->xltype		=	xltypeMulti;
+					out->val.array.lparray	=	ALLOC_ARRAY(XLOPER,m*n);
+					out->val.array.columns	=	m;
+					out->val.array.rows	=	n;
+					for (i=0;i<m;++i){
+						for (j=0;j<n;++j){
+							cell=sheet_cell_get(sr.sheet,sr.range.start.col+i,sr.range.start.row+j);
+							cell_value=NULL;
+							if (NULL!=cell){
+								gnm_cell_eval(cell);
+								cell_value=cell->value;
+							}
+							copy_construct_xloper_from_gnm_value(out->val.array.lparray+i+j*m,cell_value,ei);
+						}
+					}
+				}
+			}
+			break;
+		case VALUE_ARRAY:
+			m			=	in->v_array.x;
+			n			=	in->v_array.y;
+			out->xltype		=	xltypeMulti;
+			out->val.array.lparray	=	ALLOC_ARRAY(XLOPER,m*n);
+			out->val.array.columns	=	m;
+			out->val.array.rows	=	n;
+			for (i=0;i<m;++i){
+				for (j=0;j<n;++j){
+					copy_construct_xloper_from_gnm_value(out->val.array.lparray+i+j*m,in->v_array.vals[i][j],ei);
+				}
+			}
+			break;
+		default:;
+			g_warning(_("Unsupported GnmValue type (%d)"),in->type);
+		}
+	}
+}
+
+static void delete_string(gchar**s){
+	g_free(*s);
+	*s=NULL;
+}
+
+static void destruct_xloper(XLOPER*x){
+	int i,n;
+	if (NULL!=x){
+		switch(x->xltype & xltypeType){
+		case xltypeNum:							break;
+		case xltypeStr:		delete_string(&x->val.str);		break;
+		case xltypeBool:						break;
+		case xltypeRef:
+			if (NULL!=x->val.mref.lpmref&&x->val.mref.lpmref->count!=1) {
+				unsupported_xloper_type(x);
+			} else {
+				if (NULL!=x->val.mref.lpmref)
+					FREE_ARRAY(x->val.mref.lpmref,1);
+				x->val.mref.lpmref=NULL;
+			}
+		break;
+		case xltypeErr:							break;
+		case xltypeFlow:	unsupported_xloper_type(x);		break;
+		case xltypeMulti:
+			n=x->val.array.rows*x->val.array.columns;
+			for (i=0;i<n;++i){
+				destruct_xloper(x->val.array.lparray+i);
+			}
+			FREE_ARRAY(x->val.array.lparray,n);
+			break;
+		case xltypeMissing:						break;
+		case xltypeNil:							break;
+		case xltypeSRef:	unsupported_xloper_type(x);		break;
+		case xltypeInt:							break;
+		default:		unsupported_xloper_type(x);
+		}
+	}
+	x->xltype=xltypeNil;
+}
+
+static GnmValue* new_gnm_value_from_xloper(const XLOPER*x){
+	guint i,j,m,n;
+	char *o=NULL,*s=NULL;
+	GnmValue * g = NULL;
+	if (NULL!=x) {
+		switch (x->xltype&xltypeType){
+		case xltypeNum:		g=value_new_float(x->val.num);					break;
+		case xltypeStr:
+			s=x->val.str;
+			if (NULL!=s){
+				const guint m = ((unsigned char)s[0])+1U;
+				o=ALLOC_ARRAY(char,m);
+				g_strlcpy(o,s+1,m);
+			}
+			g=value_new_string(o);
+			if (NULL!=s){
+				FREE_ARRAY(o,((unsigned char)s[0])+1U);
+			}
+			o=NULL;
+			s=NULL;
+			break;
+		case xltypeBool:	g=value_new_bool(x->val.boolean);				break;
+		case xltypeRef:		unsupported_xloper_type(x);					break;
+		case xltypeErr:		g=value_new_error_std(NULL,gnm_value_error_from_xloper(x));	break;
+		case xltypeFlow:	unsupported_xloper_type(x);					break;
+		case xltypeMulti:
+			m=x->val.array.columns;
+			n=x->val.array.rows;
+			if (m*n>0){
+				g=value_new_array_empty(m,n);
+				for (i=0;i<m;++i){
+					for (j=0;j<n;++j){
+						g->v_array.vals[i][j]=new_gnm_value_from_xloper(x->val.array.lparray+i+j*m);
+					}
+				}
+			} else {
+				g=value_new_error_std(NULL,GNM_ERROR_VALUE);
+			}
+			break;
+		case xltypeMissing:									break;
+		case xltypeNil:		g=value_new_empty();						break;
+		case xltypeSRef:	unsupported_xloper_type(x);					break;
+		case xltypeInt:		g=value_new_int(x->val.w);					break;
+		default:		unsupported_xloper_type(x);
+		}
+	} else {
+		g=value_new_error_std(NULL,GNM_ERROR_NUM);
+	}
+	return g;
+}
+
+#ifdef GENERATE_FORWARDING_CODE
+static GnmValue * genericExcelFunction (const ExcelFunctionInfo*info, GnmFuncEvalInfo *ei, GnmValue const * const *argv)
+#else
+static GnmValue * genericExcelFunction (GnmFuncEvalInfo *ei, GnmValue const * const *argv)
+#endif
+{
+	XLOPER x[MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS], *r = 0;
+	ExcelFunctionWithVarArgs func = NULL;
+	GnmValue *g=NULL;
+	guint i,m;
+#ifndef GENERATE_FORWARDING_CODE
+	const ExcelFunctionInfo*info=NULL;
+	g_assert(NULL!=excel_function_info_map);
+	info=g_tree_lookup (excel_function_info_map,ei->func_call->func->name);
+#endif
+	g_assert(NULL!=info);
+	m=ei->func_call->argc;
+	if ( m > MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS )
+		m = MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS;
+	for (i=0;i<m;++i)
+		copy_construct_xloper_from_gnm_value(x+i,argv[i],ei);
+	m = info->number_of_arguments;
+	if ( m > MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS )
+		m = MAXIMUM_NUMBER_OF_EXCEL_FUNCTION_ARGUMENTS;
+	for (;i<m;++i)
+		x[i].xltype=xltypeMissing;
+	func = (ExcelFunctionWithVarArgs)info->xll_function;
+	g_assert(NULL!=func);
+	currently_called_xll = info->xll;
+	current_eval_info    = ei;
+	switch (info->number_of_arguments){
+	case  0: r=info->xll_function(); break;
+	/*
+	  bash script to generate code below
+	  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
+	*/
+	case  1: r=func(x+0); break;
+	case  2: r=func(x+0,x+1); break;
+	case  3: r=func(x+0,x+1,x+2); break;
+	case  4: r=func(x+0,x+1,x+2,x+3); break;
+	case  5: r=func(x+0,x+1,x+2,x+3,x+4); break;
+	case  6: r=func(x+0,x+1,x+2,x+3,x+4,x+5); break;
+	case  7: r=func(x+0,x+1,x+2,x+3,x+4,x+5,x+6); break;
+	case  8: r=func(x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7); break;
+	case  9: r=func(x+0,x+1,x+2,x+3,x+4,x+5,x+6,x+7,x+8); break;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	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;
+	}
+	g=new_gnm_value_from_xloper(r);
+	if ( r && (r->xltype & xlbitDLLFree) && (NULL!=info->xll->xlAutoFree) )
+		info->xll->xlAutoFree( r );
+	currently_called_xll = NULL;
+	current_eval_info    = NULL;
+	for (i=0;i<info->number_of_arguments;++i)
+		destruct_xloper(x+i);
+	return g;
+}
+
+/***************************************************************************/
+
+static int gnm_func_help_entries(int number_of_arguments){
+	return number_of_arguments + 4; /* NAME,DESCRIPTION,EXCEL,ARG1,...ARGn,END */
+}
+
+static void free_excel_function_info(gpointer data){
+	ExcelFunctionInfo*info=(ExcelFunctionInfo*)data;
+	const guint n = info->number_of_arguments;
+	delete_string(&info->exported_function_symbol);
+	delete_string(&info->name);
+	delete_string(&info->argument_specifications);
+	delete_string(&info->argument_names);
+	delete_string(&info->category);
+	delete_string(&info->function_description);
+	if (NULL!=info->arguments_help){
+		guint i = n;
+		while (i--){
+			delete_string(info->arguments_help+i);
+		}
+		FREE_ARRAY(info->arguments_help,n);
+		info->arguments_help=NULL;
+	}
+	if (NULL!=info->gnm_func_help){
+		guint i, m=gnm_func_help_entries(n);
+		for (i=0;i<m;++i){
+			delete_string((gchar**)&info->gnm_func_help[i].text);
+		}
+		FREE_ARRAY(info->gnm_func_help,m);
+		info->gnm_func_help=NULL;
+	}
+	info->number_of_arguments=0;
+	info->xll_function=NULL;
+	info->gnm_func_args=NULL;
+	FREE_ARRAY(info,1);
+}
+
+typedef int WINAPI (*ExcelAutoOpenFunc)(void);
+typedef int WINAPI (*ExcelAutoCloseFunc)(void);
+
+static void free_XLL(gpointer data){
+	XLL*xll=(XLL*)data;
+	if (NULL!=xll->handle){
+		ExcelAutoCloseFunc xlAutoCloseFunc=NULL;
+		g_module_symbol (xll->handle, "xlAutoClose", (gpointer) &xlAutoCloseFunc);
+		if (NULL!=xlAutoCloseFunc){
+			currently_called_xll = xll;
+			xlAutoCloseFunc();
+			currently_called_xll = NULL;
+		}
+		if (!g_module_close (xll->handle))
+			g_warning (_("%s: %s"), xll->name, g_module_error ());
+		xll->handle=NULL;
+	}
+	go_slist_free_custom (xll->excel_function_info_list, free_excel_function_info);
+	xll->excel_function_info_list=NULL;
+	delete_string(&xll->name);
+	FREE_ARRAY(xll,1);
+}
+
+static void free_XLLs(void){
+	go_slist_free_custom (XLLs, free_XLL);
+	XLLs=NULL;
+}
+
+static int actual_Excel4v(int xlfn, XLOPER* operRes, int count, XLOPER** opers) {
+	switch(xlfn) {
+	case xlfRegister: {
+		ExcelFunctionInfo*info=NULL;
+		GnmFuncFlags flags = GNM_FUNC_SIMPLE; /* We may have to include GNM_FUNC_RETURNS_NON_SCALAR here since all Excel functions may return an array. */
+		GString * argument_specifications=g_string_sized_new(21);
+		gchar ** arg_names=NULL, *exported_function_symbol;
+		guint number_of_arguments=0;
+		int i,j,m,n;
+		/*
+		 * Check http://msdn.microsoft.com/en-us/library/bb687900.aspx for details.
+		 *
+		 * 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).
+		 *             This feature allows, in principle, for one XLL (the one calling Excel4v) to register functions that physically reside in another DLL.
+			       We do not allow for this feature here.
+			       IGNORED HERE.
+		 * opers[ 1] : exported function symbol in XLL/DLL (string). To be located with g_module_symbol().
+			       MANDATORY.
+		 * 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).
+			       MANDATORY.
+		 * opers[ 3] : function name that is to be shown as the effective, user-visible, work sheet function.
+			       DEFAULTS to opers[1].
+		 * opers[ 4] : string of comma separated argument names.
+			       DEFAULTS to NULL.
+		 * opers[ 5] : function type as xltypeNum. 0.=hidden, 1.=function, 2.=command.
+			       IGNORED HERE.
+		 * opers[ 6] : function category as string.
+			       DEFAULTS to "XLL functions".
+		 * opers[ 7] : short cut text as string.
+			       IGNORED HERE.
+		 * opers[ 8] : help topic as string.
+			       IGNORED HERE.
+		 * opers[ 9] : function description as string.
+			       DEFAULTS to NULL.
+		 * opers[10 .. 10+n-1] : Help on the n arguments of actual function that is being registered.
+			       DEFAULT to NULL.
+		 */
+		if (count<3){
+			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);
+			return xlretInvCount; /* "An invalid number of arguments was entered. In versions up to Excel
+						  2003, the maximum number of arguments any function can take is 30. In
+						  Excel 2007, the maximum number is 255. Some require a fixed or minimum
+						  number of arguments." */
+		}
+		if ( xltypeStr != (opers[1]->xltype&xltypeType) ||  xltypeStr != (opers[2]->xltype&xltypeType) ){
+			g_warning(_("Excel plugin loader / xlfRegister: the second and third argument must be strings (DLL name[ignored],exported name[mandatory],types string[mandatory])."));
+			return xlretInvXloper; /* "An invalid XLOPER or XLOPER12 was passed to the function, or an argument of the wrong type was used." */
+		}
+		m=0;
+		if (opers[2]->val.str)
+			m=(unsigned char)opers[2]->val.str[0];
+		for (i=0;i<m;++i){
+			switch(opers[2]->val.str[i+1]){
+			case 'p':
+			case 'P':
+			case 'r':
+			case 'R':
+				argument_specifications=g_string_append_c(argument_specifications,'?');
+				++number_of_arguments;
+				break;
+			case '\r': /* Various junk we may as well ignore. */
+			case '\n':
+			case '\t':
+			case ',':
+			case ';':
+			case ' ':
+			case '#': /* This signals a request for this function or macro to have access to macro specific call back functions. Ignored.  */
+				break;
+			case '!':
+				flags |= GNM_FUNC_VOLATILE;
+				break;
+			default:;
+			}
+		}
+		exported_function_symbol=c_string_from_pascal_string(opers[1]->val.str);
+		g_assert(argument_specifications->len==number_of_arguments);
+		if (number_of_arguments>0){
+			--number_of_arguments; /* Subtract the return type count. The if statement is only to protect against sloppy XLLs. */
+			argument_specifications->str[0]='|'; /* All arguments are optional to an Excel function that accepts arguments type 'P'. */
+		}
+		info=ALLOC_ARRAY(ExcelFunctionInfo,1);
+		info->number_of_arguments=number_of_arguments;
+		info->argument_specifications=g_strdup(argument_specifications->str);
+		info->xll=currently_called_xll;
+		info->flags=flags;
+		info->exported_function_symbol = exported_function_symbol;
+		if (count>3 && (opers[3]->xltype&xltypeType)==xltypeStr){
+			info->name = c_string_from_pascal_string(opers[3]->val.str);
+		} else {
+			info->name = g_strdup(info->exported_function_symbol);
+		}
+		if (count>4 && (opers[4]->xltype&xltypeType)==xltypeStr){
+			info->argument_names = c_string_from_pascal_string(opers[4]->val.str);
+			arg_names=g_strsplit(info->argument_names,",",number_of_arguments);
+		}
+		if (count>6 && (opers[6]->xltype&xltypeType)==xltypeStr){
+			info->category = c_string_from_pascal_string(opers[6]->val.str);
+		} else {
+			info->category = g_strdup("XLL functions");
+		}
+		if (count>9 && (opers[9]->xltype&xltypeType)==xltypeStr){
+			info->function_description = c_string_from_pascal_string(opers[9]->val.str);
+		}
+
+		m=0;
+		n=gnm_func_help_entries(number_of_arguments);
+		info->gnm_func_help=ALLOC_ARRAY(GnmFuncHelp,n);
+		g_assert(m<n);
+		info->gnm_func_help[m].type=GNM_FUNC_HELP_NAME;
+		info->gnm_func_help[m].text=g_strdup(info->name);
+		++m;
+		g_assert(m<n);
+		info->gnm_func_help[m].type=GNM_FUNC_HELP_DESCRIPTION;
+		info->gnm_func_help[m].text=g_strdup(info->function_description);
+		++m;
+		g_assert(m<n);
+		info->gnm_func_help[m].type=GNM_FUNC_HELP_EXCEL;
+		info->gnm_func_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");
+		/* We limit the number of argument strings we copy to the minimum of the number of arguments indicated
+		   in the types string and the number of arguments strings given. We always provide enough space for as
+		   many as indicated in the types string. */
+		info->arguments_help = ALLOC_ARRAY(gchar*,number_of_arguments);
+		for (i=10,j=0; i<count && i-10<(int)number_of_arguments; ++i){
+			if ((opers[i]->xltype&xltypeType)==xltypeStr){
+				const char * arg_name = (NULL!=arg_names && NULL!=arg_names[j]) ? arg_names[j++] : NULL;
+				info->arguments_help[i-10] = c_string_from_pascal_string(opers[i]->val.str);
+				++m;
+				g_assert(m<n);
+				info->gnm_func_help[m].type=GNM_FUNC_HELP_ARG;
+				info->gnm_func_help[m].text = (NULL!=arg_name) ? g_strconcat(arg_name,":",info->arguments_help[i-10],NULL) : g_strdup(info->arguments_help[i-10]);
+			}
+		}
+		++m;
+		g_assert(m<n);
+		info->gnm_func_help[m].type=GNM_FUNC_HELP_END;
+		if (NULL!=arg_names){
+			g_strfreev(arg_names);
+			arg_names=NULL;
+		}
+		g_module_symbol (info->xll->handle, info->exported_function_symbol, (gpointer) &info->xll_function);
+		if (NULL!=info->xll_function){
+			++number_of_XLL_functions;
+			GO_SLIST_PREPEND(currently_called_xll->excel_function_info_list,info);
+			if (NULL!=operRes){
+				operRes->xltype=xltypeNum;
+				operRes->val.num=(unsigned long)info; /* This should be set to the resulting registration id. We use the info pointer as a proxy here. */
+			}
+			return xlretSuccess;
+		}
+		g_warning(_("%s:%d:%s: failed to find function \"%s\" in XLL/DLL/SO %s .\n"),__FILE__,__LINE__,__PRETTY_FUNCTION__,info->name,info->xll->name);
+		free_excel_function_info(info);
+		return xlretInvXloper; /* "An invalid XLOPER or XLOPER12 was passed to the function, or an argument of the wrong type was used." */
+	}
+	case xlFree:
+		while (count--){
+			destruct_xloper(opers[count]);
+		}
+		return xlretSuccess;
+	case xlGetName:  /* The name of the DLL that is calling */
+		if (NULL!=operRes){
+			operRes->xltype=xltypeStr;
+			operRes->val.str=pascal_string_from_c_string(currently_called_xll->name);
+			return xlretSuccess;
+		}
+		return xlretInvXloper;
+	case xlfRow:     /* Pass in the output of xlfCaller to get the row of the calling worksheet cell as an XLOPER of
+			    type integer or number. Note that the output may be an array similar to the output from
+			    xlSheetNm. */
+		if (NULL!=operRes){
+			if ( count>0 && xltypeRef==(opers[0]->xltype&xltypeType) && NULL!=operRes->val.mref.lpmref ){
+				operRes->xltype = xltypeInt;
+				operRes->val.w = operRes->val.mref.lpmref->reftbl[1].rwFirst;
+				return xlretSuccess;
+			}
+			if (NULL!=current_eval_info){
+				operRes->xltype = xltypeInt;
+				operRes->val.w = (short int) current_eval_info->pos->eval.row;
+				return xlretSuccess;
+			}
+		}
+		return xlretInvXloper;
+	case xlfColumn:  /* Pass in the output of xlfCaller to get the column of the calling worksheet cell as an XLOPER of
+			    type integer or number. Note that the output may be an array similar to the output from
+			    xlSheetNm. */
+		if (NULL!=operRes){
+			if ( count>0 && xltypeRef==(opers[0]->xltype&xltypeType) && NULL!=operRes->val.mref.lpmref ){
+				operRes->xltype = xltypeInt;
+				operRes->val.w = operRes->val.mref.lpmref->reftbl[1].colFirst;
+				return xlretSuccess;
+			}
+			if (NULL!=current_eval_info){
+				operRes->xltype = xltypeInt;
+				operRes->val.w = (short int) current_eval_info->pos->eval.col;
+				return xlretSuccess;
+			}
+		}
+		return xlretInvXloper;
+
+
+	/* Commonly called functions. None of these are fully implemented. */
+	case xlfCaller:  /* Information about the location that is calling a worksheet function. The result of this can
+			    be scalar XLOPER or a range, depending on whether a function is called as an array function,
+			    or as a single cell function call. The result should be passed back in when calling
+			    xlSheetNm, xlfRow, or xlfColumn. */
+		if (NULL!=operRes && NULL!=current_eval_info){
+			operRes->xltype           = xltypeRef;
+			operRes->val.mref.idSheet = current_eval_info->pos->sheet->index_in_wb; /* This is not globally unique but better than nothing. */
+			operRes->val.mref.lpmref  = ALLOC_ARRAY(XLMREF,1);
+			operRes->val.mref.lpmref->count=1;
+			operRes->val.mref.lpmref->reftbl[1].rwFirst  = current_eval_info->pos->eval.row;
+			operRes->val.mref.lpmref->reftbl[1].rwLast   = current_eval_info->pos->eval.row;
+			operRes->val.mref.lpmref->reftbl[1].colFirst = current_eval_info->pos->eval.col;
+			operRes->val.mref.lpmref->reftbl[1].colLast  = current_eval_info->pos->eval.col;
+			return xlretSuccess;
+		}
+		return xlretInvXloper;
+	case xlSheetNm:  /* Pass in the output of xlfCaller to get the name of the calling worksheet as an XLOPER of
+			    type string. Note that the output of this can be an array if the output of xlfCaller was an
+			    array. To play safe, always check if it is an array, and use element (0,0) if it is, else
+			    use the scalar. */
+		if (NULL!=operRes && NULL!=current_eval_info){
+			int index_in_wb = current_eval_info->pos->sheet->index_in_wb, row=current_eval_info->pos->eval.row, col=current_eval_info->pos->eval.col;
+			Workbook * workbook = current_eval_info->pos->sheet->workbook;
+			Sheet *sheet        = current_eval_info->pos->sheet;
+			if (count>0 && xltypeRef == (opers[0]->xltype&xltypeType)){
+				index_in_wb=opers[0]->val.mref.idSheet;
+				if (NULL!=operRes->val.mref.lpmref){
+					row=operRes->val.mref.lpmref->reftbl[1].rwFirst;
+					col=operRes->val.mref.lpmref->reftbl[1].colFirst;
+				}
+				sheet=workbook_sheet_by_index(workbook,index_in_wb);
+			}
+			operRes->xltype=xltypeStr;
+			operRes->val.str=pascal_string_from_c_string(sheet->name_unquoted);
+			return xlretSuccess;
+		}
+		return xlretInvXloper;
+	case xlGetHwnd:  /* The WIN32 window handle of the "Excel" window */
+	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.*/
+		if (NULL!=operRes){
+			operRes->xltype=xltypeBool;
+			operRes->val.boolean=0;
+		}
+		return xlretSuccess;
+	case xlcMessage: /* Set message bar. Expects one argument but no return value. */
+		return xlretSuccess;
+	default:;
+	}
+	return xlretInvXlfn; /* "An invalid function number was supplied. If you are using constants from XLCALL.H, this
+				 should not occur unless you are calling something that is not supported in the version
+				 of Excel you are running." */
+}
+
+static void
+load_xlcall32(GOPlugin *plugin){
+	gchar *full_module_file_name;
+	if (!g_module_supported ()) {
+		g_warning (_("Dynamic module loading is not supported on this system."));
+		return;
+	}
+	full_module_file_name = g_build_filename (go_plugin_get_dir_name (plugin), "xlcall32",NULL);
+#ifdef WIN32
+	SetErrorMode(SEM_FAILCRITICALERRORS); // avoid message box if library not found
+#endif
+	xlcall32_handle = g_module_open (full_module_file_name, G_MODULE_BIND_LAZY);
+#ifdef WIN32
+	SetErrorMode(0);
+#endif
+	if (xlcall32_handle == NULL) {
+		g_warning (_("Unable to open module file \"%s\"."),full_module_file_name);
+		return;
+	}
+	g_module_symbol (xlcall32_handle, "register_actual_excel4v", (gpointer) &register_actual_excel4v);
+	if (register_actual_excel4v == NULL) {
+		g_warning (_("Module \"%s\" doesn't contain (\"register_actual_excel4v\" symbol)."),full_module_file_name);
+		return;
+	}
+	register_actual_excel4v(actual_Excel4v);
+	g_free (full_module_file_name);
+}
+
+/***************************************************************************/
+
+static GnmFuncCollection *theFuncCollection = NULL;
+static unsigned long theFuncCollection_size = 0;
+
+const GnmFuncCollection * gnmFuncCollectionProviderFunc(void);
+
+const GnmFuncCollection * gnmFuncCollectionProviderFunc(void){
+	return theFuncCollection;
+}
+
+#ifdef GENERATE_FORWARDING_CODE
+
+typedef GnmValue * (*GenericExcelFunc) (const ExcelFunctionInfo*info, GnmFuncEvalInfo *ei, GnmValue const * const *argv);
+typedef GnmValue * (*ExcelPluginForwardFunc) (GnmFuncEvalInfo *ei, GnmValue const * const *argv);
+
+/* These markers are deliberately chosen to be asymmetric. If you try using something like just 0 and ~0, the compiler
+   optimizes the instruction width down and we can no longer fill in the absolute address at run time. This may only
+   happen on 64 bit platforms, but to be portable, we play it safe and use 4-byte asymmetric markers. */
+#define MARKER1 (((uintptr_t)1)<<(sizeof(void*)*CHAR_BIT-1))
+#define MARKER2 (MARKER1+(uintptr_t)1)
+#define MARKER3 (~MARKER1)
+#define MARKER4 (~MARKER2)
+
+static GnmValue * excelPluginFunctionForwardPoint1(GnmFuncEvalInfo *ei, GnmValue const * const *argv){
+	return ((GenericExcelFunc)MARKER1) ((void*)MARKER2,ei,argv);
+}
+static GnmValue * excelPluginFunctionForwardPoint2(GnmFuncEvalInfo *ei, GnmValue const * const *argv){
+	return ((GenericExcelFunc)MARKER3) ((void*)MARKER4,ei,argv);
+}
+static GnmValue * excelPluginFunctionForwardPoint3(GnmFuncEvalInfo *ei, GnmValue const * const *argv){
+	return ((GenericExcelFunc)MARKER1) ((void*)MARKER2,ei,argv);
+}
+
+/*
+ * The above code, with gcc 4.3.3 (x86_64-linux-gnu), compiles into the following assembly (extracted
+ * with objdump -d .libs/excelplugins.o):
+ *
+ *	0000000000000020 <excelPluginFunctionForwardPoint1>:
+ *	      20:	48 89 f2             	mov    %rsi,%rdx
+ *	      23:	49 bb 00 00 00 00 00 	mov    $0x8000000000000000,%r11
+ *	      2a:	00 00 80
+ *	      2d:	48 89 fe             	mov    %rdi,%rsi
+ *	      30:	48 bf 01 00 00 00 00 	mov    $0x8000000000000001,%rdi
+ *	      37:	00 00 80
+ *	      3a:	41 ff e3             	jmpq   *%r11
+ *	      3d:	0f 1f 00             	nopl   (%rax)
+ *
+ *	0000000000000040 <excelPluginFunctionForwardPoint2>:
+ *	      40:	48 89 f2             	mov    %rsi,%rdx
+ *	      43:	49 bb ff ff ff ff ff 	mov    $0x7fffffffffffffff,%r11
+ *	      4a:	ff ff 7f
+ *	      4d:	48 89 fe             	mov    %rdi,%rsi
+ *	      50:	48 bf fe ff ff ff ff 	mov    $0x7ffffffffffffffe,%rdi
+ *	      57:	ff ff 7f
+ *	      5a:	41 ff e3             	jmpq   *%r11
+ *	      5d:	0f 1f 00             	nopl   (%rax)
+ *
+ * The fact that there is a jmpq instead of a call(q) instruction shows that the optimizer used tail recursion
+ * optimization, leading to the jmp instruction mentioned in the original design at the beginning of this file.
+ * Note that the nopl instructions are just filling material - execution never returns there following the jmpq.
+ *
+ * Also note that the tail recursion optimization is not a crucial part of the approach used here. The crucial points
+ * are:-
+ *
+ *        + We need to identify a piece of binary code text that is a binary template for a wrapper function.
+ *        + This binary template must have a placeholder where we can write the address of the generic Excel worksheet
+ *          function wrapper function called genericExcelFunction(). In the binary template code above this is MARKER1
+ *          in excelPluginFunctionForwardPoint1(). We use dynamic replacement of this function address, despite the fact
+ *          that we know at compile time which function we wish to call, to stop the compiler from any type of
+ *          compile/link optimization that would prevent the binary template code to be clean and proper in the program
+ *          text. Experiments showed that without this, the binary template code easily ended up inlined. When the code
+ *          contains an arbitrary number as the address to jump to, especially when it is an *invalid* address, the
+ *          compiler/linker cannot optimize it away at compile/link time.
+ *
+ *        + The binary template must have a placeholder where we can write the address of a data pointer that will only
+ *          be known at run time. In the binary template code above this is MARKER2 in excelPluginFunctionForwardPoint1().
+ *
+ * At initialisation time, we check that the distance between excelPluginFunctionForwardPoint1() and
+ * excelPluginFunctionForwardPoint2() is the same as that between excelPluginFunctionForwardPoint2() and
+ * excelPluginFunctionForwardPoint3(). Also, we check that the position of MARKER1 in excelPluginFunctionForwardPoint1
+ * is the same as that of MARKER3 in excelPluginFunctionForwardPoint2, and, analogously, MARKER2 is at the same relative
+ * position in excelPluginFunctionForwardPoint1 as MARKER4 in excelPluginFunctionForwardPoint2. If any of this fails, we
+ * assume that on this platform, for this compilation, the heuristic approach for generation of a binary template
+ * failed, and do not continue with the loading. I, as the author, am aware that this approach is not failsafe. I would
+ * argue, though, that it is sufficient for practical purposes since the chances of getting a false positive seem to be
+ * astronomically small. Your mileage might vary.
+ *
+ */
+
+static void * excelPluginFunctionsForwardingCode=NULL;
+static size_t code_length=0;
+static guint excelFunctionInfoPointerOffsetInGeneratedCode=0;
+
+#endif
+
+static gboolean populate_gnm_func_descriptor_array_for_category_and_free_function_list(gpointer key, gpointer value, gpointer data){
+	gchar*category=(gchar*)key;
+	GSList*excel_function_info_list=(GSList*)value;
+	guint *i = (guint*)data, j;
+	guint n = g_slist_length(excel_function_info_list);
+	g_assert(*i<theFuncCollection_size);
+	theFuncCollection[*i].category_name=category; /* Memory not owned here. */
+	theFuncCollection[*i].func_descriptors=ALLOC_ARRAY(GnmFuncDescriptor,n+1); /* +1 for the sentinel */
+	j=0;
+	GO_SLIST_FOREACH(excel_function_info_list,ExcelFunctionInfo,info,
+		GnmFuncDescriptor* descriptor = (GnmFuncDescriptor*)(theFuncCollection[*i].func_descriptors+j);
+		descriptor->name      = info->name;                    /* Memory remains owned by info structure. */
+		descriptor->arg_spec  = info->argument_specifications; /* Memory remains owned by info structure. */
+		descriptor->help      = info->gnm_func_help;           /* Memory remains owned by info structure. */
+		descriptor->fn_args   = info->gnm_func_args;           /* Function pointer. */
+		descriptor->flags       = info->flags;
+		descriptor->impl_status = GNM_FUNC_IMPL_STATUS_COMPLETE;
+		descriptor->test_status = GNM_FUNC_TEST_STATUS_BASIC;
+		++j;
+	);
+	++*i;
+	g_slist_free(excel_function_info_list);
+	return FALSE;
+}
+
+
+static void scan_for_XLLs(const gchar*dir_name) {
+	GDir*dir=g_dir_open(dir_name,0,NULL);
+	gchar * full_entry_name;
+	const gchar *d_name;
+	struct stat d_info;
+	GModule *handle;
+	int stat_success;
+	if (NULL!=dir){
+		while ((d_name = g_dir_read_name (dir)) != NULL) {
+			if ( ! (strcmp (d_name, ".") == 0 || strcmp (d_name, "..") == 0) ){
+				full_entry_name = g_build_filename (dir_name, d_name, NULL);
+				stat_success = g_stat(full_entry_name,&d_info);
+				if (0==stat_success){
+					if (S_ISDIR(d_info.st_mode)){
+						scan_for_XLLs(full_entry_name);
+					} else {
+#ifdef WIN32
+						SetErrorMode(SEM_FAILCRITICALERRORS); // avoid message box if library not found
+#endif
+						handle = g_module_open (full_entry_name, G_MODULE_BIND_LAZY);
+#ifdef WIN32
+						SetErrorMode(0);
+#endif
+						if (NULL!=handle){
+							XLL*xll = ALLOC_ARRAY(XLL,1);
+							ExcelAutoOpenFunc xlAutoOpenFunc=NULL;
+							xll->name   = g_strdup(full_entry_name);
+							xll->handle = handle;
+							g_module_symbol (xll->handle, "xlAutoFree", (gpointer) &xll->xlAutoFree);
+							xlAutoOpenFunc=NULL;
+							if (g_module_symbol (xll->handle, "xlAutoOpen", (gpointer) &xlAutoOpenFunc)){
+								unsigned long before=number_of_XLL_functions;
+								currently_called_xll = xll;
+								xlAutoOpenFunc();
+								currently_called_xll = NULL;
+								if (NULL==xll->excel_function_info_list){
+									g_warning(_("No loadable worksheet functions found in XLL/DLL/SO file %s ."),full_entry_name);
+								} else {
+									GO_SLIST_REVERSE(xll->excel_function_info_list);
+									GO_SLIST_PREPEND(XLLs,xll);
+									g_message(_("Loaded %lu functions from XLL/DLL/SO %s ."),number_of_XLL_functions-before,full_entry_name);
+								}
+							}
+							if (NULL==xll->excel_function_info_list){
+								free_XLL(xll);
+							}
+						}
+					}
+				}
+				g_free(full_entry_name);
+			}
+		}
+		g_dir_close (dir);
+	}
+}
+
+G_MODULE_EXPORT void go_plugin_init (GOPlugin *plugin, GOCmdContext *cc) {
+	GTree * map_from_category_to_function_list=NULL;
+	guint i;
+#ifdef GENERATE_FORWARDING_CODE
+	guint d12 = (excelPluginFunctionForwardPoint1>excelPluginFunctionForwardPoint2
+		     ?(char*)excelPluginFunctionForwardPoint1-(char*)excelPluginFunctionForwardPoint2
+		     :(char*)excelPluginFunctionForwardPoint2-(char*)excelPluginFunctionForwardPoint1);
+	guint d23 = (excelPluginFunctionForwardPoint2>excelPluginFunctionForwardPoint3
+		     ?(char*)excelPluginFunctionForwardPoint2-(char*)excelPluginFunctionForwardPoint3
+		     :(char*)excelPluginFunctionForwardPoint3-(char*)excelPluginFunctionForwardPoint2);
+	void *marker1=NULL, *marker2=NULL, *marker3=NULL, *marker4=NULL;
+	guint genericExcelFunctionPointerOffsetInGeneratedCode;
+	const char * const abort_message = "%s:%d:%s: Excel plugins loader dynamic code generation heuristics failed on this platform. Aborting the loading of Excel plugins.";
+#endif
+
+	load_xlcall32(plugin);
+
+	if (NULL==xlcall32_handle) /* If we couldn't load the helper dll, there is no point in continuing. */
+		return;
+
+	this_plugin=plugin;
+
+	for (i=0;i<GNM_ERROR_UNKNOWN;++i){
+		gnm_value_error_std[i]=value_new_error_std(NULL,(GnmStdError)i);
+	}
+
+	/* Find all external XLL functions that are to be adapted into Gnumeric. We scan for all shared libraries that
+	   expose xlAutoOpen, in this directory, and all sub directories. Find them, load them, find xlAutoFree if
+	   present, call xlAutoOpen, and record via the exposed actual_Excel4v function what worksheet functions they
+	   want to register with Excel. Then, put all this information in a table and make theFuncCollection point to
+	   it. */
+
+	/* We search recursively the directory in which this plugin resides and all of its subdirectories. This is not
+	   for any philosophical reasons and can be changed as desired. */
+
+	scan_for_XLLs(go_plugin_get_dir_name(plugin));
+
+	if (NULL!=XLLs)
+		GO_SLIST_REVERSE(XLLs);
+
+	if (number_of_XLL_functions>0){
+
+		/* Populate the global static map from (XLL) function name to Excel function info structure. */
+		excel_function_info_map=g_tree_new((GCompareFunc)g_strcmp0);
+		/* Simultaneously, populate the temporary map from category to Excel function info list. */
+		map_from_category_to_function_list=g_tree_new((GCompareFunc)g_strcmp0);
+		GO_SLIST_FOREACH (XLLs, XLL, xll,
+			GO_SLIST_FOREACH (xll->excel_function_info_list, ExcelFunctionInfo, info,
+				info->gnm_func_args=(void*)genericExcelFunction;
+				{
+					ExcelFunctionInfo*info_in_map = g_tree_lookup (excel_function_info_map,info->name);
+					if (NULL!=info_in_map)
+						g_warning(_("Overriding function %s from XLL/DLL/SO file %s with function of the same name from XLL/DLL/SO file %s."),info->name,info_in_map->xll->name,info->xll->name);
+					g_tree_insert(excel_function_info_map,info->name,info);
+				}
+				{
+					GSList * function_list = g_tree_lookup(map_from_category_to_function_list,info->category);
+					GO_SLIST_PREPEND(function_list,info);
+					g_tree_insert(map_from_category_to_function_list,info->category,function_list);
+				}
+
+			);
+
+                );
+
+#ifdef GENERATE_FORWARDING_CODE
+
+		code_length=d12;
+
+		/* Don't trust the simplistic hack (designed to obviate the use of explicit assembler) if this compiled unit does
+		   not have the three almost identical code pieces above next to each other at equal distance. */
+		if (d12!=d23){
+			g_warning(abort_message,__FILE__,__LINE__,__PRETTY_FUNCTION__);
+			return;
+		}
+
+		/* Find location of function pointer that is to be changed to point to genericExcelFunction() */
+		for (i=0;i+sizeof(void*)<=code_length;++i){
+			memcpy(&marker1,(char*)excelPluginFunctionForwardPoint1+i,sizeof(void*));
+			memcpy(&marker3,(char*)excelPluginFunctionForwardPoint2+i,sizeof(void*));
+			if (marker1==(void*)MARKER1 && marker3==(void*)MARKER3)
+				break;
+		}
+		if (i+sizeof(void*)>code_length){ /* Stop right here if the markers could not be not found */
+			g_warning(abort_message,__FILE__,__LINE__,__PRETTY_FUNCTION__);
+			return;
+		}
+		genericExcelFunctionPointerOffsetInGeneratedCode=i;
+
+		/* Find location of information pointer that is to be changed to information pointer specific to just one external XLL function. */
+		for (i=0;i+sizeof(void*)<=code_length;++i){
+			memcpy(&marker2,(char*)excelPluginFunctionForwardPoint1+i,sizeof(void*));
+			memcpy(&marker4,(char*)excelPluginFunctionForwardPoint2+i,sizeof(void*));
+			if (marker2==(void*)MARKER2 && marker4==(void*)MARKER4)
+				break;
+		}
+		if (i+sizeof(void*)>code_length){ /* Stop right here if the markers could not be not found */
+			g_warning(abort_message,__FILE__,__LINE__,__PRETTY_FUNCTION__);
+			return;
+		}
+		excelFunctionInfoPointerOffsetInGeneratedCode=i;
+
+#ifdef WIN32
+		excelPluginFunctionsForwardingCode= g_malloc(number_of_XLL_functions*code_length);
+		{
+			DWORD old_protect;
+			VirtualProtect(excelPluginFunctionsForwardingCode,
+				       number_of_XLL_functions*code_length,PAGE_EXECUTE_READWRITE, &old_protect);
+		}
+#else
+		/* Documentation on mprotect, memalign, and mmap indicates that using mmap as below is the most portable way to run-time generate executable code pages. */
+		excelPluginFunctionsForwardingCode =
+			mmap(NULL,number_of_XLL_functions*code_length,
+			     PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_ANONYMOUS,0,0);
+#endif
+		/* Copy forward function code template to executable memory location */
+		memcpy(excelPluginFunctionsForwardingCode,excelPluginFunctionForwardPoint1,code_length);
+		/* Find location of function pointer that is to be changed to point to genericExcelFunction() */
+		for (i=0;i+sizeof(void*)<=code_length;++i){
+			memcpy(&marker1,(char*)excelPluginFunctionForwardPoint1+i,sizeof(void*));
+			memcpy(&marker3,(char*)excelPluginFunctionForwardPoint2+i,sizeof(void*));
+			if (marker1==(void*)MARKER1 && marker3==(void*)MARKER3)
+				break;
+		}
+		if (i+sizeof(void*)>code_length){ /* Stop right here if the markers could not be not found */
+			g_warning(abort_message,__FILE__,__LINE__,__PRETTY_FUNCTION__);
+			return;
+		}
+		/* Change function pointer in code template to point to genericExcelFunction() */
+		{
+			GenericExcelFunc generic_excel_func = genericExcelFunction;
+			memcpy((char*)excelPluginFunctionsForwardingCode+genericExcelFunctionPointerOffsetInGeneratedCode,&generic_excel_func,sizeof(void*));
+		}
+		/* The first forwarding code block is now complete apart from the unique function info pointer */
+		/* Replicate */
+		for (i=1;i<number_of_XLL_functions;++i){
+			memcpy((char*)excelPluginFunctionsForwardingCode+i*code_length,excelPluginFunctionsForwardingCode,code_length);
+		}
+
+		i=0;
+		GO_SLIST_FOREACH (XLLs, XLL, xll,
+			GO_SLIST_FOREACH (xll->excel_function_info_list, ExcelFunctionInfo, info,
+				/* Each  forwarding code block is given a unique function info pointer. */
+				memcpy((char*)excelPluginFunctionsForwardingCode+i*code_length+excelFunctionInfoPointerOffsetInGeneratedCode,&info,sizeof(void*));
+				info->gnm_func_args=(GnmFuncArgs)((char*)excelPluginFunctionsForwardingCode+i*code_length);
+				++i;
+			);
+
+                );
+
+#endif
+		/* Now convert all loaded XLL function information to GnmFuncDescriptor style */
+		theFuncCollection_size=g_tree_nnodes(map_from_category_to_function_list);
+		theFuncCollection = ALLOC_ARRAY(GnmFuncCollection,theFuncCollection_size+1); /* +1 for the sentinel */
+
+		i=0;
+		g_tree_foreach(map_from_category_to_function_list,populate_gnm_func_descriptor_array_for_category_and_free_function_list,&i);
+		g_assert(i==theFuncCollection_size);
+		g_tree_destroy(map_from_category_to_function_list);
+	}
+}
+
+G_MODULE_EXPORT void go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc) {
+	guint i,j;
+
+	/* We should really unregister all of the loaded XLL functions. To write this code is a lot of effort, and in
+	   all likelihood, it is never really needed. Postponed until it becomes really necessary. */
+
+	for (i=0;i<theFuncCollection_size;++i){
+		if (NULL!=theFuncCollection[i].func_descriptors){
+			for (j=0;NULL!=theFuncCollection[i].func_descriptors[j].name;++j);
+			FREE_ARRAY((GnmFuncDescriptor*)theFuncCollection[i].func_descriptors,j+1);
+		}
+	}
+	FREE_ARRAY(theFuncCollection,theFuncCollection_size+1);
+	theFuncCollection_size=0;
+
+	if (NULL!=excel_function_info_map){
+		g_tree_destroy(excel_function_info_map); /* Both the keys and the values in that tree are memory handled in the sublists of the list XLLs. */
+		excel_function_info_map=NULL;
+	}
+
+	free_XLLs();
+
+	if (register_actual_excel4v)
+		register_actual_excel4v(NULL);
+	register_actual_excel4v=NULL;
+	if (NULL!=xlcall32_handle)
+		g_module_close(xlcall32_handle);
+	xlcall32_handle = NULL;
+	this_plugin=NULL;
+	theFuncCollection=NULL;
+#ifdef GENERATE_FORWARDING_CODE
+#ifdef WIN32
+	g_free(excelPluginFunctionsForwardingCode);
+#else
+	munmap(excelPluginFunctionsForwardingCode,number_of_XLL_functions*code_length);
+#endif
+	excelPluginFunctionsForwardingCode=NULL;
+	code_length=0;
+#endif
+	number_of_XLL_functions=0;
+
+	for (i=0;i<GNM_ERROR_UNKNOWN;++i){
+		value_release(gnm_value_error_std[i]);
+		gnm_value_error_std[i]=0;
+	}
+
+}
+
+/***************************************************************************/
diff --git a/gnumeric/plugins/excelplugins/plugin.xml.in b/gnumeric/plugins/excelplugins/plugin.xml.in
new file mode 100644
index 0000000..3ab347c
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/plugin.xml.in
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin id="Gnumeric_excelplugins">
+	<information>
+		<_name>Excel plugins</_name>
+		<_description>Adapter interface to load Excel plugins (also known as XLLs). Only operational for worksheet functions that expect only arguments of LPXLOPER type (type 'P' or 'R') and return an LPXLOPER (type 'P' or 'R').</_description>
+	</information>
+	<loader type="Gnumeric_Builtin:module">
+		<attribute name="module_file" value="plugin"/>
+	</loader>
+	<services>
+		<service type="function_group" id="XLLs">
+			<category>gnmFuncCollectionProviderFunc</category>
+		</service>
+	</services>
+</plugin>
diff --git a/gnumeric/plugins/excelplugins/win32replacements.h b/gnumeric/plugins/excelplugins/win32replacements.h
new file mode 100644
index 0000000..70db2e2
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/win32replacements.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#ifndef _WORD_DEFINED
+typedef unsigned short WORD;
+#define _WORD_DEFINED
+#endif
+
+#ifndef _DWORD_DEFINED
+typedef unsigned long DWORD;
+#define _DWORD_DEFINED
+#endif
+
+#ifndef _BOOL_DEFINED
+typedef int BOOL;
+#define _BOOL_DEFINED
+#endif
+
+#ifndef _BYTE_DEFINED
+typedef unsigned char BYTE;
+#define _BYTE_DEFINED
+#endif
+
+#ifndef _LPSTR_DEFINED
+typedef char * LPSTR;
+#define _LPSTR_DEFINED
+#endif
+
+#ifndef FAR
+#define FAR
+#endif
+
+#ifndef far
+#define far
+#endif
+
+#ifndef _HANDLE_DEFINED
+typedef void * HANDLE;
+#define _HANDLE_DEFINED
+#endif
+
+#ifndef _cdecl
+#define _cdecl
+#endif
+
+#ifndef pascal
+#define pascal
+#endif
+
+#if ( defined( WIN32 ) || defined( WIN64 ) ) && ! defined( WINAPI )
+#define WINAPI __attribute__((stdcall))
+#else
+#define WINAPI /* By default, C/C++ calling conventions are used throughout. This can be changed if required. */
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY WINAPI
+#endif
+
+#ifndef LPVOID
+#define LPVOID void*
+#endif
+
+#ifndef TRUE
+#define TRUE                1
+#endif
+
+#ifndef FALSE
+#define FALSE               0
+#endif
diff --git a/gnumeric/plugins/excelplugins/xlcall.h b/gnumeric/plugins/excelplugins/xlcall.h
new file mode 100644
index 0000000..c09b92e
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/xlcall.h
@@ -0,0 +1,971 @@
+/*
+**  Microsoft Excel Developer's Toolkit
+**  Version 5.0
+**
+**  File:           INCLUDE\XLCALL.H
+**  Description:    Header file for for Microsoft Excel callbacks
+**  Platform:       Microsoft Windows
+**
+**  This file defines the constants and
+**  data types which are used in the
+**  Microsoft Excel C API. Include
+**  <windows.h> before you include this.
+**
+*/
+#ifndef xlsdk_xlcall_h
+#define xlsdk_xlcall_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif	/* __cplusplus */
+
+/*
+** XLREF structure
+**
+** Describes a single rectangular reference
+*/
+
+typedef struct xlref
+{
+    WORD rwFirst;
+    WORD rwLast;
+    BYTE colFirst;
+    BYTE colLast;
+} XLREF, FAR *LPXLREF;
+
+
+/*
+** XLMREF structure
+**
+** Describes multiple rectangular references.
+** This is a variable size structure, default
+** size is 1 reference.
+*/
+
+typedef struct xlmref
+{
+    WORD count;
+    XLREF reftbl[1];                        /* actually reftbl[count] */
+} XLMREF, FAR *LPXLMREF;
+
+
+/*
+** XLOPER structure 
+**
+** Excel's fundamental data type: can hold data
+** of any type. Use "R" as the argument type in the 
+** REGISTER function.
+**/
+
+typedef struct xloper 
+{
+    union
+    {
+        double num;                     /* xltypeNum */
+        LPSTR str;                      /* xltypeStr */
+        // bool is a reserved keyword
+        // WORD bool;                      /* xltypeBool */
+        WORD boolean;                      /* xltypeBool */
+        WORD err;                       /* xltypeErr */
+        short int w;                    /* xltypeInt */
+        struct
+        {
+            WORD count;                 /* always = 1 */
+            XLREF ref;
+        } sref;                         /* xltypeSRef */
+        struct
+        {
+            XLMREF far *lpmref;
+            DWORD idSheet;
+        } mref;                         /* xltypeRef */
+        struct
+        {
+            struct xloper far *lparray;
+            WORD rows;
+            WORD columns;
+        } array;                        /* xltypeMulti */
+        struct
+        {
+            union
+            {
+                short int level;        /* xlflowRestart */
+                short int tbctrl;       /* xlflowPause */
+                DWORD idSheet;          /* xlflowGoto */
+            } valflow;
+            WORD rw;                    /* xlflowGoto */
+            BYTE col;                   /* xlflowGoto */
+            BYTE xlflow;
+        } flow;                         /* xltypeFlow */
+        struct
+        {
+            union
+            {
+                BYTE far *lpbData;      /* data passed to XL */
+                HANDLE hdata;           /* data returned from XL */
+            } h;
+            long cbData;
+        } bigdata;                      /* xltypeBigData */
+    } val;
+    WORD xltype;
+} XLOPER, FAR *LPXLOPER;
+
+
+/*
+** XLOPER data types
+**
+** Used for xltype field of XLOPER structure
+*/
+
+#define xltypeNum        0x0001
+#define xltypeStr        0x0002
+#define xltypeBool       0x0004
+#define xltypeRef        0x0008
+#define xltypeErr        0x0010
+#define xltypeFlow       0x0020
+#define xltypeMulti      0x0040
+#define xltypeMissing    0x0080
+#define xltypeNil        0x0100
+#define xltypeSRef       0x0400
+#define xltypeInt        0x0800
+#define xlbitXLFree      0x1000
+#define xlbitDLLFree     0x4000
+
+#define xltypeBigData    (xltypeStr | xltypeInt)
+
+
+/*
+** Error codes
+**
+** Used for val.err field of XLOPER structure
+** when constructing error XLOPERs
+*/
+
+#define xlerrNull    0
+#define xlerrDiv0    7
+#define xlerrValue   15
+#define xlerrRef     23
+#define xlerrName    29
+#define xlerrNum     36
+#define xlerrNA      42
+
+
+/*
+** Flow data types
+**
+** Used for val.flow.xlflow field of XLOPER structure
+** when constructing flow-control XLOPERs
+**/
+
+#define xlflowHalt       1
+#define xlflowGoto       2
+#define xlflowRestart    8
+#define xlflowPause      16
+#define xlflowResume     64
+
+
+/*
+** Function prototypes
+*/
+
+int far _cdecl Excel4(int xlfn, LPXLOPER operRes, int count,... );
+/* followed by count LPXLOPERs */
+
+int far pascal Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER far opers[]);
+int far pascal XLCallVer(void);
+
+
+/*
+** Return codes
+**
+** These values can be returned from Excel4() or Excel4v().
+*/
+
+#define xlretSuccess    0     /* success */
+#define xlretAbort      1     /* macro halted */
+#define xlretInvXlfn    2     /* invalid function number */
+#define xlretInvCount   4     /* invalid number of arguments */
+#define xlretInvXloper  8     /* invalid OPER structure */
+#define xlretStackOvfl  16    /* stack overflow */
+#define xlretFailed     32    /* command failed */
+#define xlretUncalced   64    /* uncalced cell */
+
+
+/*
+** Function number bits
+*/
+
+#define xlCommand    0x8000
+#define xlSpecial    0x4000
+#define xlIntl       0x2000
+#define xlPrompt     0x1000
+
+
+/*
+** Auxiliary function numbers
+**
+** These functions are available only from the C API,
+** not from the Excel macro language.
+*/
+
+#define xlFree             (0  | xlSpecial)
+#define xlStack            (1  | xlSpecial)
+#define xlCoerce           (2  | xlSpecial)
+#define xlSet              (3  | xlSpecial)
+#define xlSheetId          (4  | xlSpecial)
+#define xlSheetNm          (5  | xlSpecial)
+#define xlAbort            (6  | xlSpecial)
+#define xlGetInst          (7  | xlSpecial)
+#define xlGetHwnd          (8  | xlSpecial)
+#define xlGetName          (9  | xlSpecial)
+#define xlEnableXLMsgs     (10 | xlSpecial)
+#define xlDisableXLMsgs    (11 | xlSpecial)
+#define xlDefineBinaryName (12 | xlSpecial)
+#define xlGetBinaryName    (13 | xlSpecial)
+
+
+/*
+** User defined function
+**
+** First argument should be a function reference.
+*/
+
+#define xlUDF      255
+
+
+/*
+** Built-in Functions and Command Equivalents
+*/
+
+
+/* Excel function numbers */
+#define xlfCount 0
+#define xlfIsna 2
+#define xlfIserror 3
+#define xlfSum 4
+#define xlfAverage 5
+#define xlfMin 6
+#define xlfMax 7
+#define xlfRow 8
+#define xlfColumn 9
+#define xlfNa 10
+#define xlfNpv 11
+#define xlfStdev 12
+#define xlfDollar 13
+#define xlfFixed 14
+#define xlfSin 15
+#define xlfCos 16
+#define xlfTan 17
+#define xlfAtan 18
+#define xlfPi 19
+#define xlfSqrt 20
+#define xlfExp 21
+#define xlfLn 22
+#define xlfLog10 23
+#define xlfAbs 24
+#define xlfInt 25
+#define xlfSign 26
+#define xlfRound 27
+#define xlfLookup 28
+#define xlfIndex 29
+#define xlfRept 30
+#define xlfMid 31
+#define xlfLen 32
+#define xlfValue 33
+#define xlfTrue 34
+#define xlfFalse 35
+#define xlfAnd 36
+#define xlfOr 37
+#define xlfNot 38
+#define xlfMod 39
+#define xlfDcount 40
+#define xlfDsum 41
+#define xlfDaverage 42
+#define xlfDmin 43
+#define xlfDmax 44
+#define xlfDstdev 45
+#define xlfVar 46
+#define xlfDvar 47
+#define xlfText 48
+#define xlfLinest 49
+#define xlfTrend 50
+#define xlfLogest 51
+#define xlfGrowth 52
+#define xlfGoto 53
+#define xlfHalt 54
+#define xlfPv 56
+#define xlfFv 57
+#define xlfNper 58
+#define xlfPmt 59
+#define xlfRate 60
+#define xlfMirr 61
+#define xlfIrr 62
+#define xlfRand 63
+#define xlfMatch 64
+#define xlfDate 65
+#define xlfTime 66
+#define xlfDay 67
+#define xlfMonth 68
+#define xlfYear 69
+#define xlfWeekday 70
+#define xlfHour 71
+#define xlfMinute 72
+#define xlfSecond 73
+#define xlfNow 74
+#define xlfAreas 75
+#define xlfRows 76
+#define xlfColumns 77
+#define xlfOffset 78
+#define xlfAbsref 79
+#define xlfRelref 80
+#define xlfArgument 81
+#define xlfSearch 82
+#define xlfTranspose 83
+#define xlfError 84
+#define xlfStep 85
+#define xlfType 86
+#define xlfEcho 87
+#define xlfSetName 88
+#define xlfCaller 89
+#define xlfDeref 90
+#define xlfWindows 91
+#define xlfSeries 92
+#define xlfDocuments 93
+#define xlfActiveCell 94
+#define xlfSelection 95
+#define xlfResult 96
+#define xlfAtan2 97
+#define xlfAsin 98
+#define xlfAcos 99
+#define xlfChoose 100
+#define xlfHlookup 101
+#define xlfVlookup 102
+#define xlfLinks 103
+#define xlfInput 104
+#define xlfIsref 105
+#define xlfGetFormula 106
+#define xlfGetName 107
+#define xlfSetValue 108
+#define xlfLog 109
+#define xlfExec 110
+#define xlfChar 111
+#define xlfLower 112
+#define xlfUpper 113
+#define xlfProper 114
+#define xlfLeft 115
+#define xlfRight 116
+#define xlfExact 117
+#define xlfTrim 118
+#define xlfReplace 119
+#define xlfSubstitute 120
+#define xlfCode 121
+#define xlfNames 122
+#define xlfDirectory 123
+#define xlfFind 124
+#define xlfCell 125
+#define xlfIserr 126
+#define xlfIstext 127
+#define xlfIsnumber 128
+#define xlfIsblank 129
+#define xlfT 130
+#define xlfN 131
+#define xlfFopen 132
+#define xlfFclose 133
+#define xlfFsize 134
+#define xlfFreadln 135
+#define xlfFread 136
+#define xlfFwriteln 137
+#define xlfFwrite 138
+#define xlfFpos 139
+#define xlfDatevalue 140
+#define xlfTimevalue 141
+#define xlfSln 142
+#define xlfSyd 143
+#define xlfDdb 144
+#define xlfGetDef 145
+#define xlfReftext 146
+#define xlfTextref 147
+#define xlfIndirect 148
+#define xlfRegister 149
+#define xlfCall 150
+#define xlfAddBar 151
+#define xlfAddMenu 152
+#define xlfAddCommand 153
+#define xlfEnableCommand 154
+#define xlfCheckCommand 155
+#define xlfRenameCommand 156
+#define xlfShowBar 157
+#define xlfDeleteMenu 158
+#define xlfDeleteCommand 159
+#define xlfGetChartItem 160
+#define xlfDialogBox 161
+#define xlfClean 162
+#define xlfMdeterm 163
+#define xlfMinverse 164
+#define xlfMmult 165
+#define xlfFiles 166
+#define xlfIpmt 167
+#define xlfPpmt 168
+#define xlfCounta 169
+#define xlfCancelKey 170
+#define xlfInitiate 175
+#define xlfRequest 176
+#define xlfPoke 177
+#define xlfExecute 178
+#define xlfTerminate 179
+#define xlfRestart 180
+#define xlfHelp 181
+#define xlfGetBar 182
+#define xlfProduct 183
+#define xlfFact 184
+#define xlfGetCell 185
+#define xlfGetWorkspace 186
+#define xlfGetWindow 187
+#define xlfGetDocument 188
+#define xlfDproduct 189
+#define xlfIsnontext 190
+#define xlfGetNote 191
+#define xlfNote 192
+#define xlfStdevp 193
+#define xlfVarp 194
+#define xlfDstdevp 195
+#define xlfDvarp 196
+#define xlfTrunc 197
+#define xlfIslogical 198
+#define xlfDcounta 199
+#define xlfDeleteBar 200
+#define xlfUnregister 201
+#define xlfUsdollar 204
+#define xlfFindb 205
+#define xlfSearchb 206
+#define xlfReplaceb 207
+#define xlfLeftb 208
+#define xlfRightb 209
+#define xlfMidb 210
+#define xlfLenb 211
+#define xlfRoundup 212
+#define xlfRounddown 213
+#define xlfAsc 214
+#define xlfDbcs 215
+#define xlfRank 216
+#define xlfAddress 219
+#define xlfDays360 220
+#define xlfToday 221
+#define xlfVdb 222
+#define xlfMedian 227
+#define xlfSumproduct 228
+#define xlfSinh 229
+#define xlfCosh 230
+#define xlfTanh 231
+#define xlfAsinh 232
+#define xlfAcosh 233
+#define xlfAtanh 234
+#define xlfDget 235
+#define xlfCreateObject 236
+#define xlfVolatile 237
+#define xlfLastError 238
+#define xlfCustomUndo 239
+#define xlfCustomRepeat 240
+#define xlfFormulaConvert 241
+#define xlfGetLinkInfo 242
+#define xlfTextBox 243
+#define xlfInfo 244
+#define xlfGroup 245
+#define xlfGetObject 246
+#define xlfDb 247
+#define xlfPause 248
+#define xlfResume 251
+#define xlfFrequency 252
+#define xlfAddToolbar 253
+#define xlfDeleteToolbar 254
+#define xlfResetToolbar 256
+#define xlfEvaluate 257
+#define xlfGetToolbar 258
+#define xlfGetTool 259
+#define xlfSpellingCheck 260
+#define xlfErrorType 261
+#define xlfAppTitle 262
+#define xlfWindowTitle 263
+#define xlfSaveToolbar 264
+#define xlfEnableTool 265
+#define xlfPressTool 266
+#define xlfRegisterId 267
+#define xlfGetWorkbook 268
+#define xlfAvedev 269
+#define xlfBetadist 270
+#define xlfGammaln 271
+#define xlfBetainv 272
+#define xlfBinomdist 273
+#define xlfChidist 274
+#define xlfChiinv 275
+#define xlfCombin 276
+#define xlfConfidence 277
+#define xlfCritbinom 278
+#define xlfEven 279
+#define xlfExpondist 280
+#define xlfFdist 281
+#define xlfFinv 282
+#define xlfFisher 283
+#define xlfFisherinv 284
+#define xlfFloor 285
+#define xlfGammadist 286
+#define xlfGammainv 287
+#define xlfCeiling 288
+#define xlfHypgeomdist 289
+#define xlfLognormdist 290
+#define xlfLoginv 291
+#define xlfNegbinomdist 292
+#define xlfNormdist 293
+#define xlfNormsdist 294
+#define xlfNorminv 295
+#define xlfNormsinv 296
+#define xlfStandardize 297
+#define xlfOdd 298
+#define xlfPermut 299
+#define xlfPoisson 300
+#define xlfTdist 301
+#define xlfWeibull 302
+#define xlfSumxmy2 303
+#define xlfSumx2my2 304
+#define xlfSumx2py2 305
+#define xlfChitest 306
+#define xlfCorrel 307
+#define xlfCovar 308
+#define xlfForecast 309
+#define xlfFtest 310
+#define xlfIntercept 311
+#define xlfPearson 312
+#define xlfRsq 313
+#define xlfSteyx 314
+#define xlfSlope 315
+#define xlfTtest 316
+#define xlfProb 317
+#define xlfDevsq 318
+#define xlfGeomean 319
+#define xlfHarmean 320
+#define xlfSumsq 321
+#define xlfKurt 322
+#define xlfSkew 323
+#define xlfZtest 324
+#define xlfLarge 325
+#define xlfSmall 326
+#define xlfQuartile 327
+#define xlfPercentile 328
+#define xlfPercentrank 329
+#define xlfMode 330
+#define xlfTrimmean 331
+#define xlfTinv 332
+#define xlfMovieCommand 334
+#define xlfGetMovie 335
+#define xlfConcatenate 336
+#define xlfPower 337
+#define xlfPivotAddData 338
+#define xlfGetPivotTable 339
+#define xlfGetPivotField 340
+#define xlfGetPivotItem 341
+#define xlfRadians 342
+#define xlfDegrees 343
+#define xlfSubtotal 344
+#define xlfSumif 345
+#define xlfCountif 346
+#define xlfCountblank 347
+#define xlfScenarioGet 348
+#define xlfOptionsListsGet 349
+#define xlfIspmt 350
+#define xlfDatedif 351
+#define xlfDatestring 352
+#define xlfNumberstring 353
+#define xlfRoman 354
+#define xlfOpenDialog 355
+#define xlfSaveDialog 356
+
+/* Excel command numbers */
+#define xlcBeep (0 | xlCommand)
+#define xlcOpen (1 | xlCommand)
+#define xlcOpenLinks (2 | xlCommand)
+#define xlcCloseAll (3 | xlCommand)
+#define xlcSave (4 | xlCommand)
+#define xlcSaveAs (5 | xlCommand)
+#define xlcFileDelete (6 | xlCommand)
+#define xlcPageSetup (7 | xlCommand)
+#define xlcPrint (8 | xlCommand)
+#define xlcPrinterSetup (9 | xlCommand)
+#define xlcQuit (10 | xlCommand)
+#define xlcNewWindow (11 | xlCommand)
+#define xlcArrangeAll (12 | xlCommand)
+#define xlcWindowSize (13 | xlCommand)
+#define xlcWindowMove (14 | xlCommand)
+#define xlcFull (15 | xlCommand)
+#define xlcClose (16 | xlCommand)
+#define xlcRun (17 | xlCommand)
+#define xlcSetPrintArea (22 | xlCommand)
+#define xlcSetPrintTitles (23 | xlCommand)
+#define xlcSetPageBreak (24 | xlCommand)
+#define xlcRemovePageBreak (25 | xlCommand)
+#define xlcFont (26 | xlCommand)
+#define xlcDisplay (27 | xlCommand)
+#define xlcProtectDocument (28 | xlCommand)
+#define xlcPrecision (29 | xlCommand)
+#define xlcA1R1c1 (30 | xlCommand)
+#define xlcCalculateNow (31 | xlCommand)
+#define xlcCalculation (32 | xlCommand)
+#define xlcDataFind (34 | xlCommand)
+#define xlcExtract (35 | xlCommand)
+#define xlcDataDelete (36 | xlCommand)
+#define xlcSetDatabase (37 | xlCommand)
+#define xlcSetCriteria (38 | xlCommand)
+#define xlcSort (39 | xlCommand)
+#define xlcDataSeries (40 | xlCommand)
+#define xlcTable (41 | xlCommand)
+#define xlcFormatNumber (42 | xlCommand)
+#define xlcAlignment (43 | xlCommand)
+#define xlcStyle (44 | xlCommand)
+#define xlcBorder (45 | xlCommand)
+#define xlcCellProtection (46 | xlCommand)
+#define xlcColumnWidth (47 | xlCommand)
+#define xlcUndo (48 | xlCommand)
+#define xlcCut (49 | xlCommand)
+#define xlcCopy (50 | xlCommand)
+#define xlcPaste (51 | xlCommand)
+#define xlcClear (52 | xlCommand)
+#define xlcPasteSpecial (53 | xlCommand)
+#define xlcEditDelete (54 | xlCommand)
+#define xlcInsert (55 | xlCommand)
+#define xlcFillRight (56 | xlCommand)
+#define xlcFillDown (57 | xlCommand)
+#define xlcDefineName (61 | xlCommand)
+#define xlcCreateNames (62 | xlCommand)
+#define xlcFormulaGoto (63 | xlCommand)
+#define xlcFormulaFind (64 | xlCommand)
+#define xlcSelectLastCell (65 | xlCommand)
+#define xlcShowActiveCell (66 | xlCommand)
+#define xlcGalleryArea (67 | xlCommand)
+#define xlcGalleryBar (68 | xlCommand)
+#define xlcGalleryColumn (69 | xlCommand)
+#define xlcGalleryLine (70 | xlCommand)
+#define xlcGalleryPie (71 | xlCommand)
+#define xlcGalleryScatter (72 | xlCommand)
+#define xlcCombination (73 | xlCommand)
+#define xlcPreferred (74 | xlCommand)
+#define xlcAddOverlay (75 | xlCommand)
+#define xlcGridlines (76 | xlCommand)
+#define xlcSetPreferred (77 | xlCommand)
+#define xlcAxes (78 | xlCommand)
+#define xlcLegend (79 | xlCommand)
+#define xlcAttachText (80 | xlCommand)
+#define xlcAddArrow (81 | xlCommand)
+#define xlcSelectChart (82 | xlCommand)
+#define xlcSelectPlotArea (83 | xlCommand)
+#define xlcPatterns (84 | xlCommand)
+#define xlcMainChart (85 | xlCommand)
+#define xlcOverlay (86 | xlCommand)
+#define xlcScale (87 | xlCommand)
+#define xlcFormatLegend (88 | xlCommand)
+#define xlcFormatText (89 | xlCommand)
+#define xlcEditRepeat (90 | xlCommand)
+#define xlcParse (91 | xlCommand)
+#define xlcJustify (92 | xlCommand)
+#define xlcHide (93 | xlCommand)
+#define xlcUnhide (94 | xlCommand)
+#define xlcWorkspace (95 | xlCommand)
+#define xlcFormula (96 | xlCommand)
+#define xlcFormulaFill (97 | xlCommand)
+#define xlcFormulaArray (98 | xlCommand)
+#define xlcDataFindNext (99 | xlCommand)
+#define xlcDataFindPrev (100 | xlCommand)
+#define xlcFormulaFindNext (101 | xlCommand)
+#define xlcFormulaFindPrev (102 | xlCommand)
+#define xlcActivate (103 | xlCommand)
+#define xlcActivateNext (104 | xlCommand)
+#define xlcActivatePrev (105 | xlCommand)
+#define xlcUnlockedNext (106 | xlCommand)
+#define xlcUnlockedPrev (107 | xlCommand)
+#define xlcCopyPicture (108 | xlCommand)
+#define xlcSelect (109 | xlCommand)
+#define xlcDeleteName (110 | xlCommand)
+#define xlcDeleteFormat (111 | xlCommand)
+#define xlcVline (112 | xlCommand)
+#define xlcHline (113 | xlCommand)
+#define xlcVpage (114 | xlCommand)
+#define xlcHpage (115 | xlCommand)
+#define xlcVscroll (116 | xlCommand)
+#define xlcHscroll (117 | xlCommand)
+#define xlcAlert (118 | xlCommand)
+#define xlcNew (119 | xlCommand)
+#define xlcCancelCopy (120 | xlCommand)
+#define xlcShowClipboard (121 | xlCommand)
+#define xlcMessage (122 | xlCommand)
+#define xlcPasteLink (124 | xlCommand)
+#define xlcAppActivate (125 | xlCommand)
+#define xlcDeleteArrow (126 | xlCommand)
+#define xlcRowHeight (127 | xlCommand)
+#define xlcFormatMove (128 | xlCommand)
+#define xlcFormatSize (129 | xlCommand)
+#define xlcFormulaReplace (130 | xlCommand)
+#define xlcSendKeys (131 | xlCommand)
+#define xlcSelectSpecial (132 | xlCommand)
+#define xlcApplyNames (133 | xlCommand)
+#define xlcReplaceFont (134 | xlCommand)
+#define xlcFreezePanes (135 | xlCommand)
+#define xlcShowInfo (136 | xlCommand)
+#define xlcSplit (137 | xlCommand)
+#define xlcOnWindow (138 | xlCommand)
+#define xlcOnData (139 | xlCommand)
+#define xlcDisableInput (140 | xlCommand)
+#define xlcEcho (141 | xlCommand)
+#define xlcOutline (142 | xlCommand)
+#define xlcListNames (143 | xlCommand)
+#define xlcFileClose (144 | xlCommand)
+#define xlcSaveWorkbook (145 | xlCommand)
+#define xlcDataForm (146 | xlCommand)
+#define xlcCopyChart (147 | xlCommand)
+#define xlcOnTime (148 | xlCommand)
+#define xlcWait (149 | xlCommand)
+#define xlcFormatFont (150 | xlCommand)
+#define xlcFillUp (151 | xlCommand)
+#define xlcFillLeft (152 | xlCommand)
+#define xlcDeleteOverlay (153 | xlCommand)
+#define xlcNote (154 | xlCommand)
+#define xlcShortMenus (155 | xlCommand)
+#define xlcSetUpdateStatus (159 | xlCommand)
+#define xlcColorPalette (161 | xlCommand)
+#define xlcDeleteStyle (162 | xlCommand)
+#define xlcWindowRestore (163 | xlCommand)
+#define xlcWindowMaximize (164 | xlCommand)
+#define xlcError (165 | xlCommand)
+#define xlcChangeLink (166 | xlCommand)
+#define xlcCalculateDocument (167 | xlCommand)
+#define xlcOnKey (168 | xlCommand)
+#define xlcAppRestore (169 | xlCommand)
+#define xlcAppMove (170 | xlCommand)
+#define xlcAppSize (171 | xlCommand)
+#define xlcAppMinimize (172 | xlCommand)
+#define xlcAppMaximize (173 | xlCommand)
+#define xlcBringToFront (174 | xlCommand)
+#define xlcSendToBack (175 | xlCommand)
+#define xlcMainChartType (185 | xlCommand)
+#define xlcOverlayChartType (186 | xlCommand)
+#define xlcSelectEnd (187 | xlCommand)
+#define xlcOpenMail (188 | xlCommand)
+#define xlcSendMail (189 | xlCommand)
+#define xlcStandardFont (190 | xlCommand)
+#define xlcConsolidate (191 | xlCommand)
+#define xlcSortSpecial (192 | xlCommand)
+#define xlcGallery3dArea (193 | xlCommand)
+#define xlcGallery3dColumn (194 | xlCommand)
+#define xlcGallery3dLine (195 | xlCommand)
+#define xlcGallery3dPie (196 | xlCommand)
+#define xlcView3d (197 | xlCommand)
+#define xlcGoalSeek (198 | xlCommand)
+#define xlcWorkgroup (199 | xlCommand)
+#define xlcFillGroup (200 | xlCommand)
+#define xlcUpdateLink (201 | xlCommand)
+#define xlcPromote (202 | xlCommand)
+#define xlcDemote (203 | xlCommand)
+#define xlcShowDetail (204 | xlCommand)
+#define xlcUngroup (206 | xlCommand)
+#define xlcObjectProperties (207 | xlCommand)
+#define xlcSaveNewObject (208 | xlCommand)
+#define xlcShare (209 | xlCommand)
+#define xlcShareName (210 | xlCommand)
+#define xlcDuplicate (211 | xlCommand)
+#define xlcApplyStyle (212 | xlCommand)
+#define xlcAssignToObject (213 | xlCommand)
+#define xlcObjectProtection (214 | xlCommand)
+#define xlcHideObject (215 | xlCommand)
+#define xlcSetExtract (216 | xlCommand)
+#define xlcCreatePublisher (217 | xlCommand)
+#define xlcSubscribeTo (218 | xlCommand)
+#define xlcAttributes (219 | xlCommand)
+#define xlcShowToolbar (220 | xlCommand)
+#define xlcPrintPreview (222 | xlCommand)
+#define xlcEditColor (223 | xlCommand)
+#define xlcShowLevels (224 | xlCommand)
+#define xlcFormatMain (225 | xlCommand)
+#define xlcFormatOverlay (226 | xlCommand)
+#define xlcOnRecalc (227 | xlCommand)
+#define xlcEditSeries (228 | xlCommand)
+#define xlcDefineStyle (229 | xlCommand)
+#define xlcLinePrint (240 | xlCommand)
+#define xlcEnterData (243 | xlCommand)
+#define xlcGalleryRadar (249 | xlCommand)
+#define xlcMergeStyles (250 | xlCommand)
+#define xlcEditionOptions (251 | xlCommand)
+#define xlcPastePicture (252 | xlCommand)
+#define xlcPastePictureLink (253 | xlCommand)
+#define xlcSpelling (254 | xlCommand)
+#define xlcZoom (256 | xlCommand)
+#define xlcResume (258 | xlCommand)
+#define xlcInsertObject (259 | xlCommand)
+#define xlcWindowMinimize (260 | xlCommand)
+#define xlcSize (261 | xlCommand)
+#define xlcMove (262 | xlCommand)
+#define xlcSoundNote (265 | xlCommand)
+#define xlcSoundPlay (266 | xlCommand)
+#define xlcFormatShape (267 | xlCommand)
+#define xlcExtendPolygon (268 | xlCommand)
+#define xlcFormatAuto (269 | xlCommand)
+#define xlcGallery3dBar (272 | xlCommand)
+#define xlcGallery3dSurface (273 | xlCommand)
+#define xlcFillAuto (274 | xlCommand)
+#define xlcCustomizeToolbar (276 | xlCommand)
+#define xlcAddTool (277 | xlCommand)
+#define xlcEditObject (278 | xlCommand)
+#define xlcOnDoubleclick (279 | xlCommand)
+#define xlcOnEntry (280 | xlCommand)
+#define xlcWorkbookAdd (281 | xlCommand)
+#define xlcWorkbookMove (282 | xlCommand)
+#define xlcWorkbookCopy (283 | xlCommand)
+#define xlcWorkbookOptions (284 | xlCommand)
+#define xlcSaveWorkspace (285 | xlCommand)
+#define xlcChartWizard (288 | xlCommand)
+#define xlcDeleteTool (289 | xlCommand)
+#define xlcMoveTool (290 | xlCommand)
+#define xlcWorkbookSelect (291 | xlCommand)
+#define xlcWorkbookActivate (292 | xlCommand)
+#define xlcAssignToTool (293 | xlCommand)
+#define xlcCopyTool (295 | xlCommand)
+#define xlcResetTool (296 | xlCommand)
+#define xlcConstrainNumeric (297 | xlCommand)
+#define xlcPasteTool (298 | xlCommand)
+#define xlcPlacement (300 | xlCommand)
+#define xlcFillWorkgroup (301 | xlCommand)
+#define xlcWorkbookNew (302 | xlCommand)
+#define xlcScenarioCells (305 | xlCommand)
+#define xlcScenarioDelete (306 | xlCommand)
+#define xlcScenarioAdd (307 | xlCommand)
+#define xlcScenarioEdit (308 | xlCommand)
+#define xlcScenarioShow (309 | xlCommand)
+#define xlcScenarioShowNext (310 | xlCommand)
+#define xlcScenarioSummary (311 | xlCommand)
+#define xlcPivotTableWizard (312 | xlCommand)
+#define xlcPivotFieldProperties (313 | xlCommand)
+#define xlcPivotField (314 | xlCommand)
+#define xlcPivotItem (315 | xlCommand)
+#define xlcPivotAddFields (316 | xlCommand)
+#define xlcOptionsCalculation (318 | xlCommand)
+#define xlcOptionsEdit (319 | xlCommand)
+#define xlcOptionsView (320 | xlCommand)
+#define xlcAddinManager (321 | xlCommand)
+#define xlcMenuEditor (322 | xlCommand)
+#define xlcAttachToolbars (323 | xlCommand)
+#define xlcVbaReset (324 | xlCommand)
+#define xlcOptionsChart (325 | xlCommand)
+#define xlcStart (326 | xlCommand)
+#define xlcVbaEnd (327 | xlCommand)
+#define xlcVbaInsertFile (328 | xlCommand)
+#define xlcVbaProcedureDefinition (330 | xlCommand)
+#define xlcVbaReferences (331 | xlCommand)
+#define xlcVbaStepInto (332 | xlCommand)
+#define xlcVbaStepOver (333 | xlCommand)
+#define xlcVbaToggleBreakpoint (334 | xlCommand)
+#define xlcVbaClearBreakpoints (335 | xlCommand)
+#define xlcRoutingSlip (336 | xlCommand)
+#define xlcRouteDocument (338 | xlCommand)
+#define xlcMailLogon (339 | xlCommand)
+#define xlcInsertPicture (342 | xlCommand)
+#define xlcEditTool (343 | xlCommand)
+#define xlcGalleryDoughnut (344 | xlCommand)
+#define xlcVbaObjectBrowser (345 | xlCommand)
+#define xlcVbaDebugWindow (346 | xlCommand)
+#define xlcVbaAddWatch (347 | xlCommand)
+#define xlcVbaEditWatch (348 | xlCommand)
+#define xlcVbaInstantWatch (349 | xlCommand)
+#define xlcChartTrend (350 | xlCommand)
+#define xlcPivotItemProperties (352 | xlCommand)
+#define xlcWorkbookInsert (354 | xlCommand)
+#define xlcOptionsTransition (355 | xlCommand)
+#define xlcOptionsGeneral (356 | xlCommand)
+#define xlcFilterAdvanced (370 | xlCommand)
+#define xlcMailAddMailer (373 | xlCommand)
+#define xlcMailDeleteMailer (374 | xlCommand)
+#define xlcMailReply (375 | xlCommand)
+#define xlcMailReplyAll (376 | xlCommand)
+#define xlcMailForward (377 | xlCommand)
+#define xlcMailNextLetter (378 | xlCommand)
+#define xlcDataLabel (379 | xlCommand)
+#define xlcInsertTitle (380 | xlCommand)
+#define xlcFontProperties (381 | xlCommand)
+#define xlcMacroOptions (382 | xlCommand)
+#define xlcWorkbookHide (383 | xlCommand)
+#define xlcWorkbookUnhide (384 | xlCommand)
+#define xlcWorkbookDelete (385 | xlCommand)
+#define xlcWorkbookName (386 | xlCommand)
+#define xlcGalleryCustom (388 | xlCommand)
+#define xlcAddChartAutoformat (390 | xlCommand)
+#define xlcDeleteChartAutoformat (391 | xlCommand)
+#define xlcChartAddData (392 | xlCommand)
+#define xlcAutoOutline (393 | xlCommand)
+#define xlcTabOrder (394 | xlCommand)
+#define xlcShowDialog (395 | xlCommand)
+#define xlcSelectAll (396 | xlCommand)
+#define xlcUngroupSheets (397 | xlCommand)
+#define xlcSubtotalCreate (398 | xlCommand)
+#define xlcSubtotalRemove (399 | xlCommand)
+#define xlcRenameObject (400 | xlCommand)
+#define xlcWorkbookScroll (412 | xlCommand)
+#define xlcWorkbookNext (413 | xlCommand)
+#define xlcWorkbookPrev (414 | xlCommand)
+#define xlcWorkbookTabSplit (415 | xlCommand)
+#define xlcFullScreen (416 | xlCommand)
+#define xlcWorkbookProtect (417 | xlCommand)
+#define xlcScrollbarProperties (420 | xlCommand)
+#define xlcPivotShowPages (421 | xlCommand)
+#define xlcTextToColumns (422 | xlCommand)
+#define xlcFormatCharttype (423 | xlCommand)
+#define xlcLinkFormat (424 | xlCommand)
+#define xlcTracerDisplay (425 | xlCommand)
+#define xlcTracerNavigate (430 | xlCommand)
+#define xlcTracerClear (431 | xlCommand)
+#define xlcTracerError (432 | xlCommand)
+#define xlcPivotFieldGroup (433 | xlCommand)
+#define xlcPivotFieldUngroup (434 | xlCommand)
+#define xlcCheckboxProperties (435 | xlCommand)
+#define xlcLabelProperties (436 | xlCommand)
+#define xlcListboxProperties (437 | xlCommand)
+#define xlcEditboxProperties (438 | xlCommand)
+#define xlcPivotRefresh (439 | xlCommand)
+#define xlcLinkCombo (440 | xlCommand)
+#define xlcOpenText (441 | xlCommand)
+#define xlcHideDialog (442 | xlCommand)
+#define xlcSetDialogFocus (443 | xlCommand)
+#define xlcEnableObject (444 | xlCommand)
+#define xlcPushbuttonProperties (445 | xlCommand)
+#define xlcSetDialogDefault (446 | xlCommand)
+#define xlcFilter (447 | xlCommand)
+#define xlcFilterShowAll (448 | xlCommand)
+#define xlcClearOutline (449 | xlCommand)
+#define xlcFunctionWizard (450 | xlCommand)
+#define xlcAddListItem (451 | xlCommand)
+#define xlcSetListItem (452 | xlCommand)
+#define xlcRemoveListItem (453 | xlCommand)
+#define xlcSelectListItem (454 | xlCommand)
+#define xlcSetControlValue (455 | xlCommand)
+#define xlcSaveCopyAs (456 | xlCommand)
+#define xlcOptionsListsAdd (458 | xlCommand)
+#define xlcOptionsListsDelete (459 | xlCommand)
+#define xlcSeriesAxes (460 | xlCommand)
+#define xlcSeriesX (461 | xlCommand)
+#define xlcSeriesY (462 | xlCommand)
+#define xlcErrorbarX (463 | xlCommand)
+#define xlcErrorbarY (464 | xlCommand)
+#define xlcFormatChart (465 | xlCommand)
+#define xlcSeriesOrder (466 | xlCommand)
+#define xlcMailLogoff (467 | xlCommand)
+#define xlcClearRoutingSlip (468 | xlCommand)
+#define xlcAppActivateMicrosoft (469 | xlCommand)
+#define xlcMailEditMailer (470 | xlCommand)
+#define xlcOnSheet (471 | xlCommand)
+#define xlcStandardWidth (472 | xlCommand)
+#define xlcScenarioMerge (473 | xlCommand)
+#define xlcSummaryInfo (474 | xlCommand)
+#define xlcFindFile (475 | xlCommand)
+#define xlcActiveCellFont (476 | xlCommand)
+#define xlcEnableTipwizard (477 | xlCommand)
+#define xlcVbaMakeAddin (478 | xlCommand)
+#define xlcMailSendMailer (482 | xlCommand)
+
+#ifdef __cplusplus
+}			/* End of extern "C" { */
+#endif	/* __cplusplus */
+
+#endif
diff --git a/gnumeric/plugins/excelplugins/xlcall32_emulation.c b/gnumeric/plugins/excelplugins/xlcall32_emulation.c
new file mode 100644
index 0000000..c36dc32
--- /dev/null
+++ b/gnumeric/plugins/excelplugins/xlcall32_emulation.c
@@ -0,0 +1,87 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * xlcall32_emulation.c:  callback module required by (genuine) Excel plugins (also known as XLLs).
+ *
+ * Author:
+ *   Peter Jaeckel (peter.jaeckel@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* For quick manual building, use something like
+ *
+ *     gcc -fPIC -shared -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include xlcall32_emulation.c -Wl,-soname -Wl,xlcall32.so -o .libs/xlcall32.so
+ * or
+ *     i586-mingw32msvc-gcc -shared -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include xlcall32_emulation.c -Wl,-soname -Wl,xlcall32.dll -o .libs/xlcall32.dll
+ */
+
+#include <gmodule.h>
+
+#if defined( WIN32 ) || defined( WIN64 )
+#include <windef.h>
+#else
+#include "win32replacements.h"
+#endif
+
+G_MODULE_EXPORT void register_actual_excel4v(void*p);
+
+G_MODULE_EXPORT int far _cdecl Excel4(int xlfn, void* operRes, int count, ...);
+
+G_MODULE_EXPORT int far pascal Excel4v(int xlfn, void* operRes, int count, void** opers);
+
+G_MODULE_EXPORT int far pascal XLCallVer(void);
+
+typedef int (*Excel4vFunc)(int xlfn, void* /* XLOper * operRes */, int /*count*/, void** /* XLOper ** opers */ );
+
+static Excel4vFunc actual_excel4v = NULL;
+
+G_MODULE_EXPORT void register_actual_excel4v(void*p){
+        actual_excel4v = (Excel4vFunc)p;
+}
+
+G_MODULE_EXPORT int far pascal Excel4v(int xlfn, void* operRes, int count, void** opers) {
+        if (NULL!=actual_excel4v)
+                return actual_excel4v(xlfn,operRes,count,opers);
+        return -1;
+}
+
+G_MODULE_EXPORT int far _cdecl Excel4(int xlfn, void* operRes, int count, ...) {
+	void **opers=(void**)alloca(count*sizeof(void*));
+	va_list arg_list;
+	int i;
+        va_start(arg_list,count);
+	for (i=0;i<count;++i)
+		opers[i]=va_arg(arg_list,void*);
+	va_end(arg_list);
+	return Excel4v(xlfn,operRes,count,(void**)opers);
+}
+
+int far pascal XLCallVer(void){
+	/*
+	 * From http://msdn.microsoft.com/en-us/library/bb687851.aspx
+	 *
+	 * "You can call this function from any XLL command or function and is thread safe.
+	 *
+	 *  In Excel 97 through Excel 2003, XLCallVer returns 1280 = 0x0500 hex = 5 x 256, which indicates Excel version
+	 *  5. In Excel 2007, it returns 3072 = 0x0c00 hex = 12 x 256, which similarly indicates version 12."
+	 *
+	 */
+	return 1280;
+}
+
+#ifdef WIN32
+asm (".section .drectve");
+asm (".ascii \"-export:Excel4v=Excel4v@16,XLCallVer=XLCallVer@0\"");
+#endif
diff --git a/gnumeric/po-functions/POTFILES.in b/gnumeric/po-functions/POTFILES.in
index a9b7463..1ebc1a3 100644
--- a/gnumeric/po-functions/POTFILES.in
+++ b/gnumeric/po-functions/POTFILES.in
@@ -8,10 +8,11 @@ plugins/excel/ms-excel-read.c
 plugins/excel/ms-excel-write.c
 plugins/excel/ms-formula-read.c
 plugins/excel/ms-formula-write.c
 plugins/excel/xlsx-read.c
 plugins/excel/xlsx-read-pivot.c
+plugins/excelplugins/excelplugins.c
 plugins/fn-christian-date/functions.c
 plugins/fn-complex/functions.c
 plugins/fn-database/functions.c
 plugins/fn-date/functions.c
 plugins/fn-derivatives/options.c
diff --git a/gnumeric/po-functions/POTFILES.skip b/gnumeric/po-functions/POTFILES.skip
index 8b1991c..efa4a85 100644
--- a/gnumeric/po-functions/POTFILES.skip
+++ b/gnumeric/po-functions/POTFILES.skip
@@ -14,10 +14,11 @@ src/go-val.c
 gnumeric.desktop.in
 plugins/applix/plugin.xml.in
 plugins/corba/plugin.xml.in
 plugins/dif/plugin.xml.in
 plugins/excel/plugin.xml.in
+plugins/excelplugins/plugin.xml.in
 plugins/fn-christian-date/plugin.xml.in
 plugins/fn-complex/plugin.xml.in
 plugins/fn-database/plugin.xml.in
 plugins/fn-date/plugin.xml.in
 plugins/fn-derivatives/plugin.xml.in
diff --git a/gnumeric/po/POTFILES.in b/gnumeric/po/POTFILES.in
index 1b116de..91388df 100644
--- a/gnumeric/po/POTFILES.in
+++ b/gnumeric/po/POTFILES.in
@@ -16,10 +16,12 @@ plugins/excel/ms-excel-write.c
 plugins/excel/ms-formula-read.c
 plugins/excel/ms-formula-write.c
 plugins/excel/plugin.xml.in
 plugins/excel/xlsx-read.c
 plugins/excel/xlsx-read-pivot.c
+plugins/excelplugins/excelplugins.c
+plugins/excelplugins/plugin.xml.in
 plugins/fn-christian-date/functions.c
 plugins/fn-christian-date/plugin.xml.in
 plugins/fn-complex/functions.c
 plugins/fn-complex/plugin.xml.in
 plugins/fn-database/functions.c
diff --git a/gnumeric/src/func.h b/gnumeric/src/func.h
index 68c77ba..f0dce88 100644
--- a/gnumeric/src/func.h
+++ b/gnumeric/src/func.h
@@ -26,10 +26,32 @@ struct _GnmFuncGroup {
 GnmFuncGroup *gnm_func_group_get_nth (gint n);
 GnmFuncGroup *gnm_func_group_fetch (char const *name,
 				    char const *translation);
 
 /******************************************************************************/
+/* Support for dynamic loading of collections of functions. Note that the GnmFuncCollection is not for internal storage.
+   It is for the purpose of importing an arbitrary set of functions from external modules without the need to specify
+   any plugin functions in XML. The plugin module needs to provide a function of signature
+        const GnmFuncCollection * gnmFuncCollectionProviderFunc();
+   The name of this function needs to be given as the <category> of a plugin service function group as in
+	<service type="function_group" id="excelplugins">
+		<category>gnmFuncCollectionProviderFunc</category>
+	</service>
+   in plugin.xml of the plugin module. The advantage of this is that the plugin module can import other functions dynamically.
+   Note that the category is only interpreted as a loader function if no translated name and no functions are given in the XML
+   for this function group. Also note that the plugin module function gnmFuncCollectionProviderFunc() must return a NULL
+   terminated array.
+ */
+struct _GnmFuncCollection {
+	const gchar             *category_name;
+	const GnmFuncDescriptor *func_descriptors;
+};
+
+typedef struct _GnmFuncCollection GnmFuncCollection;
+
+/******************************************************************************/
+
 
 /*
  * Function registration routines
  *
  * Functions come in two fashions:  Those that only deal with
diff --git a/gnumeric/src/gnm-plugin.c b/gnumeric/src/gnm-plugin.c
index ef139c6..eee1368 100644
--- a/gnumeric/src/gnm-plugin.c
+++ b/gnumeric/src/gnm-plugin.c
@@ -1,11 +1,12 @@
 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
  * go-plugin-service.c: Plugin services - reading XML info, activating, etc.
  *                   (everything independent of plugin loading method)
  *
- * Author: Zbigniew Chyla (cyba@gnome.pl)
+ * Authors: Zbigniew Chyla (cyba@gnome.pl)
+ *          Peter Jaeckel (XLL plugin loading components, specifically GnmFuncCollection support)
  */
 
 #include <gnumeric-config.h>
 #include "gutils.h"
 #include <tools/gnm-solver.h>
@@ -59,11 +60,11 @@ plugin_service_function_group_finalize (GObject *obj)
 static void
 plugin_service_function_group_read_xml (GOPluginService *service, xmlNode *tree, GOErrorInfo **ret_error)
 {
 	xmlNode *category_node, *translated_category_node, *functions_node;
 	gchar *category_name, *translated_category_name;
-	GSList *function_name_list = NULL;
+	GSList *function_name_list = NULL, *error_list = NULL;
 	gchar *textdomain = NULL;
 
 	GO_INIT_RET_ERROR_INFO (ret_error);
 	category_node = go_xml_get_child_by_name_no_lang (tree, "category");
 	if (category_node != NULL) {
@@ -106,37 +107,41 @@ plugin_service_function_group_read_xml (GOPluginService *service, xmlNode *tree,
 			}
 			GO_SLIST_PREPEND (function_name_list, func_name);
 		}
 		GO_SLIST_REVERSE (function_name_list);
 	}
-	if (category_name != NULL && function_name_list != NULL) {
+	if (category_name != NULL && translated_category_name == NULL && function_name_list == NULL) {
+		/* We allow an empty function name list. This indicates that the "category" is actually the name of a
+		   module that we can call to give us the real category and function list at the time of activation. */
+		PluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
+		sfg->category_name = category_name;
+	} else if (category_name != NULL && function_name_list != NULL) {
 		PluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
 
 		sfg->category_name = category_name;
 		sfg->translated_category_name = translated_category_name;
 		sfg->function_name_list = function_name_list;
 		sfg->textdomain = textdomain;
 	} else {
-		GSList *error_list = NULL;
-
 		if (category_name == NULL) {
 			GO_SLIST_PREPEND (error_list, go_error_info_new_str (
 				_("Missing function category name.")));
 		}
 		if (function_name_list == NULL) {
 			GO_SLIST_PREPEND (error_list, go_error_info_new_str (
 				_("Function group is empty.")));
 		}
-		GO_SLIST_REVERSE (error_list);
-		*ret_error = go_error_info_new_from_error_list (error_list);
-
 		g_free (category_name);
 		g_free (translated_category_name);
 		go_slist_free_custom (function_name_list, g_free);
 
 		g_free (textdomain);
 	}
+	if (error_list != NULL) {
+		GO_SLIST_REVERSE (error_list);
+		*ret_error = go_error_info_new_from_error_list (error_list);
+	}
 }
 
 static gboolean
 plugin_service_function_group_func_desc_load (GnmFunc const *fn_def,
 					      GnmFuncDescriptor *res)
@@ -177,27 +182,44 @@ plugin_service_function_group_func_ref_notify  (GnmFunc *fn_def, int refcount)
 		go_plugin_use_ref (service->plugin);
 	}
 }
 
 static void
+gnm_plugin_module_function_group_load_collection (GOPluginService *service, GOErrorInfo **ret_error);
+
+static void
 plugin_service_function_group_activate (GOPluginService *service, GOErrorInfo **ret_error)
 {
 	PluginServiceFunctionGroup *sfg =
 		GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
 
 	GO_INIT_RET_ERROR_INFO (ret_error);
-	sfg->func_group = gnm_func_group_fetch (sfg->category_name,
-						sfg->translated_category_name);
-	GO_SLIST_FOREACH (sfg->function_name_list, char, fname,
-		GnmFunc *fn_def;
 
-		fn_def = gnm_func_add_stub (
-			sfg->func_group, fname, sfg->textdomain,
-			plugin_service_function_group_func_desc_load,
-			plugin_service_function_group_func_ref_notify);
-		gnm_func_set_user_data (fn_def, service);
-	);
+	/* If only the category name is given, attempt to call a function with that name in the module to retrieve
+	   (actual) category name and function list. */
+	if (NULL!=sfg->category_name && NULL==sfg->translated_category_name && NULL==sfg->function_name_list) {
+
+		go_plugin_load_service (service->plugin, service, ret_error);
+
+		if (NULL==*ret_error)
+			gnm_plugin_module_function_group_load_collection (service,ret_error);
+
+	} else {
+
+		sfg->func_group = gnm_func_group_fetch (sfg->category_name,
+							sfg->translated_category_name);
+		GO_SLIST_FOREACH (sfg->function_name_list, char, fname,
+			GnmFunc *fn_def;
+
+			fn_def = gnm_func_add_stub (
+				sfg->func_group, fname, sfg->textdomain,
+				plugin_service_function_group_func_desc_load,
+				plugin_service_function_group_func_ref_notify);
+			gnm_func_set_user_data (fn_def, service);
+		);
+
+	}
 	service->is_active = TRUE;
 }
 
 static void
 plugin_service_function_group_deactivate (GOPluginService *service, GOErrorInfo **ret_error)
@@ -728,17 +750,26 @@ gnm_plugin_loader_module_load_service_function_group (GOPluginLoader  *loader,
 			                     GINT_TO_POINTER (i));
 		}
 		g_object_set_data_full (
 			G_OBJECT (service), "loader_data", loader_data, function_group_loader_data_free);
 	} else {
-		*ret_error = go_error_info_new_printf (
-		             _("Module file \"%s\" has invalid format."),
-		             loader_module->module_file_name);
-		go_error_info_add_details (*ret_error,
-					go_error_info_new_printf (
-					_("File doesn't contain \"%s\" array."),
-					fn_info_array_name));
+		/* We allow a missing ID_functions symbol in the module if the category_name is defined but the
+		   translated_category_name and function list are empty since this indicates that the "category" is
+		   actually the name of a module that we can call to give us the real category and function list at the
+		   time of activation. */
+		PluginServiceFunctionGroup *sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
+		if (NULL==sfg->category_name ||
+		    NULL!=sfg->translated_category_name ||
+		    NULL!=sfg->function_name_list) {
+			*ret_error = go_error_info_new_printf (
+			             _("Module file \"%s\" has invalid format."),
+			             loader_module->module_file_name);
+			go_error_info_add_details (*ret_error,
+						go_error_info_new_printf (
+						_("File doesn't contain \"%s\" array."),
+						fn_info_array_name));
+		}
 	}
 	g_free (fn_info_array_name);
 }
 
 /*
@@ -910,10 +941,115 @@ GSF_CLASS_FULL (GnmPluginLoaderModule, gnm_plugin_loader_module,
            NULL, GO_TYPE_PLUGIN_LOADER_MODULE, 0,
 	   GSF_INTERFACE (go_plugin_loader_module_iface_init, GO_TYPE_PLUGIN_LOADER))
 
 /****************************************************************************/
 
+static PluginServiceFunctionGroup *
+plugin_service_new_function_group (GOPlugin *plugin, const GnmFuncCollection * func_collection, GOErrorInfo **ret_error)
+{
+	GOPluginServiceCreate ctor = plugin_service_function_group_get_type;
+	PluginServiceFunctionGroup *sfg = NULL;
+	const GnmFuncDescriptor *func_descriptor = NULL;
+	GOPluginService *service = NULL;
+	GSList *function_name_list = NULL;
+
+	g_return_val_if_fail (GO_IS_PLUGIN (plugin), NULL);
+
+	GO_INIT_RET_ERROR_INFO (ret_error);
+
+	func_descriptor = func_collection->func_descriptors;
+	if (NULL!=func_descriptor){
+		service = g_object_new (ctor(), NULL);
+		service->plugin = plugin;
+		service->id = g_strdup (func_collection->category_name);
+		if (service->id == NULL)
+			service->id = g_strdup ("default");
+		/* Populate with function group specific data */
+		sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
+		sfg->category_name = g_strdup (func_collection->category_name);
+		sfg->translated_category_name = NULL;
+		sfg->func_group = gnm_func_group_fetch (sfg->category_name,sfg->translated_category_name);
+		while (NULL!=func_descriptor->name){
+			if (gnm_func_add (sfg->func_group,func_descriptor,NULL)){
+				GO_SLIST_PREPEND (function_name_list, g_strdup(func_descriptor->name));
+			}
+			++func_descriptor;
+		}
+	}
+	if (NULL==function_name_list) {
+                *ret_error = go_error_info_new_printf (_("No functions specified for category %s.\n"),_(func_collection->category_name));
+		g_object_unref(sfg);
+		sfg=0;
+	} else {
+		GO_SLIST_REVERSE(function_name_list);
+		sfg->function_name_list = function_name_list;
+		service->is_active = TRUE;
+	}
+	return sfg;
+}
+
+typedef const GnmFuncCollection * (*GnmFuncCollectionProviderFunc)(void);
+
+static void
+gnm_plugin_module_function_group_load_collection (GOPluginService *service, GOErrorInfo **ret_error)
+{
+	PluginServiceFunctionGroup *sfg = NULL;
+	GnmPluginLoaderModule *loader_module = NULL;
+	GnmFuncCollectionProviderFunc provider = NULL;
+	GSList *error_list = NULL;
+
+	g_return_if_fail (IS_GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service));
+
+	sfg = GNM_PLUGIN_SERVICE_FUNCTION_GROUP (service);
+	loader_module = GNM_PLUGIN_LOADER_MODULE (go_plugin_get_loader(service->plugin));
+
+	GO_INIT_RET_ERROR_INFO (ret_error);
+	g_module_symbol (loader_module->handle, sfg->category_name, (gpointer) &provider);
+	if (NULL==provider) {
+		gchar * message = g_strconcat (_("Cannot find function collection provider function"),
+					         " \"",_(sfg->category_name),
+					         "\" ",_("in module")," \"",loader_module->module_file_name,"\".",
+					         NULL);
+		GO_SLIST_PREPEND (error_list, go_error_info_new_str (message));
+		g_free(message);
+	} else {
+		const GnmFuncCollection * theCollection = provider();
+		if (NULL!=theCollection) {
+			/* We attach new services to the existing service list in this plugin.
+			   At least one service is guaranteed to be in it already, namely the service function group
+			   that was the proxy for this function collection in the first place. */
+			GSList *service_list_tail = g_slist_last (go_plugin_get_services(service->plugin));
+			g_assert (NULL != service_list_tail);
+			g_assert (NULL == service_list_tail->next);
+			while (NULL!=theCollection->category_name) {
+				// Create a function group for this category
+				GOErrorInfo *error=0;
+				PluginServiceFunctionGroup *additional_service_function_group
+					= plugin_service_new_function_group (service->plugin, theCollection, &error);
+				if (NULL!=error){
+					GO_SLIST_PREPEND (error_list, error);
+					g_object_unref(additional_service_function_group);
+					additional_service_function_group=0;
+				} else {
+					service_list_tail->next = g_slist_append(NULL,additional_service_function_group);
+					service_list_tail = service_list_tail->next;
+				}
+				++theCollection;
+			}
+		}
+	}
+	g_free(sfg->category_name);
+	sfg->category_name = NULL;
+	if (error_list != NULL) {
+		GO_SLIST_REVERSE (error_list);
+		*ret_error = go_error_info_new_from_error_list (error_list);
+	}
+
+}
+
+/****************************************************************************/
+
 void
 gnm_plugins_init (GOCmdContext *context)
 {
 	char const *env_var;
 	GSList *dir_list = go_slist_create (
