diff options
Diffstat (limited to 'src/loadlib.c')
-rw-r--r-- | src/loadlib.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/loadlib.c b/src/loadlib.c new file mode 100644 index 00000000..4adcbdc0 --- /dev/null +++ b/src/loadlib.c @@ -0,0 +1,464 @@ +/* +** $Id: loadlib.c,v 1.15 2004/12/29 18:56:34 roberto Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +* +* This module contains an implementation of loadlib for Unix systems +* that have dlfcn, an implementation for Darwin (Mac OS X), an +* implementation for Windows, and a stub for other systems. +*/ + + +#include <stdlib.h> +#include <string.h> + + +#define loadlib_c +#define LUA_LIB + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + + +#define LIBPREFIX "LOADLIB: " + +#define POF LUA_POF +#define LIB_FAIL "open" + + +static void ll_unloadlib (void *lib); +static void *ll_load (lua_State *L, const char *path); +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); + + + +#if defined(USE_DLOPEN) +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + +#include <dlfcn.h> + +static void ll_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + void *lib = dlopen(path, RTLD_NOW); + if (lib == NULL) lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)dlsym(lib, sym); + if (f == NULL) lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(USE_DLL) +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include <windows.h> + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer), NULL)) + lua_pushstring(L,buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void ll_unloadlib (void *lib) { + FreeLibrary((HINSTANCE)lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + HINSTANCE lib = LoadLibrary(path); + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + + +#elif defined(USE_DYLD) +/* +** {====================================================================== +** Native Mac OS X / Darwin Implementation +** ======================================================================= +*/ + +#include <mach-o/dyld.h> + + +#undef POF +#define POF "_" LUA_POF + + +static void pusherror (lua_State *L) { + const char *err_str; + const char *err_file; + NSLinkEditErrors err; + int err_num; + NSLinkEditError(&err, &err_num, &err_file, &err_str); + lua_pushstring(L, err_str); +} + + +static const char *errorfromcode (NSObjectFileImageReturnCode ret) { + switch (ret) { + case NSObjectFileImageInappropriateFile: + return "file is not a bundle"; + case NSObjectFileImageArch: + return "library is for wrong CPU type"; + case NSObjectFileImageFormat: + return "bad format"; + case NSObjectFileImageAccess: + return "cannot access file"; + case NSObjectFileImageFailure: + default: + return "unable to load library"; + } +} + + +static void ll_unloadlib (void *lib) { + NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); +} + + +static void *ll_load (lua_State *L, const char *path) { + NSObjectFileImage img; + NSObjectFileImageReturnCode ret; + /* this would be a rare case, but prevents crashing if it happens */ + if(!_dyld_present()) { + lua_pushliteral(L, "dyld not present"); + return NULL; + } + ret = NSCreateObjectFileImageFromFile(path, &img); + if (ret == NSObjectFileImageSuccess) { + NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(img); + if (mod == NULL) pusherror(L); + return mod; + } + lua_pushstring(L, errorfromcode(ret)); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); + if (nss == NULL) { + lua_pushfstring(L, "symbol `%s' not found", sym); + return NULL; + } + return (lua_CFunction)NSAddressOfSymbol(nss); +} + +/* }====================================================== */ + + + +#else +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +static void ll_unloadlib (void *lib) { + (void)lib; /* to avoid warnings */ +} + + +static void *ll_load (lua_State *L, const char *path) { + (void)path; /* to avoid warnings */ + lua_pushliteral(L,"`loadlib' not supported"); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + (void)lib; (void)sym; /* to avoid warnings */ + lua_pushliteral(L,"`loadlib' not supported"); + return NULL; +} + +/* }====================================================== */ +#endif + + + +static void **ll_register (lua_State *L, const char *path) { + void **plib; + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ + if (!lua_isnil(L, -1)) /* is there an entry? */ + plib = (void **)lua_touserdata(L, -1); + else { /* no entry yet; create one */ + lua_pop(L, 1); + plib = (void **)lua_newuserdata(L, sizeof(const void *)); + *plib = NULL; + luaL_getmetatable(L, "_LOADLIB"); + lua_setmetatable(L, -2); + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + return plib; +} + + +/* +** __gc tag method: calls library's `ll_unloadlib' function with the lib +** handle +*/ +static int gctm (lua_State *L) { + void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); + if (lib) { + if (*lib) ll_unloadlib(*lib); + *lib = NULL; /* mark library as closed */ + } + return 0; +} + + +static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { + const char *reason; + void **reg = ll_register(L, path); + if (*reg == NULL) *reg = ll_load(L, path); + if (*reg == NULL) + reason = LIB_FAIL; + else { + lua_CFunction f = ll_sym(L, *reg, sym); + if (f) { + lua_pushcfunction(L, f); + return 1; /* return function */ + } + reason = "init"; + } + lua_pushnil(L); + lua_insert(L, -2); + lua_pushstring(L, reason); + return 3; /* return nil, ll_error, reason */ +} + + +static int ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + return ll_loadfunc(L, path, init); +} + + + +/* +** {====================================================== +** `require' and `module' functions +** ======================================================= +*/ + + +static const char *loadLua (lua_State *L, const char *fname, const char *name) { + const char *path; + /* try first `LUA_PATH' for compatibility */ + lua_getglobal(L, "LUA_PATH"); + path = lua_tostring(L, -1); + if (!path) { + lua_pop(L, 1); + luaL_getfield(L, LUA_GLOBALSINDEX, "package.path"); + path = lua_tostring(L, -1); + } + if (path == NULL) + luaL_error(L, "`package.path' must be a string"); + fname = luaL_searchpath(L, fname, path); + if (fname == NULL) return path; /* library not found in this path */ + if (luaL_loadfile(L, fname) != 0) + luaL_error(L, "error loading package `%s' (%s)", name, lua_tostring(L, -1)); + return NULL; /* library loaded successfully */ +} + + +static const char *loadC (lua_State *L, const char *fname, const char *name) { + const char *path; + const char *funcname; + luaL_getfield(L, LUA_GLOBALSINDEX, "package.cpath"); + path = lua_tostring(L, -1); + if (path == NULL) + luaL_error(L, "`package.cpath' must be a string"); + fname = luaL_searchpath(L, fname, path); + if (fname == NULL) return path; /* library not found in this path */ + funcname = luaL_gsub(L, name, ".", LUA_OFSEP); + funcname = lua_pushfstring(L, "%s%s", POF, funcname); + if (ll_loadfunc(L, fname, funcname) != 1) + luaL_error(L, "error loading package `%s' (%s)", name, lua_tostring(L, -2)); + return NULL; /* library loaded successfully */ +} + + +static int ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_settop(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, 2, name); + if (lua_toboolean(L, -1)) /* is it there? */ + return 1; /* package is already loaded; return its result */ + /* else must load it; first mark it as loaded */ + lua_pushboolean(L, 1); + lua_setfield(L, 2, name); /* _LOADED[name] = true */ + /* check whether it is preloaded */ + lua_getfield(L, LUA_REGISTRYINDEX, "_PRELOAD"); + lua_getfield(L, -1, name); + if (lua_isnil(L, -1)) { /* no preload function for that module? */ + const char *fname = luaL_gsub(L, name, ".", LUA_DIRSEP); + const char *cpath = loadC(L, fname, name); /* try a C module */ + if (cpath) { /* not found? */ + const char *path = loadLua(L, fname, name); /* try a Lua module */ + if (path) { /* yet not found? */ + lua_pushnil(L); + lua_setfield(L, 2, name); /* unmark _LOADED[name] */ + return luaL_error(L, "package `%s' not found\n" + " cpath: %s\n path: %s", + name, cpath, path); + } + } + } + lua_pushvalue(L, 1); /* pass name as argument to module */ + lua_call(L, 1, 1); /* run loaded module */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* update _LOADED[name] with returned value */ + lua_getfield(L, 2, name); /* return _LOADED[name] */ + return 1; +} + + +static void setfenv (lua_State *L) { + lua_Debug ar; + lua_getstack(L, 1, &ar); + lua_getinfo(L, "f", &ar); + lua_pushvalue(L, -2); + lua_setfenv(L, -2); +} + + +static int ll_module (lua_State *L) { + const char *modname = luaL_checkstring(L, 1); + const char *dot; + lua_settop(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + /* try to find given table */ + luaL_getfield(L, LUA_GLOBALSINDEX, modname); + if (lua_isnil(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + lua_newtable(L); /* create it */ + /* register it with given name */ + lua_pushvalue(L, -1); + luaL_setfield(L, LUA_GLOBALSINDEX, modname); + } + else if (!lua_istable(L, -1)) + return luaL_error(L, "name conflict for module `%s'", modname); + /* check whether table already has a _NAME field */ + lua_getfield(L, -1, "_NAME"); + if (!lua_isnil(L, -1)) /* is table an initialized module? */ + lua_pop(L, 1); + else { /* no; initialize it */ + lua_pop(L, 1); + lua_newtable(L); /* create new metatable */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfield(L, -2, "__index"); /* mt.__index = _G */ + lua_setmetatable(L, -2); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_M"); /* module._M = module */ + lua_pushstring(L, modname); + lua_setfield(L, -2, "_NAME"); + dot = strrchr(modname, '.'); /* look for last dot in module name */ + if (dot == NULL) dot = modname; + else dot++; + /* set _PACKAGE as package name (full module name minus last part) */ + lua_pushlstring(L, modname, dot - modname); + lua_setfield(L, -2, "_PACKAGE"); + } + lua_pushvalue(L, -1); + lua_setfield(L, 2, modname); /* _LOADED[modname] = new table */ + setfenv(L); + return 0; +} + + +/* }====================================================== */ + + +static const luaL_reg ll_funcs[] = { + {"loadlib", ll_loadlib}, + {"require", ll_require}, + {"module", ll_module}, + {NULL, NULL} +}; + + + +LUALIB_API int luaopen_loadlib (lua_State *L) { + const char *path; + /* create new type _LOADLIB */ + luaL_newmetatable(L, "_LOADLIB"); + lua_pushcfunction(L, gctm); + lua_setfield(L, -2, "__gc"); + /* create `package' table */ + lua_newtable(L); + lua_pushvalue(L, -1); + lua_setglobal(L, "package"); + /* set field `path' */ + path = getenv(LUA_PATH); + if (path == NULL) path = LUA_PATH_DEFAULT; + lua_pushstring(L, path); + lua_setfield(L, -2, "path"); + /* set field `cpath' */ + path = getenv(LUA_CPATH); + if (path == NULL) path = LUA_CPATH_DEFAULT; + lua_pushstring(L, path); + lua_setfield(L, -2, "cpath"); + /* set field `loaded' */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_setfield(L, -2, "loaded"); + /* set field `preload' */ + lua_getfield(L, LUA_REGISTRYINDEX, "_PRELOAD"); + lua_setfield(L, -2, "preload"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_openlib(L, NULL, ll_funcs, 0); /* open lib into global table */ + return 1; +} + |