// This file is part of SmallBASIC
//
// SmallBASIC - External library support (modules)
//
// This program is distributed under the terms of the GPL v2.0 or later
// Download the GNU Public License (GPL) from www.gnu.org
//
// Copyright(C) 2001 Nicholas Christopoulos
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#if defined(__CYGWIN__)
#include <w32api/windows.h>
#include <sys/cygwin.h>
#define WIN_EXTLIB
#elif defined(__MINGW32__)
#include <windows.h>
#define WIN_EXTLIB
#endif
#include "common/smbas.h"
#include "common/extlib.h"
#include "common/pproc.h"
#if defined(__linux__) && defined(_UnixOS)
#define LNX_EXTLIB
#endif
#ifdef LNX_EXTLIB
#define LIB_EXT ".so"
#else
#define LIB_EXT ".dll"
#endif
#if defined(LNX_EXTLIB)
#include <dlfcn.h>
#endif
#include <dirent.h>
#if defined(OS_LIMITED)
#define MAX_SLIB_N 16
#else
#define MAX_SLIB_N 256
#endif
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
/* --- Global Symbol Table ----------------------------------------------------------------------------------------------- */
static slib_t slib_table[MAX_SLIB_N]; /**< module index */
static int slib_count; /**< module count */
static ext_proc_node_t *extproctable; /**< external procedure table */
static int extprocsize; /**< ext-proc table allocated size */
static int extproccount; /**< ext-proc table count */
static ext_func_node_t *extfunctable; /**< external function table */
static int extfuncsize; /**< ext-func table allocated size */
static int extfunccount; /**< ext-func table count */
/*
* reset the external proc/func lists
*/
static void slib_reset_externals(void) {
// reset functions
if (extfunctable) {
tmp_free(extfunctable);
}
extfunctable = NULL;
extfunccount = extfuncsize = 0;
// reset procedures
if (extproctable) {
tmp_free(extproctable);
}
extproctable = NULL;
extproccount = extprocsize = 0;
}
/*
* add an external procedure to the list
*/
static int slib_add_external_proc(const char *proc_name, int lib_id) {
// TODO: scan for conflicts
char buf[256];
sprintf(buf, "%s.%s", slib_table[lib_id].name, proc_name);
strupper(buf);
if (extproctable == NULL) {
extprocsize = 16;
extproctable =
(ext_proc_node_t *) tmp_alloc(sizeof(ext_proc_node_t) * extprocsize);
}
else if (extprocsize <= (extproccount + 1)) {
extprocsize += 16;
extproctable =
(ext_proc_node_t *) tmp_realloc(extproctable,
sizeof(ext_proc_node_t) * extprocsize);
}
extproctable[extproccount].lib_id = lib_id;
extproctable[extproccount].symbol_index = 0;
strcpy(extproctable[extproccount].name, buf);
strupper(extproctable[extproccount].name);
if (opt_verbose)
printf("LID: %d, Idx: %d, PROC '%s'\n", lib_id, extproccount,
extproctable[extproccount].name);
extproccount++;
return extproccount - 1;
}
/*
* Add an external function to the list
*/
static int slib_add_external_func(const char *func_name, int lib_id) {
char buf[256];
sprintf(buf, "%s.%s", slib_table[lib_id].name, func_name);
strupper(buf);
// TODO: scan for conflicts
if (extfunctable == NULL) {
extfuncsize = 16;
extfunctable =
(ext_func_node_t *) tmp_alloc(sizeof(ext_func_node_t) * extfuncsize);
}
else if (extfuncsize <= (extfunccount + 1)) {
extfuncsize += 16;
extfunctable =
(ext_func_node_t *) tmp_realloc(extfunctable,
sizeof(ext_func_node_t) * extfuncsize);
}
extfunctable[extfunccount].lib_id = lib_id;
extfunctable[extfunccount].symbol_index = 0;
strcpy(extfunctable[extfunccount].name, buf);
strupper(extfunctable[extfunccount].name);
if (opt_verbose)
printf("LID: %d, Idx: %d, FUNC '%s'\n", lib_id, extfunccount,
extfunctable[extfunccount].name);
extfunccount++;
return extfunccount - 1;
}
/*
* returns the external procedure id
*/
static int slib_is_external_proc(const char *name) {
int i;
for (i = 0; i < extproccount; i++) {
if (strcmp(extproctable[i].name, name) == 0)
return i;
}
return -1;
}
/*
* returns the external function id
*/
static int slib_is_external_func(const char *name) {
int i;
for (i = 0; i < extfunccount; i++) {
if (strcmp(extfunctable[i].name, name) == 0)
return i;
}
return -1;
}
#endif
/**
* returns the ID of the keyword
*/
int slib_get_kid(const char *name) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int i;
for (i = 0; i < extproccount; i++) {
if (strcmp(extproctable[i].name, name) == 0)
return i;
}
for (i = 0; i < extfunccount; i++) {
if (strcmp(extfunctable[i].name, name) == 0)
return i;
}
#endif
return -1;
}
/**
* returns the library-id (index of library of the current process)
*/
int slib_get_module_id(const char *name) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int i;
char xname[OS_FILENAME_SIZE + 1];
slib_t *lib;
strcpy(xname, name);
strcat(xname, LIB_EXT);
for (i = 0; i < slib_count; i++) {
lib = &slib_table[i];
// printf("slib: %s=%s\n", lib->name, name);
if (strcasecmp(lib->name, name) == 0)
return i;
}
#endif
return -1; // not found
}
/**
* updates compiler with the module's keywords
*/
void slib_setup_comp(int mid) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int i;
for (i = 0; i < extproccount; i++) {
if (extproctable[i].lib_id == mid)
comp_add_external_proc(extproctable[i].name, mid);
}
for (i = 0; i < extfunccount; i++) {
if (extfunctable[i].lib_id == mid)
comp_add_external_func(extfunctable[i].name, mid);
}
#endif
}
/* --- System Load/Execute ----------------------------------------------------------------------------------------------- */
/*
* retrieve the function pointer
*/
void *slib_getoptptr(slib_t * lib, const char *name) {
#if defined(LNX_EXTLIB)
return dlsym(lib->handle, name);
#elif defined(WIN_EXTLIB)
return GetProcAddress((HMODULE) lib->handle, name);
#else
return NULL;
#endif
}
/*
* retrieve the function pointer; error if its not exists
*/
void *slib_getptr(slib_t * lib, const char *name) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
void *ptr;
ptr = slib_getoptptr(lib->handle, name);
if (!ptr)
panic("SB-LibMgr: %s, missing function %s\n", lib->name, name);
return ptr;
#else
return NULL;
#endif
}
/*
* open library (basic open)
*/
int slib_llopen(slib_t * lib) {
#if defined(LNX_EXTLIB)
lib->handle = dlopen(lib->fullname, RTLD_NOW);
if (lib->handle == NULL)
panic("SB-LibMgr: error on loading %s\n%s", lib->name, dlerror());
return (lib->handle != NULL);
#elif defined(__CYGWIN__)
char win32Path[1024];
cygwin_conv_to_full_win32_path(lib->fullname, win32Path);
lib->handle = LoadLibrary(win32Path);
if (lib->handle == NULL)
panic("SB-LibMgr: error on loading %s\n", win32Path);
return (lib->handle != NULL);
#elif defined(WIN_EXTLIB)
lib->handle = LoadLibrary(lib->fullname);
if (lib->handle == NULL)
panic("SB-LibMgr: error on loading %s\n", lib->name);
return (lib->handle != NULL);
#else
return 0;
#endif
}
/*
* close library (basic close)
*/
int slib_llclose(slib_t * lib) {
#if defined(LNX_EXTLIB)
if (!lib->handle)
return 0;
dlclose(lib->handle);
lib->handle = NULL;
return 1;
#elif defined(WIN_EXTLIB)
if (!lib->handle)
return 0;
FreeLibrary(lib->handle);
lib->handle = NULL;
return 1;
#else
return 1;
#endif
}
/*
*/
void slib_import_routines(slib_t * lib) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int i, count;
char buf[SB_KEYWORD_SIZE];
int (*fgetname) (int, char *);
int (*fcount) (void);
lib->first_proc = extproccount;
lib->first_func = extfunccount;
fcount = (int (*)(void))slib_getoptptr(lib, "sblib_proc_count");
fgetname = (int (*)(int, char *))slib_getoptptr(lib, "sblib_proc_getname");
if (fcount) {
count = fcount();
for (i = 0; i < count; i++) {
if (fgetname(i, buf))
slib_add_external_proc(buf, lib->id);
}
}
fcount = (int (*)(void))slib_getoptptr(lib, "sblib_func_count");
fgetname = (int (*)(int, char *))slib_getoptptr(lib, "sblib_func_getname");
if (fcount) {
count = fcount();
for (i = 0; i < count; i++) {
if (fgetname(i, buf))
slib_add_external_func(buf, lib->id);
}
}
#endif
}
/*
* load a lib
*/
void slib_import(const char *name, const char *fullname) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
slib_t *lib;
int (*minit) (void);
int (*mtype) (void);
void (*mdrvname) (char *);
int mok = 0;
int name_index = 0;
if (strncmp(name, "lib", 3) == 0) {
// libmysql -> store mysql
name_index = 3;
}
lib = &slib_table[slib_count];
memset(lib, 0, sizeof(slib_t));
strncpy(lib->name, name + name_index, 255);
strncpy(lib->fullname, fullname, 1023);
lib->id = slib_count;
if (!opt_quiet)
printf("SB-LibMgr: importing %s", fullname);
if (slib_llopen(lib)) {
mok = 1;
// init
minit = (int (*)(void))slib_getoptptr(lib, "sblib_init");
if (minit) {
if (!minit()) {
mok = 0;
panic("SB-LibMgr: %s->sblib_init(), failed", lib->name);
}
}
// get type
mtype = (int (*)(void))slib_getoptptr(lib, "sblib_type");
if (mtype) {
lib->type = mtype();
} else {
lib->type = lib_lang_ext; // default type
}
// get info
switch (lib->type) {
case lib_lang_ext:
slib_import_routines(lib);
mok = 1;
break;
case lib_vfs_driver:
mdrvname = (void (*)(char *))slib_getptr(lib, "sblib_vfsname");
memset(lib->vfs_drvname, 0, 5);
mdrvname(lib->vfs_drvname);
mok = 1;
break;
default:
panic("SB-LibMgr: %s->sbmod_type(), type %d is not supported", lib->type);
mok = 0;
};
}
else {
printf("SB-LibMgr: can't open %s", fullname);
}
if (mok) {
slib_count++;
if (!opt_quiet) {
printf("... done\n");
}
}
else {
if (!opt_quiet) {
printf("... error\n");
}
}
#endif
}
/*
* scan libraries
*/
void sblmgr_scanlibs(const char *path) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
DIR *dp;
struct dirent *e;
char *name, *p;
char full[1024], libname[256];
if ((dp = opendir(path)) == NULL) {
if (!opt_quiet) {
printf("SB-LibMgr: module path %s not found.\n", path);
}
return;
}
while ((e = readdir(dp)) != NULL) {
name = e->d_name;
if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) {
continue;
}
if ((p = strstr(name, LIB_EXT)) != NULL) {
if (strcmp(p, LIB_EXT) == 0) {
// store it
strcpy(libname, name);
p = strchr(libname, '.');
*p = '\0';
strcpy(full, path);
if (path[strlen(path) - 1] != '/') {
strcat(full, "/"); // add trailing separator
}
strcat(full, name);
slib_import(libname, full);
}
}
}
closedir(dp);
#endif
}
/*
* slib-manager: initialize manager
*/
void sblmgr_init(int mcount, const char *mlist) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int all = 0;
slib_count = 0;
if (!opt_quiet && mcount) {
printf("SB-LibMgr: scanning for modules...\n");
}
if (mcount) {
if (mlist) {
if (strlen(mlist) == 0) {
all = 1;
} else {
all = 1;
}
// TODO: else load the specified modules
}
else {
all = 1;
}
if (all) {
// the -m argument specifies the location of all modules
#if defined(LNX_EXTLIB) && !defined(PACKAGE_LIB_DIR)
sblmgr_scanlibs(opt_modlist);
#elif defined(LNX_EXTLIB)
sblmgr_scanlibs(opt_modlist[0] ? opt_modlist : PACKAGE_LIB_DIR);
#elif defined(__CYGWIN__) || defined(__MINGW32__)
sblmgr_scanlibs(opt_modlist);
#elif defined(WIN_EXTLIB)
sblmgr_scanlibs("c:\\sbasic\\modules");
sblmgr_scanlibs("sbasic\\modules");
#endif
}
}
if (!opt_quiet) {
printf("\n");
}
#endif
}
/*
* slib-manager: close everything
*/
void sblmgr_close() {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int i;
slib_t *lib;
void (*mclose) (void);
for (i = 0; i < slib_count; i++) {
lib = &slib_table[i];
if (lib->handle) {
mclose = (void (*)(void))slib_getoptptr(lib, "sblib_close");
if (mclose)
mclose();
slib_llclose(lib);
}
}
#endif
}
/**
* @ingroup mod
*
* search modules for a vfsmodule with that driver-name.
*
* @param name the name of the driver (char[5])
* @return lib-id on success; otherwise -1
*/
int sblmgr_getvfs(const char *name) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int i;
slib_t *lib;
for (i = 0; i < slib_count; i++) {
lib = &slib_table[i];
if (lib->type == lib_vfs_driver) {
if (strncmp(lib->vfs_drvname, name, 5) == 0)
return i;
}
}
#endif
return -1;
}
/*
* returns the 'index' function-name of the 'lib'
*/
int sblmgr_getfuncname(int lib_id, int index, char *buf) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
slib_t *lib;
int (*mgf) (int, char *);
buf[0] = '\0';
if (lib_id < 0 || lib_id >= slib_count)
return 0; // error
lib = &slib_table[lib_id];
mgf = (int (*)(int, char *))slib_getoptptr(lib, "sblib_func_getname");
if (mgf == NULL)
return 0;// error
return mgf(index, buf);
#else
return 0;
#endif
}
/*
* returns the 'index' procedure-name of the 'lib'
*/
int sblmgr_getprocname(int lib_id, int index, char *buf) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
slib_t *lib;
int (*mgp) (int, char *);
buf[0] = '\0';
if (lib_id < 0 || lib_id >= slib_count)
return 0; // error
lib = &slib_table[lib_id];
mgp = (int (*)(int, char *))slib_getoptptr(lib, "sblib_proc_getname");
if (mgp == NULL)
return 0;// error
return mgp(index, buf);
#else
return 0;
#endif
}
/*
* build parameter table
*/
int slib_build_ptable(slib_par_t * ptable) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int pcount = 0;
var_t *arg = NULL;
byte ready, code;
addr_t ofs;
if (code_peek() == kwTYPE_LEVEL_BEGIN) {
code_skipnext(); // kwTYPE_LEVEL_BEGIN
ready = 0;
do {
code = code_peek();
switch (code) {
case kwTYPE_EOC:
code_skipnext();
break;
case kwTYPE_SEP: // separator
code_skipsep();
break;
case kwTYPE_LEVEL_END:// ) -- end of parameters
ready = 1;
break;
case kwTYPE_VAR:// variable
ofs = prog_ip;// store IP
if (code_isvar()) {
// push parameter
ptable[pcount].var_p = code_getvarptr();
ptable[pcount].byref = 1;
pcount++;
break;
}
prog_ip = ofs; // restore IP
// no 'break' here
default:
// default --- expression (BYVAL ONLY)
arg = v_new();
eval(arg);
if (!prog_error) {
// push parameter
ptable[pcount].var_p = arg;
ptable[pcount].byref = 0;
pcount++;
}
else {
v_free(arg);
tmp_free(arg);
return pcount;
}
}
}while (!ready);
code_skipnext(); // kwTYPE_LEVEL_END
}
return pcount;
#else
return 0;
#endif
}
/*
* free parameter table
*/
void slib_free_ptable(slib_par_t * ptable, int pcount) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
int i;
for (i = 0; i < pcount; i++) {
if (ptable[i].byref == 0) {
v_free(ptable[i].var_p);
tmp_free(ptable[i].var_p);
}
}
#endif
}
/*
* execute a procedure
*/
int sblmgr_procexec(int lib_id, int index) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
slib_t *lib;
var_t ret;
slib_par_t *ptable = NULL;
int (*pexec) (int, int, slib_par_t *, var_t *);
int pcount = 0;
int success = 0;
if (lib_id < 0 || lib_id >= slib_count) {
// rt_raise(...)
return 0;// error
}
lib = &slib_table[lib_id];
if (lib->type == lib_vfs_driver)
pexec = slib_getoptptr(lib, "sblib_vfs_exec");
else
pexec = slib_getoptptr(lib, "sblib_proc_exec");
if (pexec == NULL) {
// rt_raise(...)
return 0;// error
}
// build parameter table
ptable = tmp_alloc(sizeof(slib_par_t) * 64);// 64 = maximum parameter
pcount = slib_build_ptable(ptable);
if (prog_error) {
slib_free_ptable(ptable, pcount);
return 0;
}
// exec
v_init(&ret);
success = pexec(index - lib->first_proc, pcount, ptable, &ret);
// error
if (!success) {
if (ret.type == V_STR)
rt_raise("SB-LibMgr:\n%s: %s\n", lib->name, ret.v.p.ptr);
else
rt_raise("SB-LibMgr:\n%s: not specified error\n", lib->name);
}
// clean-up
if (ptable) {
slib_free_ptable(ptable, pcount);
tmp_free(ptable);
}
v_free(&ret);
return success;
#else
return 0;
#endif
}
/*
* execute a function
*/
int sblmgr_funcexec(int lib_id, int index, var_t * ret) {
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
slib_t *lib;
slib_par_t *ptable = NULL;
int (*pexec) (int, int, slib_par_t *, var_t *);
int pcount = 0;
int success = 0;
if (lib_id < 0 || lib_id >= slib_count) {
// rt_raise(...)
return 0;// error
}
lib = &slib_table[lib_id];
if (lib->type == lib_vfs_driver)
pexec = slib_getoptptr(lib, "sblib_vfs_exec");
else
pexec = slib_getoptptr(lib, "sblib_func_exec");
if (pexec == NULL) {
// rt_raise(...)
return 0;// error
}
// build parameter table
ptable = tmp_alloc(sizeof(slib_par_t) * 64);// 64 = maximum parameter
pcount = slib_build_ptable(ptable);
if (prog_error) {
slib_free_ptable(ptable, pcount);
return 0;
}
// exec
v_init(ret);
success = pexec(index - lib->first_func, pcount, ptable, ret);
// error
if (!success) {
if (ret->type == V_STR)
rt_raise("SB-LibMgr:\n%s: %s\n", lib->name, ret->v.p.ptr);
else
rt_raise("SB-LibMgr:\n%s: (error not specified)\n", lib->name);
}
// clean-up
if (ptable) {
slib_free_ptable(ptable, pcount);
tmp_free(ptable);
}
return success;
#else
return 0;
#endif
}
/**
* @ingroup mod
*
* executes a vfs standard function
*
* @param func the function's index
* @param f the file structure
* @return it is depended on 'func'
*/
long sblmgr_vfsexec(enum slib_vfs_idx_t func, dev_file_t * f, ...)
{
va_list ap;
long retval = 0;
va_start(ap, f);
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
{
slib_t *lib;
lib = &slib_table[f->vfslib];
switch (func) {
case lib_vfs_open:
{
int (*func) (const char *name, int flags);
func = slib_getoptptr(lib->handle, "vfs_open");
if (func) {
f->last_error = f->handle = func(f->name + 5, f->open_flags);
retval = (f->handle >= 0);
}
}
break;
case lib_vfs_close:
{
int (*func) (int handle);
func = slib_getoptptr(lib->handle, "vfs_close");
if (func) {
retval = func(f->handle);
f->handle = -1;
}
}
break;
case lib_vfs_read:
{
long (*func) (int handle, char *data, long size);
byte *data;
dword size;
data = va_arg(ap, byte *);
size = va_arg(ap, dword);
func = slib_getoptptr(lib->handle, "vfs_read");
if (func)
retval = (func(f->handle, data, size) == size);
}
break;
case lib_vfs_write:
{
long (*func) (int handle, char *data, long size);
byte *data;
dword size;
data = va_arg(ap, byte *);
size = va_arg(ap, dword);
func = slib_getoptptr(lib->handle, "vfs_write");
if (func)
retval = (func(f->handle, data, size) == size);
}
break;
case lib_vfs_eof:
{
int (*func) (int handle);
func = slib_getoptptr(lib->handle, "vfs_eof");
if (func)
retval = func(f->handle);
}
case lib_vfs_tell:
{
long (*func) (int handle);
func = slib_getoptptr(lib->handle, "vfs_tell");
if (func)
retval = func(f->handle);
}
case lib_vfs_length:
{
long (*func) (int handle);
func = slib_getoptptr(lib->handle, "vfs_length");
if (func)
retval = func(f->handle);
}
case lib_vfs_seek:
{
long (*func) (int handle, long offset);
dword offset;
offset = va_arg(ap, dword);
func = slib_getoptptr(lib->handle, "vfs_seek");
if (func)
retval = func(f->handle, offset);
}
default:
// error
;
}
}
#endif
va_end(ap);
return retval;
}
/**
* @ingroup mod
*
* executes a vfs directory-function
*
* @param func the function's index
* @param lib the lib's id
* @return it is depended on 'func'
*/
long sblmgr_vfsdirexec(enum slib_vfs_idx_t func, int ilib, ...)
{
va_list ap;
int retval = 0;
va_start(ap, ilib);
#if defined(LNX_EXTLIB) || defined(WIN_EXTLIB)
{
slib_t *lib;
lib = &slib_table[ilib];
switch (func) {
case lib_vfs_chmod:
{
long (*func) (const char *name, int mode);
char *name;
int mode;
name = va_arg(ap, char *);
mode = va_arg(ap, int);
func = slib_getoptptr(lib->handle, "vfs_chmod");
if (func)
retval = (func(name, mode) == 0);
}
break;
case lib_vfs_access:
{
long (*func) (const char *name, int mode);
char *name;
int mode;
name = va_arg(ap, char *);
mode = va_arg(ap, int);
func = slib_getoptptr(lib->handle, "vfs_access");
if (func)
retval = func(name, mode);
}
break;
case lib_vfs_attr:
case lib_vfs_exist:
case lib_vfs_remove:
case lib_vfs_list:
case lib_vfs_chdir:
case lib_vfs_mkdir:
case lib_vfs_rmdir:
default:
;
}
}
#endif
va_end(ap);
return retval;
}