/*
** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $
** Lua stand-alone interpreter
** See Copyright Notice in lua.h
*/
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <locale.h>
#include <ncurses.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define lua_c
#include "lua.h"
//? #include "lstate.h"
#include "teliva.h"
#include "lauxlib.h"
#include "lualib.h"
#define BG(i) (COLOR_PAIR((i)+8))
#define FG(i) (COLOR_PAIR(i))
static lua_State *globalL = NULL;
static const char *progname = LUA_PROGNAME;
static void lstop (lua_State *L, lua_Debug *ar) {
(void)ar; /* unused arg. */
lua_sethook(L, NULL, 0, 0);
luaL_error(L, "interrupted!");
}
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 print_usage (void) {
printw(
"usage: %s [options] [script [args]].\n"
"Available options are:\n"
" -e stat execute string " LUA_QL("stat") "\n"
" -l name require library " LUA_QL("name") "\n"
" -i enter interactive mode after executing " LUA_QL("script") "\n"
" -v show version information\n"
" -- stop handling options\n"
,
progname);
refresh();
}
static void l_message (const char *pname, const char *msg) {
if (!stdscr || isendwin()) {
printf("%s: %s\n", pname, msg);
exit(1);
}
if (pname) mvprintw(LINES-2, 0, "%s: ", pname);
printw(msg);
mvprintw(LINES-1, 0, "sorry, you'll need to edit the image directly. press any key to exit.");
refresh();
getch();
}
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(progname, msg);
lua_pop(L, 1);
}
return status;
}
static int traceback (lua_State *L) {
if (!lua_isstring(L, 1)) /* 'message' not a string? */
return 1; /* keep it intact */
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;
}
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 != 0) lua_gc(L, LUA_GCCOLLECT, 0);
return status;
}
static void print_version (void) {
l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT);
}
/* pushes commandline args to the stack, then an array of all commandline args */
static int getargs (lua_State *L, char **argv, int n) {
int narg;
int i;
int argc = 0;
while (argv[argc]) argc++; /* count total number of arguments */
narg = argc - (n + 1); /* number of arguments to the script */
luaL_checkstack(L, narg + 3, "too many arguments to script");
for (i=n+1; i < argc; i++)
lua_pushstring(L, argv[i]);
lua_createtable(L, narg, n + 1);
for (i=0; i < argc; i++) {
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, i - n);
}
return narg;
}
static int dofile (lua_State *L, const char *name) {
int status = luaL_loadfile(L, name) || docall(L, 0, 1);
return report(L, status);
}
static int dostring (lua_State *L, const char *s, const char *name) {
int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
return report(L, status);
}
static int dolibrary (lua_State *L, const char *name) {
lua_getglobal(L, "require");
lua_pushstring(L, name);
return report(L, docall(L, 1, 1));
}
static const char *get_prompt (lua_State *L, int firstline) {
const char *p;
lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
p = lua_tostring(L, -1);
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
lua_pop(L, 1); /* remove global */
return p;
}
static int incomplete (lua_State *L, int status) {
if (status == LUA_ERRSYNTAX) {
size_t lmsg;
const char *msg = lua_tolstring(L, -1, &lmsg);
const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
if (strstr(msg, LUA_QL("<eof>")) == tp) {
lua_pop(L, 1);
return 1;
}
}
return 0; /* else... */
}
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);
addstr(prmt);
refresh();
getnstr(b, LUA_MAXINPUT);
l = strlen(b);
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
b[l-1] = '\0'; /* remove it */
if (firstline && b[0] == '=') /* first line starts with `=' ? */
lua_pushfstring(L, "return %s", b+1); /* change it to `return' */
else
lua_pushstring(L, b);
return 1;
}
static int loadline (lua_State *L) {
int status;
lua_settop(L, 0);
if (!pushline(L, 1))
return -1; /* no input */
for (;;) { /* repeat until gets a complete line */
status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=keyboard");
if (!incomplete(L, status)) break; /* cannot try to add lines? */
if (!pushline(L, 0)) /* no more input? */
return -1;
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) {
int status;
const char *oldprogname = progname;
progname = NULL;
while ((status = loadline(L)) != -1) {
if (status == 0) status = docall(L, 0, 0);
report(L, status);
if (status == 0 && 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(progname, lua_pushfstring(L,
"error calling " LUA_QL("print") " (%s)",
lua_tostring(L, -1)));
}
}
lua_settop(L, 0); /* clear stack */
refresh();
progname = oldprogname;
}
void stack_dump (lua_State *L) {
int i;
int top = lua_gettop(L);
int y = 1;
for (i = 1; i <= top; i++) { /* repeat for each level */
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: /* strings */
mvprintw(y, 30, "`%s'", lua_tostring(L, i));
break;
case LUA_TBOOLEAN: /* booleans */
mvprintw(y, 30, lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */
mvprintw(y, 30, "%g", lua_tonumber(L, i));
break;
default: /* other values */
mvprintw(y, 30, "%s", lua_typename(L, t));
break;
}
y++;
mvprintw(y, 30, " "); /* put a separator */
y++;
}
mvprintw(y, 30, "\n"); /* end the listing */
y++;
}
static int binding_exists (lua_State *L, const char *name) {
int result = 0;
lua_getglobal(L, name);
result = !lua_isnil(L, -1);
lua_pop(L, 1);
return result;
}
static const char *special_history_keys[] = {
"__teliva_timestamp",
"__teliva_undo",
"__teliva_note",
NULL,
};
int is_special_history_key(const char *key) {
for (const char **curr = special_history_keys; *curr != NULL; ++curr) {
if (strcmp(*curr, key) == 0)
return 1;
}
return 0;
}
/* when found, return 1 and leave string on top of stack
* when not found, return 0
* caller is responsible for cleaning up the stack. */
static int look_up_definition (lua_State *L, const char *name) {
lua_getglobal(L, "teliva_program");
int history_array = lua_gettop(L);
/* iterate over mutations in teliva_program history in reverse order */
int history_array_size = luaL_getn(L, history_array);
for (int i = history_array_size; i > 0; --i) {
lua_rawgeti(L, history_array, i);
int table = lua_gettop(L);
/* iterate over bindings */
/* really we expect only one */
for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
const char* key = lua_tostring(L, -2);
if (strcmp(key, "__teliva_undo") == 0) {
int next_i = lua_tointeger(L, -1);
assert(next_i < i);
i = next_i + 1; /* account for decrement */
lua_pop(L, 1);
break;
}
if (is_special_history_key(key)) continue;
if (strcmp(key, name) == 0)
return 1;
}
lua_pop(L, 1);
}
lua_pop(L, 1);
return 0;
}
int load_definitions(lua_State *L) {
int status;
lua_getglobal(L, "teliva_program");
int history_array = lua_gettop(L);
/* iterate over mutations in teliva_program history in reverse order */
int history_array_size = luaL_getn(L, history_array);
for (int i = history_array_size; i > 0; --i) {
lua_rawgeti(L, history_array, i);
int table = lua_gettop(L);
/* iterate over bindings */
/* really we expect only one */
for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {
const char* key = lua_tostring(L, -2);
if (strcmp(key, "__teliva_undo") == 0) {
int next_i = lua_tointeger(L, -1);
assert(next_i < i);
i = next_i + 1; /* account for decrement */
lua_pop(L, 1);
break;
}
if (is_special_history_key(key<