/* mswdynld - Dynamic loading and C function calling routines. */
/* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney */
/* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz */
/* You may give out copies of this software; for conditions see the */
/* file COPYING included with this distribution. */
/* Calling conventions are based on the conventions given in the New S */
/* book. */
#include "xlisp.h"
#include "xlstat.h"
#include "xlsx.h"
extern LVAL s_dll_list;
/************************************************************************/
/** **/
/** DLL Handling Functions **/
/** **/
/************************************************************************/
LVAL xsload_dll()
{
char *dllname;
HANDLE hdll;
LVAL dllhandle, dll_list;
#ifdef DODO
{
HANDLE h1,h2;
h1 = LoadLibrary("foo.dll");
h2 = LoadLibrary("simplex.dll");
FreeLibrary(h1);
FreeLibrary(h2);
return(NIL);
}
#endif DODO
dllname = (char *) getstring(xlgastring());
xllastarg();
hdll = LoadLibrary(dllname);
if ((UINT) hdll >= 32) {
xlsave1(dllhandle);
dllhandle = cvfixnum((FIXTYPE) hdll);
dll_list = consp(getvalue(s_dll_list)) ? getvalue(s_dll_list) : NIL;
setvalue(s_dll_list, cons(dllhandle, dll_list));
xlpop();
#ifdef MULVALS
xlnumresults = 1;
xlresults[0] = dllhandle;
#endif
return(dllhandle);
}
else {
#ifdef MULVALS
xlnumresults = 2;
xlresults[0] = NIL;
xlresults[1] = cvfixnum((FIXTYPE) hdll);
#endif
return NIL;
}
}
LVAL xsfree_dll()
{
HANDLE hdll;
LVAL last, list;
hdll = (HANDLE) getfixnum(xlgafixnum());
xllastarg();
// SysBeep(10);
// return(NIL);
if ((UINT) hdll >= 32) {
for (last = NIL, list = getvalue(s_dll_list);
consp(list);
last = list, list = cdr(list)) {
//*** because of some bug, on a 386SX20 at least, frees MUST be done
//*** on a last in-first out basis. So only the head of the list
//*** can be freed. Hence the following line:
if (consp(last)) break;
if (hdll == (HANDLE) getfixnum(car(list))) {
if (consp(last)) cdr(last) = cdr(list);
else setvalue(s_dll_list, cdr(list));
FreeLibrary(hdll);
break;
}
}
}
return(NIL);
}
void MSWDLLCleanup()
{
LVAL list;
if (s_dll_list != NULL)
for (list = getvalue(s_dll_list); consp(list); list = cdr(list))
FreeLibrary((HANDLE) getfixnum(car(list)));
}
/************************************************************************/
/** **/
/** Allocation and Error Signalling Functions **/
/** **/
/************************************************************************/
static LVAL current_allocs = NULL;
#define fixup_current_allocs \
{ if (current_allocs == NULL) current_allocs = NIL; }
/* allocate space that will be garbage collected after return */
static char *xscall_alloc(int n, int m)
{
LVAL adata;
char *p;
fixup_current_allocs;
adata = newadata(n, m, FALSE);
if (adata == NIL || (p = getadaddr(adata)) == NULL)
xlfail("allocation failed");
current_allocs = cons(adata, current_allocs);
return(p);
}
/************************************************************************/
/** **/
/** Lisp to C/FORTRAN Data Conversion **/
/** **/
/************************************************************************/
#define IN 0
#define RE 1
typedef struct {
int type, size;
char *addr;
} call_arg;
/* convert lisp argument to allocated pointer */
static call_arg lisp2arg(LVAL x)
{
call_arg a;
LVAL elem, data;
int i;
xlprot1(x);
/* make sure x is a sequence and find its length */
if (! seqp(x)) x = consa(x);
a.size = seqlen(x);
/* determine the mode of the data */
for (i = 0, a.type = IN, data = x; i < a.size; i++) {
elem = getnextelement(&data, i);
if (floatp(elem)) a.type = RE;
#ifdef BIGNUMS
else if (ratiop(elem)) a.type = RE;
#endif
else if (! integerp(elem)) xlerror("not a real number", elem);
}
/* allocate space for the data */
a.addr = xscall_alloc(a.size, (a.type == IN) ? sizeof(int) : sizeof(double));
/* fill the space */
for (i = 0, data = x; i < a.size; i++) {
elem = getnextelement(&data, i);
if (a.type == IN) ((int *) a.addr)[i] = getfixnum(elem);
else ((double *) a.addr)[i] = makefloat(elem);
}
xlpop();
return(a);
}
/* copy allocated pointer back to new lisp list */
static LVAL arg2lisp(call_arg a)
{
LVAL x, next;
int i;
xlsave1(x);
x = mklist(a.size, NIL);
for (i = 0, next = x; i < a.size; i++, next = cdr(next)) {
if (a.type == IN) rplaca(next, cvfixnum((FIXTYPE) ((int *) a.addr)[i]));
else rplaca(next, cvflonum((FLOTYPE) ((double *) a.addr)[i]));
}
xlpop();
return(x);
}
/************************************************************************/
/** **/
/** Foreign Function Call Function **/
/** **/
/************************************************************************/
typedef void VFUN(XLSXblock *);
typedef VFUN *VFPTR;
LVAL xscall_cfun()
{
LVAL result, Lfun, old_allocs, next;
call_arg *args, *pargs;
int nargs, i;
VFPTR routine;
HANDLE hdll;
char *name;
XLSXblock params;
fixup_current_allocs;
// ### patch this to handle errors properly (reset allocs, etc) -- use dynamic scoping
// ### also use allocation with free as in (new) linalg?
xlstkcheck(3);
xlsave(old_allocs);
xlprotect(current_allocs);
xlsave(result);
old_allocs = current_allocs;
current_allocs = NIL;
/* get the routine pointer */
hdll = (HANDLE) getfixnum(xlgafixnum());
Lfun = xlgetarg();
if (stringp(Lfun)) name = getstring(Lfun);
else if (fixp(Lfun)) name = (char *) MAKEINTRESOURCE((int) getfixnum(Lfun));
routine = (VFPTR) GetProcAddress(hdll, name);
if (! routine) xlerror("can't find function address", Lfun);
/* convert the arguments to allocated pointers */
nargs = xlargc;
if (nargs == 0) xlfail("too few arguments");
args = (call_arg *) xscall_alloc(nargs, sizeof(call_arg));
params.argc = nargs;
params.argv = (char **) xscall_alloc(nargs, sizeof(char *));
for (i = 0; i < nargs; i++) {
args[i] = lisp2arg(xlgetarg());
params.argv[i] = args[i].addr;
}
/* make the call */
(*routine)(¶ms);
/* convert the pointers back to lists, grouped in a list */
result = (nargs > 0) ? mklist(nargs, NIL) : NIL;
for (next = result, pargs = args; consp(next); next = cdr(next), pargs++)
rplaca(next, arg2lisp(*pargs));
current_allocs = old_allocs;
xlpopn(3);
return(result);
}
syntax highlighted by Code2HTML, v. 0.9.1