/* ** LuaJIT frontend. Runs commands, scripts, read-eval-print (REPL) etc. ** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h ** ** Major portions taken verbatim or adapted from the Lua interpreter. ** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h */ #include #include #include #include #include #include #include "lua-headers.hpp" #include "util.hpp" #include "lpx-table.hpp" #include "lpx-classdb.hpp" // Add another error status. #define LUA_PARTIAL (LUA_ERRERR + 10) #define LUA_EOF (LUA_ERRERR + 11) static lua_State *globalL = NULL; static void lstop(lua_State *L, lua_Debug *ar) { (void)ar; /* unused arg. */ lua_sethook(L, NULL, 0, 0); /* Avoid luaL_error -- a C hook doesn't add an extra frame. */ luaL_where(L, 0); lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1)); lua_error(L); } static void laction(int i) { signal(i, SIG_DFL); /* if another SIGINT happens before lstop, terminate process (default action) */ lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); } static void l_message(const char *msg) { fputs(msg, stderr); fputc('\n', stderr); fflush(stderr); } static int report(lua_State *L, int status) { if (status && !lua_isnil(L, -1)) { const char *msg = lua_tostring(L, -1); if (msg == NULL) msg = "(error object is not a string)"; l_message(msg); lua_pop(L, 1); } return status; } static int traceback(lua_State *L) { if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */ if (lua_isnoneornil(L, 1) || !luaL_callmeta(L, 1, "__tostring") || !lua_isstring(L, -1)) return 1; /* Return non-string error object. */ lua_remove(L, 1); /* Replace object by result of __tostring metamethod. */ } luaL_traceback(L, L, lua_tostring(L, 1), 1); return 1; } static int docall(lua_State *L, int narg, int clear) { int status; int base = lua_gettop(L) - narg; /* function index */ lua_pushcfunction(L, traceback); /* push traceback function */ lua_insert(L, base); /* put it under chunk and args */ signal(SIGINT, laction); status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); signal(SIGINT, SIG_DFL); lua_remove(L, base); /* remove traceback function */ /* force a complete garbage collection in case of errors */ if (status != LUA_OK) lua_gc(L, LUA_GCCOLLECT, 0); return status; } // Print a prompt, read a line of text, and push it on the stack. // If it's the first line, translate "=" to "return" // static int pushline(lua_State *L, int firstline) { const int MAXINPUT = 1000; char buf[MAXINPUT]; fputs(firstline ? "> " : ">> ", stdout); fflush(stdout); if (fgets(buf, MAXINPUT, stdin)) { size_t len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; if (firstline && buf[0] == '=') lua_pushfstring(L, "return %s", buf + 1); else lua_pushstring(L, buf); return 1; } return 0; } // Same as lua_loadbuffer, except: if the form passed in // is a partial form, returns LUA_PARTIAL. // static int loadbuffer_partial(lua_State *L, const char *s, int len, const char *fn) { const char *eof = "''"; int status = luaL_loadbuffer(L, s, len, fn); if (status != LUA_ERRSYNTAX) { return status; } size_t lmsg; const char *msg = lua_tolstring(L, -1, &lmsg); const char *tp = msg + lmsg - (sizeof(eof) - 1); if (strstr(msg, eof) == tp) { return LUA_PARTIAL; } else { return status; } } // Read an entire form. On success, returns a closure. On EOF, // returns just the LUA_EOF status code. On failure, returns an // error message and a status code. static int read_and_load(lua_State *L) { int status; lua_settop(L, 0); if (!pushline(L, 1)) return LUA_EOF; /* no input */ for (;;) { /* repeat until gets a complete line */ status = loadbuffer_partial(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); if (status != LUA_PARTIAL) break; lua_pop(L, 1); /* pop the error message */ if (!pushline(L, 0)) /* no more input? */ return LUA_EOF; lua_pushliteral(L, "\n"); /* add a new line... */ lua_insert(L, -2); /* ...between the two lines */ lua_concat(L, 3); /* join them */ } lua_remove(L, 1); /* remove line */ return status; } static void dotty(lua_State *L) { while (1) { int status = read_and_load(L); if (status == LUA_EOF) break; if (status == LUA_OK) status = docall(L, 0, 0); report(L, status); if (status == LUA_OK && lua_gettop(L) > 0) { /* any result to print? */ lua_getglobal(L, "print"); lua_insert(L, 1); if (lua_pcall(L, lua_gettop(L) - 1, 0, 0) != 0) { l_message( lua_pushfstring(L, "error calling 'print' (%s)", lua_tostring(L, -1))); } } } lua_settop(L, 0); /* clear stack */ fputs("\n", stdout); fflush(stdout); } static void loadmain(lua_State *L) { util::stringvec filenames = util::trim_and_uncomment(util::read_lines("syslua/control.lst")); if (filenames.empty()) { lua_pushfstring(L, "Cannot read syslua/control.lst"); lua_error(L); } for (const auto &fn : filenames) { std::string full = "syslua/" + fn; int status = luaL_loadfilex(L, full.c_str(), NULL); if (status == LUA_OK) { status = docall(L, 0, 0); } if (status != LUA_OK) { lua_error(L); } } } static int pmain(lua_State *L) { globalL = L; LUAJIT_VERSION_SYM(); /* Linker-enforced version check. */ lua_gc(L, LUA_GCSTOP, 0); luaopen_base(L); // luaopen_package(L); // Omitted because we use our own package system. luaopen_table(L); // Standard table operations. luaopen_lpx_table(L); // Supplemental table operations. // luaopen_io(L); // Not safe for the sandbox. // luaopen_os(L); // Not safe for the sandbox. luaopen_string(L); luaopen_math(L); // luaopen_debug(L); // Not safe for the sandbox. luaopen_bit(L); // luaopen_jit(L); // Don't know what it's for. luaopen_lpx_classdb(L); lua_gc(L, LUA_GCRESTART, -1); loadmain(L); dotty(L); return 0; } int main(int argc, char **argv) { int status; lua_State *L = lua_open(); if (L == NULL) { l_message("cannot create state: not enough memory"); return -1; } status = lua_cpcall(L, pmain, NULL); report(L, status); lua_close(L); return status ? -1 : 0; }