xref: /netbsd-src/external/mit/lua/dist/src/lua.c (revision a536ee5124e62c9a0051a252f7833dc8f50f44c9)
1 /*	$NetBSD: lua.c,v 1.1.1.2 2012/03/15 00:08:11 alnsn Exp $	*/
2 
3 /*
4 ** $Id: lua.c,v 1.1.1.2 2012/03/15 00:08:11 alnsn Exp $
5 ** Lua stand-alone interpreter
6 ** See Copyright Notice in lua.h
7 */
8 
9 
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #define lua_c
16 
17 #include "lua.h"
18 
19 #include "lauxlib.h"
20 #include "lualib.h"
21 
22 
23 
24 static lua_State *globalL = NULL;
25 
26 static const char *progname = LUA_PROGNAME;
27 
28 
29 
30 static void lstop (lua_State *L, lua_Debug *ar) {
31   (void)ar;  /* unused arg. */
32   lua_sethook(L, NULL, 0, 0);
33   luaL_error(L, "interrupted!");
34 }
35 
36 
37 static void laction (int i) {
38   signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
39                               terminate process (default action) */
40   lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
41 }
42 
43 
44 static void print_usage (void) {
45   fprintf(stderr,
46   "usage: %s [options] [script [args]].\n"
47   "Available options are:\n"
48   "  -e stat  execute string " LUA_QL("stat") "\n"
49   "  -l name  require library " LUA_QL("name") "\n"
50   "  -i       enter interactive mode after executing " LUA_QL("script") "\n"
51   "  -v       show version information\n"
52   "  --       stop handling options\n"
53   "  -        execute stdin and stop handling options\n"
54   ,
55   progname);
56   fflush(stderr);
57 }
58 
59 
60 static void l_message (const char *pname, const char *msg) {
61   if (pname) fprintf(stderr, "%s: ", pname);
62   fprintf(stderr, "%s\n", msg);
63   fflush(stderr);
64 }
65 
66 
67 static int report (lua_State *L, int status) {
68   if (status && !lua_isnil(L, -1)) {
69     const char *msg = lua_tostring(L, -1);
70     if (msg == NULL) msg = "(error object is not a string)";
71     l_message(progname, msg);
72     lua_pop(L, 1);
73   }
74   return status;
75 }
76 
77 
78 static int traceback (lua_State *L) {
79   if (!lua_isstring(L, 1))  /* 'message' not a string? */
80     return 1;  /* keep it intact */
81   lua_getfield(L, LUA_GLOBALSINDEX, "debug");
82   if (!lua_istable(L, -1)) {
83     lua_pop(L, 1);
84     return 1;
85   }
86   lua_getfield(L, -1, "traceback");
87   if (!lua_isfunction(L, -1)) {
88     lua_pop(L, 2);
89     return 1;
90   }
91   lua_pushvalue(L, 1);  /* pass error message */
92   lua_pushinteger(L, 2);  /* skip this function and traceback */
93   lua_call(L, 2, 1);  /* call debug.traceback */
94   return 1;
95 }
96 
97 
98 static int docall (lua_State *L, int narg, int clear) {
99   int status;
100   int base = lua_gettop(L) - narg;  /* function index */
101   lua_pushcfunction(L, traceback);  /* push traceback function */
102   lua_insert(L, base);  /* put it under chunk and args */
103   signal(SIGINT, laction);
104   status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
105   signal(SIGINT, SIG_DFL);
106   lua_remove(L, base);  /* remove traceback function */
107   /* force a complete garbage collection in case of errors */
108   if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
109   return status;
110 }
111 
112 
113 static void print_version (void) {
114   l_message(NULL, LUA_RELEASE "  " LUA_COPYRIGHT);
115 }
116 
117 
118 static int getargs (lua_State *L, char **argv, int n) {
119   int narg;
120   int i;
121   int argc = 0;
122   while (argv[argc]) argc++;  /* count total number of arguments */
123   narg = argc - (n + 1);  /* number of arguments to the script */
124   luaL_checkstack(L, narg + 3, "too many arguments to script");
125   for (i=n+1; i < argc; i++)
126     lua_pushstring(L, argv[i]);
127   lua_createtable(L, narg, n + 1);
128   for (i=0; i < argc; i++) {
129     lua_pushstring(L, argv[i]);
130     lua_rawseti(L, -2, i - n);
131   }
132   return narg;
133 }
134 
135 
136 static int dofile (lua_State *L, const char *name) {
137   int status = luaL_loadfile(L, name) || docall(L, 0, 1);
138   return report(L, status);
139 }
140 
141 
142 static int dostring (lua_State *L, const char *s, const char *name) {
143   int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
144   return report(L, status);
145 }
146 
147 
148 static int dolibrary (lua_State *L, const char *name) {
149   lua_getglobal(L, "require");
150   lua_pushstring(L, name);
151   return report(L, docall(L, 1, 1));
152 }
153 
154 
155 static const char *get_prompt (lua_State *L, int firstline) {
156   const char *p;
157   lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
158   p = lua_tostring(L, -1);
159   if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
160   lua_pop(L, 1);  /* remove global */
161   return p;
162 }
163 
164 
165 static int incomplete (lua_State *L, int status) {
166   if (status == LUA_ERRSYNTAX) {
167     size_t lmsg;
168     const char *msg = lua_tolstring(L, -1, &lmsg);
169     const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
170     if (strstr(msg, LUA_QL("<eof>")) == tp) {
171       lua_pop(L, 1);
172       return 1;
173     }
174   }
175   return 0;  /* else... */
176 }
177 
178 
179 static int pushline (lua_State *L, int firstline) {
180   char buffer[LUA_MAXINPUT];
181   char *b = buffer;
182   size_t l;
183   const char *prmt = get_prompt(L, firstline);
184   if (lua_readline(L, b, prmt) == 0)
185     return 0;  /* no input */
186   l = strlen(b);
187   if (l > 0 && b[l-1] == '\n')  /* line ends with newline? */
188     b[l-1] = '\0';  /* remove it */
189   if (firstline && b[0] == '=')  /* first line starts with `=' ? */
190     lua_pushfstring(L, "return %s", b+1);  /* change it to `return' */
191   else
192     lua_pushstring(L, b);
193   lua_freeline(L, b);
194   return 1;
195 }
196 
197 
198 static int loadline (lua_State *L) {
199   int status;
200   lua_settop(L, 0);
201   if (!pushline(L, 1))
202     return -1;  /* no input */
203   for (;;) {  /* repeat until gets a complete line */
204     status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
205     if (!incomplete(L, status)) break;  /* cannot try to add lines? */
206     if (!pushline(L, 0))  /* no more input? */
207       return -1;
208     lua_pushliteral(L, "\n");  /* add a new line... */
209     lua_insert(L, -2);  /* ...between the two lines */
210     lua_concat(L, 3);  /* join them */
211   }
212   lua_saveline(L, 1);
213   lua_remove(L, 1);  /* remove line */
214   return status;
215 }
216 
217 
218 static void dotty (lua_State *L) {
219   int status;
220   const char *oldprogname = progname;
221   progname = NULL;
222   while ((status = loadline(L)) != -1) {
223     if (status == 0) status = docall(L, 0, 0);
224     report(L, status);
225     if (status == 0 && lua_gettop(L) > 0) {  /* any result to print? */
226       lua_getglobal(L, "print");
227       lua_insert(L, 1);
228       if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
229         l_message(progname, lua_pushfstring(L,
230                                "error calling " LUA_QL("print") " (%s)",
231                                lua_tostring(L, -1)));
232     }
233   }
234   lua_settop(L, 0);  /* clear stack */
235   fputs("\n", stdout);
236   fflush(stdout);
237   progname = oldprogname;
238 }
239 
240 
241 static int handle_script (lua_State *L, char **argv, int n) {
242   int status;
243   const char *fname;
244   int narg = getargs(L, argv, n);  /* collect arguments */
245   lua_setglobal(L, "arg");
246   fname = argv[n];
247   if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
248     fname = NULL;  /* stdin */
249   status = luaL_loadfile(L, fname);
250   lua_insert(L, -(narg+1));
251   if (status == 0)
252     status = docall(L, narg, 0);
253   else
254     lua_pop(L, narg);
255   return report(L, status);
256 }
257 
258 
259 /* check that argument has no extra characters at the end */
260 #define notail(x)	{if ((x)[2] != '\0') return -1;}
261 
262 
263 static int collectargs (char **argv, int *pi, int *pv, int *pe) {
264   int i;
265   for (i = 1; argv[i] != NULL; i++) {
266     if (argv[i][0] != '-')  /* not an option? */
267         return i;
268     switch (argv[i][1]) {  /* option */
269       case '-':
270         notail(argv[i]);
271         return (argv[i+1] != NULL ? i+1 : 0);
272       case '\0':
273         return i;
274       case 'i':
275         notail(argv[i]);
276         *pi = 1;  /* go through */
277       case 'v':
278         notail(argv[i]);
279         *pv = 1;
280         break;
281       case 'e':
282         *pe = 1;  /* go through */
283       case 'l':
284         if (argv[i][2] == '\0') {
285           i++;
286           if (argv[i] == NULL) return -1;
287         }
288         break;
289       default: return -1;  /* invalid option */
290     }
291   }
292   return 0;
293 }
294 
295 
296 static int runargs (lua_State *L, char **argv, int n) {
297   int i;
298   for (i = 1; i < n; i++) {
299     if (argv[i] == NULL) continue;
300     lua_assert(argv[i][0] == '-');
301     switch (argv[i][1]) {  /* option */
302       case 'e': {
303         const char *chunk = argv[i] + 2;
304         if (*chunk == '\0') chunk = argv[++i];
305         lua_assert(chunk != NULL);
306         if (dostring(L, chunk, "=(command line)") != 0)
307           return 1;
308         break;
309       }
310       case 'l': {
311         const char *filename = argv[i] + 2;
312         if (*filename == '\0') filename = argv[++i];
313         lua_assert(filename != NULL);
314         if (dolibrary(L, filename))
315           return 1;  /* stop if file fails */
316         break;
317       }
318       default: break;
319     }
320   }
321   return 0;
322 }
323 
324 
325 static int handle_luainit (lua_State *L) {
326   const char *init = getenv(LUA_INIT);
327   if (init == NULL) return 0;  /* status OK */
328   else if (init[0] == '@')
329     return dofile(L, init+1);
330   else
331     return dostring(L, init, "=" LUA_INIT);
332 }
333 
334 
335 struct Smain {
336   int argc;
337   char **argv;
338   int status;
339 };
340 
341 
342 static int pmain (lua_State *L) {
343   struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
344   char **argv = s->argv;
345   int script;
346   int has_i = 0, has_v = 0, has_e = 0;
347   globalL = L;
348   if (argv[0] && argv[0][0]) progname = argv[0];
349   lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */
350   luaL_openlibs(L);  /* open libraries */
351   lua_gc(L, LUA_GCRESTART, 0);
352   s->status = handle_luainit(L);
353   if (s->status != 0) return 0;
354   script = collectargs(argv, &has_i, &has_v, &has_e);
355   if (script < 0) {  /* invalid args? */
356     print_usage();
357     s->status = 1;
358     return 0;
359   }
360   if (has_v) print_version();
361   s->status = runargs(L, argv, (script > 0) ? script : s->argc);
362   if (s->status != 0) return 0;
363   if (script)
364     s->status = handle_script(L, argv, script);
365   if (s->status != 0) return 0;
366   if (has_i)
367     dotty(L);
368   else if (script == 0 && !has_e && !has_v) {
369     if (lua_stdin_is_tty()) {
370       print_version();
371       dotty(L);
372     }
373     else dofile(L, NULL);  /* executes stdin as a file */
374   }
375   return 0;
376 }
377 
378 
379 int main (int argc, char **argv) {
380   int status;
381   struct Smain s;
382   lua_State *L = lua_open();  /* create state */
383   if (L == NULL) {
384     l_message(argv[0], "cannot create state: not enough memory");
385     return EXIT_FAILURE;
386   }
387   s.argc = argc;
388   s.argv = argv;
389   status = lua_cpcall(L, &pmain, &s);
390   report(L, status);
391   lua_close(L);
392   return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
393 }
394 
395