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