% luastuff.w % Copyright 2006-2010 Taco Hoekwater % This file is part of LuaTeX. % LuaTeX 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. % LuaTeX 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 Lesser General Public % License for more details. % You should have received a copy of the GNU General Public License along % with LuaTeX; if not, see . @ @c static const char _svn_version[] = "$Id: luastuff.w 4001 2010-11-28 15:49:46Z taco $ " "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.70.1/source/texk/web2c/luatexdir/lua/luastuff.w $"; #include "lua/luatex-api.h" #include "ptexlib.h" @ @c lua_State *Luas = NULL; int luastate_bytes = 0; int lua_active = 0; @ @c void make_table(lua_State * L, const char *tab, const char *getfunc, const char *setfunc) { /* make the table *//* |[{}]| */ lua_pushstring(L, tab); /* |[{},"dimen"]| */ lua_newtable(L); /* |[{},"dimen",{}]| */ lua_settable(L, -3); /* |[{}]| */ /* fetch it back */ lua_pushstring(L, tab); /* |[{},"dimen"]| */ lua_gettable(L, -2); /* |[{},{}]| */ /* make the meta entries */ luaL_newmetatable(L, tab); /* |[{},{},{}]| */ lua_pushstring(L, "__index"); /* |[{},{},{},"__index"]| */ lua_pushstring(L, getfunc); /* |[{},{},{},"__index","getdimen"]| */ lua_gettable(L, -5); /* |[{},{},{},"__index",]| */ lua_settable(L, -3); /* |[{},{},{}]| */ lua_pushstring(L, "__newindex"); /* |[{},{},{},"__newindex"]| */ lua_pushstring(L, setfunc); /* |[{},{},{},"__newindex","setdimen"]| */ lua_gettable(L, -5); /* |[{},{},{},"__newindex",]| */ lua_settable(L, -3); /* |[{},{},{}]| */ lua_setmetatable(L, -2); /* |[{},{}]| : assign the metatable */ lua_pop(L, 1); /* |[{}]| : clean the stack */ } @ @c static const char *getS(lua_State * L, void *ud, size_t * size) { LoadS *ls = (LoadS *) ud; (void) L; if (ls->size == 0) return NULL; *size = ls->size; ls->size = 0; return ls->s; } @ @c static void *my_luaalloc(void *ud, void *ptr, size_t osize, size_t nsize) { void *ret = NULL; (void) ud; /* for -Wunused */ if (nsize == 0) free(ptr); else ret = realloc(ptr, nsize); luastate_bytes += (int) (nsize - osize); return ret; } @ @c static int my_luapanic(lua_State * L) { (void) L; /* to avoid warnings */ fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1)); return 0; } @ @c static const luaL_Reg lualibs[] = { {"", luaopen_base}, {"package", luaopen_package}, {"table", luaopen_table}, {"io", luaopen_io}, {"os", luaopen_os}, {"string", luaopen_string}, {"math", luaopen_math}, {"debug", luaopen_debug}, {"unicode", luaopen_unicode}, {"zip", luaopen_zip}, {"lpeg", luaopen_lpeg}, {"md5", luaopen_md5}, {"lfs", luaopen_lfs}, {"profiler", luaopen_profiler}, {NULL, NULL} }; @ @c static void do_openlibs(lua_State * L) { const luaL_Reg *lib = lualibs; for (; lib->func; lib++) { lua_pushcfunction(L, lib->func); lua_pushstring(L, lib->name); lua_call(L, 1, 0); } } @ @c static int load_aux (lua_State *L, int status) { if (status == 0) /* OK? */ return 1; else { lua_pushnil(L); lua_insert(L, -2); /* put before error message */ return 2; /* return nil plus error message */ } } @ @c static int luatex_loadfile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); if (!lua_only && !fname) { if (interaction == batch_mode) { lua_pushnil(L); lua_pushstring(L, "reading from stdin is disabled in batch mode"); return 2; /* return nil plus error message */ } else { tprint_nl("lua> "); } } return load_aux(L, luaL_loadfile(L, fname)); } @ @c static int luatex_dofile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); int n = lua_gettop(L); if (!lua_only && !fname) { if (interaction == batch_mode) { lua_pushnil(L); lua_pushstring(L, "reading from stdin is disabled in batch mode"); return 2; /* return nil plus error message */ } else { tprint_nl("lua> "); } } if (luaL_loadfile(L, fname) != 0) lua_error(L); lua_call(L, 0, LUA_MULTRET); return lua_gettop(L) - n; } @ @c void luainterpreter(void) { lua_State *L; L = lua_newstate(my_luaalloc, NULL); if (L == NULL) { fprintf(stderr, "Can't create the Lua state.\n"); return; } lua_atpanic(L, &my_luapanic); do_openlibs(L); /* does all the 'simple' libraries */ lua_pushcfunction(L,luatex_dofile); lua_setglobal(L, "dofile"); lua_pushcfunction(L,luatex_loadfile); lua_setglobal(L, "loadfile"); luatex_md5_lua_open(L); open_oslibext(L, safer_option); open_lfslibext(L); /* luasockets */ /* socket and mime are a bit tricky to open because they use a load-time dependency that has to be worked around for luatex, where the C module is loaded way before the lua module. */ if (!nosocket_option) { lua_getglobal(L, "package"); lua_getfield(L, -1, "loaded"); if (!lua_istable(L, -1)) { lua_newtable(L); lua_setfield(L, -2, "loaded"); lua_getfield(L, -1, "loaded"); } luaopen_socket_core(L); lua_setfield(L, -2, "socket.core"); lua_pushnil(L); lua_setfield(L, -2, "socket"); /* package.loaded.socket = nil */ luaopen_mime_core(L); lua_setfield(L, -2, "mime.core"); lua_pushnil(L); lua_setfield(L, -2, "mime"); /* package.loaded.mime = nil */ lua_pop(L, 2); /* pop the tables */ luatex_socketlua_open(L); /* preload the pure lua modules */ } /* zlib. slightly odd calling convention */ luaopen_zlib(L); lua_setglobal(L, "zlib"); luaopen_gzip(L); /* our own libraries */ luaopen_ff(L); luaopen_tex(L); luaopen_token(L); luaopen_node(L); luaopen_texio(L); luaopen_kpse(L); luaopen_callback(L); luaopen_lua(L, startup_filename); luaopen_stats(L); luaopen_font(L); luaopen_lang(L); luaopen_mplib(L); /* |luaopen_pdf(L);| */ /* environment table at |LUA_ENVIRONINDEX| needs to load this way: */ lua_pushcfunction(L, luaopen_pdf); lua_pushstring(L, "pdf"); lua_call(L, 1, 0); /* |luaopen_img(L);| */ lua_pushcfunction(L, luaopen_img); lua_pushstring(L, "img"); lua_call(L, 1, 0); /* |luaopen_epdf(L);| */ lua_pushcfunction(L, luaopen_epdf); lua_pushstring(L, "epdf"); lua_call(L, 1, 0); lua_createtable(L, 0, 0); lua_setglobal(L, "texconfig"); if (safer_option) { /* disable some stuff if --safer */ (void) hide_lua_value(L, "os", "execute"); (void) hide_lua_value(L, "os", "rename"); (void) hide_lua_value(L, "os", "remove"); (void) hide_lua_value(L, "io", "popen"); /* make io.open only read files */ luaL_checkstack(L, 2, "out of stack space"); lua_getglobal(L, "io"); lua_getfield(L, -1, "open_ro"); lua_setfield(L, -2, "open"); (void) hide_lua_value(L, "io", "tmpfile"); (void) hide_lua_value(L, "io", "output"); (void) hide_lua_value(L, "lfs", "chdir"); (void) hide_lua_value(L, "lfs", "lock"); (void) hide_lua_value(L, "lfs", "touch"); (void) hide_lua_value(L, "lfs", "rmdir"); (void) hide_lua_value(L, "lfs", "mkdir"); } Luas = L; } @ @c int hide_lua_table(lua_State * L, const char *name) { int r = 0; lua_getglobal(L, name); if (lua_istable(L, -1)) { r = luaL_ref(L, LUA_REGISTRYINDEX); lua_pushnil(L); lua_setglobal(L, name); } return r; } @ @c void unhide_lua_table(lua_State * L, const char *name, int r) { lua_rawgeti(L, LUA_REGISTRYINDEX, r); lua_setglobal(L, name); luaL_unref(L, LUA_REGISTRYINDEX, r); } @ @c int hide_lua_value(lua_State * L, const char *name, const char *item) { int r = 0; lua_getglobal(L, name); if (lua_istable(L, -1)) { lua_getfield(L, -1, item); r = luaL_ref(L, LUA_REGISTRYINDEX); lua_pushnil(L); lua_setfield(L, -2, item); } return r; } @ @c void unhide_lua_value(lua_State * L, const char *name, const char *item, int r) { lua_getglobal(L, name); if (lua_istable(L, -1)) { lua_rawgeti(L, LUA_REGISTRYINDEX, r); lua_setfield(L, -2, item); luaL_unref(L, LUA_REGISTRYINDEX, r); } } @ @c int lua_traceback(lua_State * L) { lua_getfield(L, LUA_GLOBALSINDEX, "debug"); if (!lua_istable(L, -1)) { lua_pop(L, 1); return 1; } lua_getfield(L, -1, "traceback"); if (!lua_isfunction(L, -1)) { lua_pop(L, 2); return 1; } lua_pushvalue(L, 1); /* pass error message */ lua_pushinteger(L, 2); /* skip this function and traceback */ lua_call(L, 2, 1); /* call debug.traceback */ return 1; } @ @c static void luacall(int p, int nameptr, boolean is_string) { LoadS ls; int i; size_t ll = 0; char *lua_id; char *s = NULL; if (Luas == NULL) { luainterpreter(); } lua_active++; if (is_string) { const char *ss = NULL; lua_rawgeti(Luas, LUA_REGISTRYINDEX, p); ss = lua_tolstring(Luas, -1, &ll); s = xmalloc(ll+1); memcpy(s,ss,ll+1); lua_pop(Luas,1); } else { int l = 0; s = tokenlist_to_cstring(p, 1, &l); ll = (size_t)l; } ls.s = s; ls.size = ll; if (ls.size > 0) { if (nameptr > 0) { int l = 0; /* not used */ lua_id = tokenlist_to_cstring(nameptr, 1, &l); } else if (nameptr < 0) { char *tmp = get_lua_name((nameptr + 65536)); if (tmp != NULL) lua_id = xstrdup(tmp); else lua_id = xstrdup("\\latelua "); } else { lua_id = xmalloc(20); snprintf((char *) lua_id, 20, "\\latelua "); } i = lua_load(Luas, getS, &ls, lua_id); if (i != 0) { Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); } else { int base = lua_gettop(Luas); /* function index */ lua_checkstack(Luas, 1); lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ lua_insert(Luas, base); /* put it under chunk */ i = lua_pcall(Luas, 0, 0, base); lua_remove(Luas, base); /* remove traceback function */ if (i != 0) { lua_gc(Luas, LUA_GCCOLLECT, 0); Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); } } xfree(lua_id); xfree(ls.s); } lua_active--; } @ @c void late_lua(PDF pdf, halfword p) { (void) pdf; if (late_lua_type(p)==normal) { expand_macros_in_tokenlist(p); /* sets |def_ref| */ luacall(def_ref, late_lua_name(p), false); flush_list(def_ref); } else { luacall(late_lua_data(p), late_lua_name(p), true); } } @ @c void luatokencall(int p, int nameptr) { LoadS ls; int i, l; char *s = NULL; char *lua_id; assert(Luas); l = 0; lua_active++; s = tokenlist_to_cstring(p, 1, &l); ls.s = s; ls.size = (size_t) l; if (ls.size > 0) { if (nameptr > 0) { lua_id = tokenlist_to_cstring(nameptr, 1, &l); } else if (nameptr < 0) { char *tmp = get_lua_name((nameptr + 65536)); if (tmp != NULL) lua_id = xstrdup(tmp); else lua_id = xstrdup("\\directlua "); } else { lua_id = xstrdup("\\directlua "); } i = lua_load(Luas, getS, &ls, lua_id); xfree(s); if (i != 0) { Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); } else { int base = lua_gettop(Luas); /* function index */ lua_checkstack(Luas, 1); lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ lua_insert(Luas, base); /* put it under chunk */ i = lua_pcall(Luas, 0, 0, base); lua_remove(Luas, base); /* remove traceback function */ if (i != 0) { lua_gc(Luas, LUA_GCCOLLECT, 0); Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); } } xfree(lua_id); } lua_active--; } @ @c lua_State *luatex_error(lua_State * L, int is_fatal) { const char *luaerr; size_t len = 0; char *err = NULL; if (lua_isstring(L, -1)) { luaerr = lua_tolstring(L, -1, &len); err = (char *) xmalloc((unsigned) (len + 1)); len = (size_t) snprintf(err, (len + 1), "%s", luaerr); } if (is_fatal > 0) { /* Normally a memory error from lua. The pool may overflow during the |maketexlstring()|, but we are crashing anyway so we may as well abort on the pool size */ lua_fatal_error(err); /* never reached */ xfree(err); lua_close(L); return (lua_State *) NULL; } else { lua_norm_error(err); xfree(err); return L; } } @ @c void preset_environment(lua_State * L, const parm_struct * p) { int i; assert(L != NULL); lua_newtable(L); /* t */ for (i = 1, ++p; p->name != NULL; i++, p++) { assert(i == p->idx); lua_pushstring(L, p->name); /* k t */ lua_pushinteger(L, p->idx); /* v k t */ lua_settable(L, -3); /* t */ } lua_replace(L, LUA_ENVIRONINDEX); /* - */ }