1/*
2** LuaJIT frontend. Runs commands, scripts, read-eval-print (REPL) etc.
3** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h
4**
5** Major portions taken verbatim or adapted from the Lua interpreter.
6** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
7*/
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#define luajit_c
14
15#include "lua.h"
16#include "lauxlib.h"
17#include "lualib.h"
18#include "luajit.h"
19
20#include "lj_arch.h"
21
22#if LJ_TARGET_POSIX
23#include <unistd.h>
24#define lua_stdin_is_tty() isatty(0)
25#elif LJ_TARGET_WINDOWS
26#include <io.h>
27#ifdef __BORLANDC__
28#define lua_stdin_is_tty() isatty(_fileno(stdin))
29#else
30#define lua_stdin_is_tty() _isatty(_fileno(stdin))
31#endif
32#else
33#define lua_stdin_is_tty() 1
34#endif
35
36#if !LJ_TARGET_CONSOLE
37#include <signal.h>
38#endif
39
40static lua_State *globalL = NULL;
41static const char *progname = LUA_PROGNAME;
42
43#if !LJ_TARGET_CONSOLE
44static void lstop(lua_State *L, lua_Debug *ar)
45{
46 (void)ar; /* unused arg. */
47 lua_sethook(L, NULL, 0, 0);
48 /* Avoid luaL_error -- a C hook doesn't add an extra frame. */
49 luaL_where(L, 0);
50 lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1));
51 lua_error(L);
52}
53
54static void laction(int i)
55{
56 signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
57 terminate process (default action) */
58 lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
59}
60#endif
61
62static void print_usage(void)
63{
64 fputs("usage: ", stderr);
65 fputs(progname, stderr);
66 fputs(" [options]... [script [args]...].\n"
67 "Available options are:\n"
68 " -e chunk Execute string " LUA_QL("chunk") ".\n"
69 " -l name Require library " LUA_QL("name") ".\n"
70 " -b ... Save or list bytecode.\n"
71 " -j cmd Perform LuaJIT control command.\n"
72 " -O[opt] Control LuaJIT optimizations.\n"
73 " -i Enter interactive mode after executing " LUA_QL("script") ".\n"
74 " -v Show version information.\n"
75 " -E Ignore environment variables.\n"
76 " -- Stop handling options.\n"
77 " - Execute stdin and stop handling options.\n", stderr);
78 fflush(stderr);
79}
80
81static void l_message(const char *pname, const char *msg)
82{
83 if (pname) { fputs(pname, stderr); fputc(':', stderr); fputc(' ', stderr); }
84 fputs(msg, stderr); fputc('\n', stderr);
85 fflush(stderr);
86}
87
88static int report(lua_State *L, int status)
89{
90 if (status && !lua_isnil(L, -1)) {
91 const char *msg = lua_tostring(L, -1);
92 if (msg == NULL) msg = "(error object is not a string)";
93 l_message(progname, msg);
94 lua_pop(L, 1);
95 }
96 return status;
97}
98
99static int traceback(lua_State *L)
100{
101 if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */
102 if (lua_isnoneornil(L, 1) ||
103 !luaL_callmeta(L, 1, "__tostring") ||
104 !lua_isstring(L, -1))
105 return 1; /* Return non-string error object. */
106 lua_remove(L, 1); /* Replace object by result of __tostring metamethod. */
107 }
108 luaL_traceback(L, L, lua_tostring(L, 1), 1);
109 return 1;
110}
111
112static int docall(lua_State *L, int narg, int clear)
113{
114 int status;
115 int base = lua_gettop(L) - narg; /* function index */
116 lua_pushcfunction(L, traceback); /* push traceback function */
117 lua_insert(L, base); /* put it under chunk and args */
118#if !LJ_TARGET_CONSOLE
119 signal(SIGINT, laction);
120#endif
121 status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
122#if !LJ_TARGET_CONSOLE
123 signal(SIGINT, SIG_DFL);
124#endif
125 lua_remove(L, base); /* remove traceback function */
126 /* force a complete garbage collection in case of errors */
127 if (status != LUA_OK) lua_gc(L, LUA_GCCOLLECT, 0);
128 return status;
129}
130
131static void print_version(void)
132{
133 fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout);
134}
135
136static void print_jit_status(lua_State *L)
137{
138 int n;
139 const char *s;
140 lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
141 lua_getfield(L, -1, "jit"); /* Get jit.* module table. */
142 lua_remove(L, -2);
143 lua_getfield(L, -1, "status");
144 lua_remove(L, -2);
145 n = lua_gettop(L);
146 lua_call(L, 0, LUA_MULTRET);
147 fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout);
148 for (n++; (s = lua_tostring(L, n)); n++) {
149 putc(' ', stdout);
150 fputs(s, stdout);
151 }
152 putc('\n', stdout);
153 lua_settop(L, 0); /* clear stack */
154}
155
156static void createargtable(lua_State *L, char **argv, int argc, int argf)
157{
158 int i;
159 lua_createtable(L, argc - argf, argf);
160 for (i = 0; i < argc; i++) {
161 lua_pushstring(L, argv[i]);
162 lua_rawseti(L, -2, i - argf);
163 }
164 lua_setglobal(L, "arg");
165}
166
167static int dofile(lua_State *L, const char *name)
168{
169 int status = luaL_loadfile(L, name) || docall(L, 0, 1);
170 return report(L, status);
171}
172
173static int dostring(lua_State *L, const char *s, const char *name)
174{
175 int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
176 return report(L, status);
177}
178
179static int dolibrary(lua_State *L, const char *name)
180{
181 lua_getglobal(L, "require");
182 lua_pushstring(L, name);
183 return report(L, docall(L, 1, 1));
184}
185
186static void write_prompt(lua_State *L, int firstline)
187{
188 const char *p;
189 lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
190 p = lua_tostring(L, -1);
191 if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2;
192 fputs(p, stdout);
193 fflush(stdout);
194 lua_pop(L, 1); /* remove global */
195}
196
197static int incomplete(lua_State *L, int status)
198{
199 if (status == LUA_ERRSYNTAX) {
200 size_t lmsg;
201 const char *msg = lua_tolstring(L, -1, &lmsg);
202 const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
203 if (strstr(msg, LUA_QL("<eof>")) == tp) {
204 lua_pop(L, 1);
205 return 1;
206 }
207 }
208 return 0; /* else... */
209}
210
211static int pushline(lua_State *L, int firstline)
212{
213 char buf[LUA_MAXINPUT];
214 write_prompt(L, firstline);
215 if (fgets(buf, LUA_MAXINPUT, stdin)) {
216 size_t len = strlen(buf);
217 if (len > 0 && buf[len-1] == '\n')
218 buf[len-1] = '\0';
219 if (firstline && buf[0] == '=')
220 lua_pushfstring(L, "return %s", buf+1);
221 else
222 lua_pushstring(L, buf);
223 return 1;
224 }
225 return 0;
226}
227
228static int loadline(lua_State *L)
229{
230 int status;
231 lua_settop(L, 0);
232 if (!pushline(L, 1))
233 return -1; /* no input */
234 for (;;) { /* repeat until gets a complete line */
235 status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
236 if (!incomplete(L, status)) break; /* cannot try to add lines? */
237 if (!pushline(L, 0)) /* no more input? */
238 return -1;
239 lua_pushliteral(L, "\n"); /* add a new line... */
240 lua_insert(L, -2); /* ...between the two lines */
241 lua_concat(L, 3); /* join them */
242 }
243 lua_remove(L, 1); /* remove line */
244 return status;
245}
246
247static void dotty(lua_State *L)
248{
249 int status;
250 const char *oldprogname = progname;
251 progname = NULL;
252 while ((status = loadline(L)) != -1) {
253 if (status == LUA_OK) status = docall(L, 0, 0);
254 report(L, status);
255 if (status == LUA_OK && lua_gettop(L) > 0) { /* any result to print? */
256 lua_getglobal(L, "print");
257 lua_insert(L, 1);
258 if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
259 l_message(progname,
260 lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)",
261 lua_tostring(L, -1)));
262 }
263 }
264 lua_settop(L, 0); /* clear stack */
265 fputs("\n", stdout);
266 fflush(stdout);
267 progname = oldprogname;
268}
269
270static int handle_script(lua_State *L, char **argx)
271{
272 int status;
273 const char *fname = argx[0];
274 if (strcmp(fname, "-") == 0 && strcmp(argx[-1], "--") != 0)
275 fname = NULL; /* stdin */
276 status = luaL_loadfile(L, fname);
277 if (status == LUA_OK) {
278 /* Fetch args from arg table. LUA_INIT or -e might have changed them. */
279 int narg = 0;
280 lua_getglobal(L, "arg");
281 if (lua_istable(L, -1)) {
282 do {
283 narg++;
284 lua_rawgeti(L, -narg, narg);
285 } while (!lua_isnil(L, -1));
286 lua_pop(L, 1);
287 lua_remove(L, -narg);
288 narg--;
289 } else {
290 lua_pop(L, 1);
291 }
292 status = docall(L, narg, 0);
293 }
294 return report(L, status);
295}
296
297/* Load add-on module. */
298static int loadjitmodule(lua_State *L)
299{
300 lua_getglobal(L, "require");
301 lua_pushliteral(L, "jit.");
302 lua_pushvalue(L, -3);
303 lua_concat(L, 2);
304 if (lua_pcall(L, 1, 1, 0)) {
305 const char *msg = lua_tostring(L, -1);
306 if (msg && !strncmp(msg, "module ", 7))
307 goto nomodule;
308 return report(L, 1);
309 }
310 lua_getfield(L, -1, "start");
311 if (lua_isnil(L, -1)) {
312 nomodule:
313 l_message(progname,
314 "unknown luaJIT command or jit.* modules not installed");
315 return 1;
316 }
317 lua_remove(L, -2); /* Drop module table. */
318 return 0;
319}
320
321/* Run command with options. */
322static int runcmdopt(lua_State *L, const char *opt)
323{
324 int narg = 0;
325 if (opt && *opt) {
326 for (;;) { /* Split arguments. */
327 const char *p = strchr(opt, ',');
328 narg++;
329 if (!p) break;
330 if (p == opt)
331 lua_pushnil(L);
332 else
333 lua_pushlstring(L, opt, (size_t)(p - opt));
334 opt = p + 1;
335 }
336 if (*opt)
337 lua_pushstring(L, opt);
338 else
339 lua_pushnil(L);
340 }
341 return report(L, lua_pcall(L, narg, 0, 0));
342}
343
344/* JIT engine control command: try jit library first or load add-on module. */
345static int dojitcmd(lua_State *L, const char *cmd)
346{
347 const char *opt = strchr(cmd, '=');
348 lua_pushlstring(L, cmd, opt ? (size_t)(opt - cmd) : strlen(cmd));
349 lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
350 lua_getfield(L, -1, "jit"); /* Get jit.* module table. */
351 lua_remove(L, -2);
352 lua_pushvalue(L, -2);
353 lua_gettable(L, -2); /* Lookup library function. */
354 if (!lua_isfunction(L, -1)) {
355 lua_pop(L, 2); /* Drop non-function and jit.* table, keep module name. */
356 if (loadjitmodule(L))
357 return 1;
358 } else {
359 lua_remove(L, -2); /* Drop jit.* table. */
360 }
361 lua_remove(L, -2); /* Drop module name. */
362 return runcmdopt(L, opt ? opt+1 : opt);
363}
364
365/* Optimization flags. */
366static int dojitopt(lua_State *L, const char *opt)
367{
368 lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
369 lua_getfield(L, -1, "jit.opt"); /* Get jit.opt.* module table. */
370 lua_remove(L, -2);
371 lua_getfield(L, -1, "start");
372 lua_remove(L, -2);
373 return runcmdopt(L, opt);
374}
375
376/* Save or list bytecode. */
377static int dobytecode(lua_State *L, char **argv)
378{
379 int narg = 0;
380 lua_pushliteral(L, "bcsave");
381 if (loadjitmodule(L))
382 return 1;
383 if (argv[0][2]) {
384 narg++;
385 argv[0][1] = '-';
386 lua_pushstring(L, argv[0]+1);
387 }
388 for (argv++; *argv != NULL; narg++, argv++)
389 lua_pushstring(L, *argv);
390 report(L, lua_pcall(L, narg, 0, 0));
391 return -1;
392}
393
394/* check that argument has no extra characters at the end */
395#define notail(x) {if ((x)[2] != '\0') return -1;}
396
397#define FLAGS_INTERACTIVE 1
398#define FLAGS_VERSION 2
399#define FLAGS_EXEC 4
400#define FLAGS_OPTION 8
401#define FLAGS_NOENV 16
402
403static int collectargs(char **argv, int *flags)
404{
405 int i;
406 for (i = 1; argv[i] != NULL; i++) {
407 if (argv[i][0] != '-') /* Not an option? */
408 return i;
409 switch (argv[i][1]) { /* Check option. */
410 case '-':
411 notail(argv[i]);
412 return i+1;
413 case '\0':
414 return i;
415 case 'i':
416 notail(argv[i]);
417 *flags |= FLAGS_INTERACTIVE;
418 /* fallthrough */
419 case 'v':
420 notail(argv[i]);
421 *flags |= FLAGS_VERSION;
422 break;
423 case 'e':
424 *flags |= FLAGS_EXEC;
425 /* fallthrough */
426 case 'j': /* LuaJIT extension */
427 case 'l':
428 *flags |= FLAGS_OPTION;
429 if (argv[i][2] == '\0') {
430 i++;
431 if (argv[i] == NULL) return -1;
432 }
433 break;
434 case 'O': break; /* LuaJIT extension */
435 case 'b': /* LuaJIT extension */
436 if (*flags) return -1;
437 *flags |= FLAGS_EXEC;
438 return i+1;
439 case 'E':
440 *flags |= FLAGS_NOENV;
441 break;
442 default: return -1; /* invalid option */
443 }
444 }
445 return i;
446}
447
448static int runargs(lua_State *L, char **argv, int argn)
449{
450 int i;
451 for (i = 1; i < argn; i++) {
452 if (argv[i] == NULL) continue;
453 lua_assert(argv[i][0] == '-');
454 switch (argv[i][1]) {
455 case 'e': {
456 const char *chunk = argv[i] + 2;
457 if (*chunk == '\0') chunk = argv[++i];
458 lua_assert(chunk != NULL);
459 if (dostring(L, chunk, "=(command line)") != 0)
460 return 1;
461 break;
462 }
463 case 'l': {
464 const char *filename = argv[i] + 2;
465 if (*filename == '\0') filename = argv[++i];
466 lua_assert(filename != NULL);
467 if (dolibrary(L, filename))
468 return 1;
469 break;
470 }
471 case 'j': { /* LuaJIT extension. */
472 const char *cmd = argv[i] + 2;
473 if (*cmd == '\0') cmd = argv[++i];
474 lua_assert(cmd != NULL);
475 if (dojitcmd(L, cmd))
476 return 1;
477 break;
478 }
479 case 'O': /* LuaJIT extension. */
480 if (dojitopt(L, argv[i] + 2))
481 return 1;
482 break;
483 case 'b': /* LuaJIT extension. */
484 return dobytecode(L, argv+i);
485 default: break;
486 }
487 }
488 return LUA_OK;
489}
490
491static int handle_luainit(lua_State *L)
492{
493#if LJ_TARGET_CONSOLE
494 const char *init = NULL;
495#else
496 const char *init = getenv(LUA_INIT);
497#endif
498 if (init == NULL)
499 return LUA_OK;
500 else if (init[0] == '@')
501 return dofile(L, init+1);
502 else
503 return dostring(L, init, "=" LUA_INIT);
504}
505
506static struct Smain {
507 char **argv;
508 int argc;
509 int status;
510} smain;
511
512static int pmain(lua_State *L)
513{
514 struct Smain *s = &smain;
515 char **argv = s->argv;
516 int argn;
517 int flags = 0;
518 globalL = L;
519 if (argv[0] && argv[0][0]) progname = argv[0];
520
521 LUAJIT_VERSION_SYM(); /* Linker-enforced version check. */
522
523 argn = collectargs(argv, &flags);
524 if (argn < 0) { /* Invalid args? */
525 print_usage();
526 s->status = 1;
527 return 0;
528 }
529
530 if ((flags & FLAGS_NOENV)) {
531 lua_pushboolean(L, 1);
532 lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
533 }
534
535 /* Stop collector during library initialization. */
536 lua_gc(L, LUA_GCSTOP, 0);
537 luaL_openlibs(L);
538 lua_gc(L, LUA_GCRESTART, -1);
539
540 createargtable(L, argv, s->argc, argn);
541
542 if (!(flags & FLAGS_NOENV)) {
543 s->status = handle_luainit(L);
544 if (s->status != LUA_OK) return 0;
545 }
546
547 if ((flags & FLAGS_VERSION)) print_version();
548
549 s->status = runargs(L, argv, argn);
550 if (s->status != LUA_OK) return 0;
551
552 if (s->argc > argn) {
553 s->status = handle_script(L, argv + argn);
554 if (s->status != LUA_OK) return 0;
555 }
556
557 if ((flags & FLAGS_INTERACTIVE)) {
558 print_jit_status(L);
559 dotty(L);
560 } else if (s->argc == argn && !(flags & (FLAGS_EXEC|FLAGS_VERSION))) {
561 if (lua_stdin_is_tty()) {
562 print_version();
563 print_jit_status(L);
564 dotty(L);
565 } else {
566 dofile(L, NULL); /* Executes stdin as a file. */
567 }
568 }
569 return 0;
570}
571
572int main(int argc, char **argv)
573{
574 int status;
575 lua_State *L = lua_open();
576 if (L == NULL) {
577 l_message(argv[0], "cannot create state: not enough memory");
578 return EXIT_FAILURE;
579 }
580 smain.argc = argc;
581 smain.argv = argv;
582 status = lua_cpcall(L, pmain, NULL);
583 report(L, status);
584 lua_close(L);
585 return (status || smain.status > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
586}
587
588