#define lua_c
#include "lprefix.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#if !defined(LUA_PROMPT)
#define LUA_PROMPT "> "
#define LUA_PROMPT2 ">> "
#endif
#if !defined(LUA_PROGNAME)
#define LUA_PROGNAME "lua"
#endif
#if !defined(LUA_MAXINPUT)
#define LUA_MAXINPUT 512
#endif
#if !defined(LUA_INIT_VAR)
#define LUA_INIT_VAR "LUA_INIT"
#endif
#define LUA_INITVARVERSION \
LUA_INIT_VAR
"_"
LUA_VERSION_MAJOR
"_"
LUA_VERSION_MINOR
#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 /* assume stdin is a tty */
#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 = LUA_PROGNAME;
static
void
lstop (lua_State *L, lua_Debug *ar) {
(
void
)ar;
marpa_lua_sethook(L, NULL, 0, 0);
marpa_luaL_error(L,
"interrupted!"
);
}
static
void
laction (
int
i) {
signal
(i, SIG_DFL);
marpa_lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}
static
void
print_usage (
const
char
*badoption) {
marpa_lua_writestringerror(
"%s: "
, progname);
if
(badoption[1] ==
'e'
|| badoption[1] ==
'l'
)
marpa_lua_writestringerror(
"'%s' needs argument\n"
, badoption);
else
marpa_lua_writestringerror(
"unrecognized option '%s'\n"
, badoption);
marpa_lua_writestringerror(
"usage: %s [options] [script [args]]\n"
"Available options are:\n"
" -e stat execute string 'stat'\n"
" -i enter interactive mode after executing 'script'\n"
" -l name require library 'name'\n"
" -v show version information\n"
" -E ignore environment variables\n"
" -- stop handling options\n"
" - stop handling options and execute stdin\n"
,
progname);
}
static
void
l_message (
const
char
*pname,
const
char
*msg) {
if
(pname) marpa_lua_writestringerror(
"%s: "
, pname);
marpa_lua_writestringerror(
"%s\n"
, msg);
}
static
int
report (lua_State *L,
int
status) {
if
(status != LUA_OK) {
const
char
*msg = marpa_lua_tostring(L, -1);
l_message(progname, msg);
marpa_lua_pop(L, 1);
}
return
status;
}
static
int
msghandler (lua_State *L) {
const
char
*msg = marpa_lua_tostring(L, 1);
if
(msg == NULL) {
if
(marpa_luaL_callmeta(L, 1,
"__tostring"
) &&
marpa_lua_type(L, -1) == LUA_TSTRING)
return
1;
else
msg = marpa_lua_pushfstring(L,
"(error object is a %s value)"
,
marpa_luaL_typename(L, 1));
}
marpa_luaL_traceback(L, L, msg, 1);
return
1;
}
static
int
docall (lua_State *L,
int
narg,
int
nres) {
int
status;
int
base = marpa_lua_gettop(L) - narg;
marpa_lua_pushcfunction(L, msghandler);
marpa_lua_insert(L, base);
globalL = L;
signal
(SIGINT, laction);
status = marpa_lua_pcall(L, narg, nres, base);
signal
(SIGINT, SIG_DFL);
marpa_lua_remove(L, base);
return
status;
}
static
void
print_version (
void
) {
marpa_lua_writestring(LUA_COPYRIGHT,
strlen
(LUA_COPYRIGHT));
marpa_lua_writeline();
}
static
void
createargtable (lua_State *L,
char
**argv,
int
argc,
int
script) {
int
i, narg;
if
(script == argc) script = 0;
narg = argc - (script + 1);
marpa_lua_createtable(L, narg, script + 1);
for
(i = 0; i < argc; i++) {
marpa_lua_pushstring(L, argv[i]);
marpa_lua_rawseti(L, -2, i - script);
}
marpa_lua_setglobal(L,
"arg"
);
}
static
int
dochunk (lua_State *L,
int
status) {
if
(status == LUA_OK) status = docall(L, 0, 0);
return
report(L, status);
}
static
int
dofile (lua_State *L,
const
char
*name) {
return
dochunk(L, marpa_luaL_loadfile(L, name));
}
static
int
dostring (lua_State *L,
const
char
*s,
const
char
*name) {
return
dochunk(L, marpa_luaL_loadbuffer(L, s,
strlen
(s), name));
}
static
int
dolibrary (lua_State *L,
const
char
*name) {
int
status;
marpa_lua_getglobal(L,
"require"
);
marpa_lua_pushstring(L, name);
status = docall(L, 1, 1);
if
(status == LUA_OK)
marpa_lua_setglobal(L, name);
return
report(L, status);
}
static
const
char
*get_prompt (lua_State *L,
int
firstline) {
const
char
*p;
marpa_lua_getglobal(L, firstline ?
"_PROMPT"
:
"_PROMPT2"
);
p = marpa_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 = marpa_lua_tolstring(L, -1, &lmsg);
if
(lmsg >= marklen &&
strcmp
(msg + lmsg - marklen, EOFMARK) == 0) {
marpa_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;
marpa_lua_pop(L, 1);
l =
strlen
(b);
if
(l > 0 && b[l-1] ==
'\n'
)
b[--l] =
'\0'
;
if
(firstline && b[0] ==
'='
)
marpa_lua_pushfstring(L,
"return %s"
, b + 1);
else
marpa_lua_pushlstring(L, b, l);
lua_freeline(L, b);
return
1;
}
static
int
addreturn (lua_State *L) {
const
char
*line = marpa_lua_tostring(L, -1);
const
char
*retline = marpa_lua_pushfstring(L,
"return %s;"
, line);
int
status = marpa_luaL_loadbuffer(L, retline,
strlen
(retline),
"=stdin"
);
if
(status == LUA_OK) {
marpa_lua_remove(L, -2);
if
(line[0] !=
'\0'
)
lua_saveline(L, line);
}
else
marpa_lua_pop(L, 2);
return
status;
}
static
int
multiline (lua_State *L) {
for
(;;) {
size_t
len;
const
char
*line = marpa_lua_tolstring(L, 1, &len);
int
status = marpa_luaL_loadbuffer(L, line, len,
"=stdin"
);
if
(!incomplete(L, status) || !pushline(L, 0)) {
lua_saveline(L, line);
return
status;
}
marpa_lua_pushliteral(L,
"\n"
);
marpa_lua_insert(L, -2);
marpa_lua_concat(L, 3);
}
}
static
int
loadline (lua_State *L) {
int
status;
marpa_lua_settop(L, 0);
if
(!pushline(L, 1))
return
-1;
if
((status = addreturn(L)) != LUA_OK)
status = multiline(L);
marpa_lua_remove(L, 1);
lua_assert(marpa_lua_gettop(L) == 1);
return
status;
}
static
void
l_print (lua_State *L) {
int
n = marpa_lua_gettop(L);
if
(n > 0) {
marpa_luaL_checkstack(L, LUA_MINSTACK,
"too many results to print"
);
marpa_lua_getglobal(L,
"print"
);
marpa_lua_insert(L, 1);
if
(marpa_lua_pcall(L, n, 0, 0) != LUA_OK)
l_message(progname, marpa_lua_pushfstring(L,
"error calling 'print' (%s)"
,
marpa_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);
}
marpa_lua_settop(L, 0);
marpa_lua_writeline();
progname = oldprogname;
}
static
int
pushargs (lua_State *L) {
int
i, n;
if
(marpa_lua_getglobal(L,
"arg"
) != LUA_TTABLE)
marpa_luaL_error(L,
"'arg' is not a table"
);
n = (
int
)marpa_luaL_len(L, -1);
marpa_luaL_checkstack(L, n + 3,
"too many arguments to script"
);
for
(i = 1; i <= n; i++)
marpa_lua_rawgeti(L, -i, i);
marpa_lua_remove(L, -i);
return
n;
}
static
int
handle_script (lua_State *L,
char
**argv) {
int
status;
const
char
*fname = argv[0];
if
(
strcmp
(fname,
"-"
) == 0 &&
strcmp
(argv[-1],
"--"
) != 0)
fname = NULL;
status = marpa_luaL_loadfile(L, fname);
if
(status == LUA_OK) {
int
n = pushargs(L);
status = docall(L, n, LUA_MULTRET);
}
return
report(L, status);
}
#define has_error 1 /* bad option */
#define has_i 2 /* -i */
#define has_v 4 /* -v */
#define has_e 8 /* -e */
#define has_E 16 /* -E */
static
int
collectargs (
char
**argv,
int
*first) {
int
args = 0;
int
i;
for
(i = 1; argv[i] != NULL; i++) {
*first = i;
if
(argv[i][0] !=
'-'
)
return
args;
switch
(argv[i][1]) {
case
'-'
:
if
(argv[i][2] !=
'\0'
)
return
has_error;
*first = i + 1;
return
args;
case
'\0'
:
return
args;
case
'E'
:
if
(argv[i][2] !=
'\0'
)
return
has_error;
args |= has_E;
break
;
case
'i'
:
args |= has_i;
case
'v'
:
if
(argv[i][2] !=
'\0'
)
return
has_error;
args |= has_v;
break
;
case
'e'
:
args |= has_e;
case
'l'
:
if
(argv[i][2] ==
'\0'
) {
i++;
if
(argv[i] == NULL || argv[i][0] ==
'-'
)
return
has_error;
}
break
;
default
:
return
has_error;
}
}
*first = i;
return
args;
}
static
int
runargs (lua_State *L,
char
**argv,
int
n) {
int
i;
for
(i = 1; i < n; i++) {
int
option = argv[i][1];
lua_assert(argv[i][0] ==
'-'
);
if
(option ==
'e'
|| option ==
'l'
) {
int
status;
const
char
*extra = argv[i] + 2;
if
(*extra ==
'\0'
) extra = argv[++i];
lua_assert(extra != NULL);
status = (option ==
'e'
)
? dostring(L, extra,
"=(command line)"
)
: dolibrary(L, extra);
if
(status != LUA_OK)
return
0;
}
}
return
1;
}
static
int
handle_luainit (lua_State *L) {
const
char
*name =
"="
LUA_INITVARVERSION;
const
char
*init =
getenv
(name + 1);
if
(init == NULL) {
name =
"="
LUA_INIT_VAR;
init =
getenv
(name + 1);
}
if
(init == NULL)
return
LUA_OK;
else
if
(init[0] ==
'@'
)
return
dofile(L, init+1);
else
return
dostring(L, init, name);
}
static
int
pmain (lua_State *L) {
int
argc = (
int
)marpa_lua_tointeger(L, 1);
char
**argv = (
char
**)marpa_lua_touserdata(L, 2);
int
script;
int
args = collectargs(argv, &script);
marpa_luaL_checkversion(L);
if
(argv[0] && argv[0][0]) progname = argv[0];
if
(args == has_error) {
print_usage(argv[script]);
return
0;
}
if
(args & has_v)
print_version();
if
(args & has_E) {
marpa_lua_pushboolean(L, 1);
marpa_lua_setfield(L, LUA_REGISTRYINDEX,
"LUA_NOENV"
);
}
marpa_luaL_openlibs(L);
createargtable(L, argv, argc, script);
if
(!(args & has_E)) {
if
(handle_luainit(L) != LUA_OK)
return
0;
}
if
(!runargs(L, argv, script))
return
0;
if
(script < argc &&
handle_script(L, argv + script) != LUA_OK)
return
0;
if
(args & has_i)
doREPL(L);
else
if
(script == argc && !(args & (has_e | has_v))) {
if
(lua_stdin_is_tty()) {
print_version();
doREPL(L);
}
else
dofile(L, NULL);
}
marpa_lua_pushboolean(L, 1);
return
1;
}
int
main (
int
argc,
char
**argv) {
int
status, result;
lua_State *L = marpa_luaL_newstate();
if
(L == NULL) {
l_message(argv[0],
"cannot create state: not enough memory"
);
return
EXIT_FAILURE;
}
marpa_lua_pushcfunction(L, &pmain);
marpa_lua_pushinteger(L, argc);
marpa_lua_pushlightuserdata(L, argv);
status = marpa_lua_pcall(L, 2, 1, 0);
result = marpa_lua_toboolean(L, -1);
report(L, status);
marpa_lua_close(L);
return
(result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}