#include "lprefix.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lauxlib.h"
#include "lualib.h"
#if !defined(LUA_PROMPT)
#define LUA_PROMPT "> "
#define LUA_PROMPT2 ">> "
#endif
#if !defined(LUA_MAXINPUT)
#define LUA_MAXINPUT 512
#endif
#if !defined(lua_stdin_is_tty)
#if defined(LUA_USE_POSIX)
#include <unistd.h>
#define lua_stdin_is_tty() isatty(0)
#elif defined(LUA_USE_WINDOWS)
#include <io.h>
#define lua_stdin_is_tty() _isatty(_fileno(stdin))
#else
#define lua_stdin_is_tty() 1
#endif
#endif
#if !defined(lua_readline)
#if defined(LUA_USE_READLINE)
#include <readline/readline.h>
#include <readline/history.h>
#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
#define lua_saveline(L,line) ((void)L, add_history(line))
#define lua_freeline(L,b) ((void)L, free(b))
#else
#define lua_readline(L,b,p) \
((void)L, fputs(p, stdout), fflush(stdout), \
fgets(b, LUA_MAXINPUT, stdin) != NULL)
#define lua_saveline(L,line) { (void)L; (void)line; }
#define lua_freeline(L,b) { (void)L; (void)b; }
#endif
#endif
static lua_State *globalL = NULL;
static const char *progname = NULL;
static void lstop (lua_State *L, lua_Debug *ar) {
(void)ar;
lua_sethook(L, NULL, 0, 0);
luaL_error(L, "interrupted!");
}
static void laction (int i) {
signal(i, SIG_DFL);
lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}
static void l_message (const char *pname, const char *msg) {
if (pname) lua_writestringerror("%s: ", pname);
lua_writestringerror("%s\n", msg);
}
static int report (lua_State *L, int status) {
if (status != LUA_OK) {
const char *msg = lua_tostring(L, -1);
l_message(progname, msg);
lua_pop(L, 1);
}
return status;
}
static int msghandler (lua_State *L) {
const char *msg = lua_tostring(L, 1);
if (msg == NULL) {
if (luaL_callmeta(L, 1, "__tostring") &&
lua_type(L, -1) == LUA_TSTRING)
return 1;
else
msg = lua_pushfstring(L, "(error object is a %s value)",
luaL_typename(L, 1));
}
luaL_traceback(L, L, msg, 1);
return 1;
}
int docall (lua_State *L, int narg, int nres) {
int status;
int base = lua_gettop(L) - narg;
lua_pushcfunction(L, msghandler);
lua_insert(L, base);
globalL = L;
signal(SIGINT, laction);
status = lua_pcall(L, narg, nres, base);
signal(SIGINT, SIG_DFL);
lua_remove(L, base);
return status;
}
static const char *get_prompt (lua_State *L, int firstline) {
const char *p;
lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2");
p = lua_tostring(L, -1);
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
return p;
}
#define EOFMARK "<eof>"
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
static int incomplete (lua_State *L, int status) {
if (status == LUA_ERRSYNTAX) {
size_t lmsg;
const char *msg = lua_tolstring(L, -1, &lmsg);
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
lua_pop(L, 1);
return 1;
}
}
return 0;
}
static int pushline (lua_State *L, int firstline) {
char buffer[LUA_MAXINPUT];
char *b = buffer;
size_t l;
const char *prmt = get_prompt(L, firstline);
int readstatus = lua_readline(L, b, prmt);
if (readstatus == 0)
return 0;
lua_pop(L, 1);
l = strlen(b);
if (l > 0 && b[l-1] == '\n')
b[--l] = '\0';
if (firstline && b[0] == '=')
lua_pushfstring(L, "return %s", b + 1);
else
lua_pushlstring(L, b, l);
lua_freeline(L, b);
return 1;
}
static int addreturn (lua_State *L) {
const char *line = lua_tostring(L, -1);
const char *retline = lua_pushfstring(L, "return %s;", line);
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
if (status == LUA_OK) {
lua_remove(L, -2);
if (line[0] != '\0')
lua_saveline(L, line);
}
else
lua_pop(L, 2);
return status;
}
static int multiline (lua_State *L) {
for (;;) {
size_t len;
const char *line = lua_tolstring(L, 1, &len);
int status = luaL_loadbuffer(L, line, len, "=stdin");
if (!incomplete(L, status) || !pushline(L, 0)) {
lua_saveline(L, line);
return status;
}
lua_pushliteral(L, "\n");
lua_insert(L, -2);
lua_concat(L, 3);
}
}
static int loadline (lua_State *L) {
int status;
lua_settop(L, 0);
if (!pushline(L, 1))
return -1;
if ((status = addreturn(L)) != LUA_OK)
status = multiline(L);
lua_remove(L, 1);
lua_assert(lua_gettop(L) == 1);
return status;
}
static void l_print (lua_State *L) {
int n = lua_gettop(L);
if (n > 0) {
luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
lua_getglobal(L, "print");
lua_insert(L, 1);
if (lua_pcall(L, n, 0, 0) != LUA_OK)
l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
lua_tostring(L, -1)));
}
}
static void doREPL (lua_State *L) {
int status;
const char *oldprogname = progname;
progname = NULL;
while ((status = loadline(L)) != -1) {
if (status == LUA_OK)
status = docall(L, 0, LUA_MULTRET);
if (status == LUA_OK) l_print(L);
else report(L, status);
}
lua_settop(L, 0);
lua_writeline();
progname = oldprogname;
}
static int pmain (lua_State *L) {
luaL_checkversion(L);
doREPL(L);
lua_pushboolean(L, 1);
return 1;
}
int lua_repl(lua_State *L, char *main_progname) {
int status, result;
if (L == NULL) return EXIT_FAILURE;
progname = main_progname;
lua_pushcfunction(L, &pmain);
status = lua_pcall(L, 0, 1, 0);
result = lua_toboolean(L, -1);
report(L, status);
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}