1 /*
2 ** $Id$
3 ** Standard I/O (and system) library
4 ** See Copyright Notice in lua.h
5 */
6 
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #define liolib_c
13 #define LUA_LIB
14 
15 #include "lua.h"
16 
17 #include "lauxlib.h"
18 #include "lualib.h"
19 #include "common/textconsole.h"
20 #include "scummvm_file.h"
21 
22 
23 #define IO_INPUT	1
24 #define IO_OUTPUT	2
25 
26 
27 //static const char *const fnames[] = {"input", "output"};
28 
29 
pushresult(lua_State * L,int i,const char * filename)30 static int pushresult (lua_State *L, int i, const char *filename) {
31   int en = 0; /*errno;*/  // Currently hardcoded for ScumMVM, this may need to be changed
32   if (i) {
33     lua_pushboolean(L, 1);
34     return 1;
35   }
36   else {
37     lua_pushnil(L);
38     if (filename)
39       lua_pushfstring(L, "%s: %s", filename, "General error" /*strerror(en)*/);
40     else
41       lua_pushfstring(L, "%s", "General error" /*strerror(en)*/);
42     lua_pushinteger(L, en);
43     return 3;
44   }
45 }
46 
47 /*
48 static void fileerror (lua_State *L, int arg, const char *filename) {
49   lua_pushfstring(L, "%s: %s", filename, "LUA I/O error descriptions have been removed in ScummVM");
50   luaL_argerror(L, arg, lua_tostring(L, -1));
51 }
52 */
53 
54 #define tofilep(L)	((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
55 #define tofileProxy(L)	((Lua::LuaFileProxy **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
56 
io_type(lua_State * L)57 static int io_type (lua_State *L) {
58 	return luaL_error(L, "%s", "LUA I/O has been removed in ScummVM");
59 /*
60   void *ud;
61   luaL_checkany(L, 1);
62   ud = lua_touserdata(L, 1);
63   lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
64   if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
65     lua_pushnil(L);  // not a file
66   else if (*((FILE **)ud) == NULL)
67     lua_pushliteral(L, "closed file");
68   else
69     lua_pushliteral(L, "file");
70   return 1;
71 */
72 }
73 
tofile(lua_State * L)74 static Lua::LuaFileProxy *tofile (lua_State *L) {
75   Lua::LuaFileProxy **f = tofileProxy(L);
76   if (*f == NULL)
77     luaL_error(L, "attempt to use a closed file");
78   return *f;
79 }
80 
81 
82 /*
83 ** When creating file handles, always creates a `closed' file handle
84 ** before opening the actual file; so, if there is a memory error, the
85 ** file is not left opened.
86 */
newfile(lua_State * L)87 static Lua::LuaFileProxy **newfile (lua_State *L) {
88 	Lua::LuaFileProxy **pf = (Lua::LuaFileProxy **)lua_newuserdata(L, sizeof(Lua::LuaFileProxy *));
89   *pf = NULL;  /* file handle is currently `closed' */
90   luaL_getmetatable(L, LUA_FILEHANDLE);
91   lua_setmetatable(L, -2);
92   return pf;
93 }
94 
95 
96 /*
97 ** function to (not) close the standard files stdin, stdout, and stderr
98 */
io_noclose(lua_State * L)99 static int io_noclose (lua_State *L) {
100   lua_pushnil(L);
101   lua_pushliteral(L, "cannot close standard file");
102   return 2;
103 }
104 
105 
106 /*
107 ** function to close 'popen' files
108 */
io_pclose(lua_State * L)109 static int io_pclose (lua_State *L) {
110 	error("LUA I/O has been removed in ScummVM");
111 /*
112   FILE **p = tofilep(L);
113   int ok = lua_pclose(L, *p);
114   *p = NULL;
115   return pushresult(L, ok, NULL);
116   */
117 }
118 
119 
120 /*
121 ** function to close regular files
122 */
io_fclose(lua_State * L)123 static int io_fclose (lua_State *L) {
124 	error("LUA I/O has been removed in ScummVM");
125 	/*
126   FILE **p = tofilep(L);
127   int ok = (fclose(*p) == 0);
128   *p = NULL;
129   return pushresult(L, ok, NULL);
130   */
131 }
132 
133 /*
134 static int aux_close (lua_State *L) {
135   lua_getfenv(L, 1);
136   lua_getfield(L, -1, "__close");
137   return (lua_tocfunction(L, -1))(L);
138 }
139 */
140 
io_close(lua_State * L)141 static int io_close (lua_State *L) {
142   if (lua_isnone(L, 1))
143     lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
144 
145   Lua::LuaFileProxy **f = tofileProxy(L);
146   delete *f;
147   *f = NULL;
148 
149   return 0;
150 }
151 
152 
io_gc(lua_State * L)153 static int io_gc (lua_State *L) {
154   Lua::LuaFileProxy **f = tofileProxy(L);
155   // ignore closed files
156   if (*f != NULL)
157     delete *f;
158 
159   return 0;
160 }
161 
162 
io_tostring(lua_State * L)163 static int io_tostring (lua_State *L) {
164   error("LUA I/O has been removed in ScummVM");
165   /*
166   FILE *f = *tofilep(L);
167   if (f == NULL)
168     lua_pushliteral(L, "file (closed)");
169   else
170     lua_pushfstring(L, "file (%p)", f);
171   return 1;
172   */
173 }
174 
175 
io_open(lua_State * L)176 static int io_open (lua_State *L) {
177   const char *filename = luaL_checkstring(L, 1);
178   const char *mode = luaL_optstring(L, 2, "r");
179   Lua::LuaFileProxy **pf = newfile(L);
180   *pf = Lua::LuaFileProxy::create(filename, mode);
181   return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
182 }
183 
184 
185 /*
186 ** this function has a separated environment, which defines the
187 ** correct __close for 'popen' files
188 */
io_popen(lua_State * L)189 static int io_popen (lua_State *L) {
190   error("LUA I/O has been removed in ScummVM");
191 /*
192   const char *filename = luaL_checkstring(L, 1);
193   const char *mode = luaL_optstring(L, 2, "r");
194   FILE **pf = newfile(L);
195   *pf = lua_popen(L, filename, mode);
196   return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
197   */
198 }
199 
200 
io_tmpfile(lua_State * L)201 static int io_tmpfile (lua_State *L) {
202   return luaL_error(L, "%s", "LUA I/O error descriptions have been removed in ScummVM");
203 }
204 
205 /*
206 static FILE *getiofile (lua_State *L, int findex) {
207   FILE *f;
208   lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
209   f = *(FILE **)lua_touserdata(L, -1);
210   if (f == NULL)
211     luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
212   return f;
213 }
214 */
215 /*
216 static int g_iofile (lua_State *L, int f, const char *mode) {
217   if (!lua_isnoneornil(L, 1)) {
218     const char *filename = lua_tostring(L, 1);
219     if (filename) {
220       FILE **pf = newfile(L);
221       *pf = fopen(filename, mode);
222       if (*pf == NULL)
223         fileerror(L, 1, filename);
224     }
225     else {
226       tofile(L);  // check that it's a valid file handle
227       lua_pushvalue(L, 1);
228     }
229     lua_rawseti(L, LUA_ENVIRONINDEX, f);
230   }
231   // return current value
232   lua_rawgeti(L, LUA_ENVIRONINDEX, f);
233   return 1;
234 }
235 */
236 
io_input(lua_State * L)237 static int io_input (lua_State *L) {
238   error("LUA I/O has been removed in ScummVM");
239 //  return g_iofile(L, IO_INPUT, "r");
240 }
241 
242 
io_output(lua_State * L)243 static int io_output (lua_State *L) {
244   error("LUA I/O has been removed in ScummVM");
245 //  return g_iofile(L, IO_OUTPUT, "w");
246 }
247 
248 
249 static int io_readline (lua_State *L);
250 
251 
aux_lines(lua_State * L,int idx,int toclose)252 static void aux_lines (lua_State *L, int idx, int toclose) {
253   lua_pushvalue(L, idx);
254   lua_pushboolean(L, toclose);  /* close/not close file when finished */
255   lua_pushcclosure(L, io_readline, 2);
256 }
257 
258 
f_lines(lua_State * L)259 static int f_lines (lua_State *L) {
260   tofile(L);  /* check that it's a valid file handle */
261   aux_lines(L, 1, 0);
262   return 1;
263 }
264 
265 
io_lines(lua_State * L)266 static int io_lines (lua_State *L) {
267   error("LUA I/O has been removed in ScummVM");
268 /*
269   if (lua_isnoneornil(L, 1)) {  // no arguments?
270     // will iterate over default input
271     lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
272     return f_lines(L);
273   }
274   else {
275     const char *filename = luaL_checkstring(L, 1);
276     FILE **pf = newfile(L);
277     *pf = fopen(filename, "r");
278     if (*pf == NULL)
279       fileerror(L, 1, filename);
280     aux_lines(L, lua_gettop(L), 1);
281     return 1;
282   }
283   */
284 }
285 
286 
287 /*
288 ** {======================================================
289 ** READ
290 ** =======================================================
291 */
292 
293 /*
294 static int read_number (lua_State *L, Lua::LuaFileProxy *f) {
295   lua_Number d;
296   if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
297     lua_pushnumber(L, d);
298     return 1;
299   }
300   else return 0;  // read fails
301 }
302 
303 
304 static int test_eof (lua_State *L, Lua::LuaFileProxy *f) {
305   int c = getc(f);
306   ungetc(c, f);
307   lua_pushlstring(L, NULL, 0);
308   return (c != EOF);
309 }
310 
311 
312 static int read_line (lua_State *L, Lua::LuaFileProxy *f) {
313   luaL_Buffer b;
314   luaL_buffinit(L, &b);
315   for (;;) {
316     size_t l;
317     char *p = luaL_prepbuffer(&b);
318     if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) {  // eof?
319       luaL_pushresult(&b);  // close buffer
320       return (lua_objlen(L, -1) > 0);  // check whether read something
321     }
322     l = strlen(p);
323     if (l == 0 || p[l-1] != '\n')
324       luaL_addsize(&b, l);
325     else {
326       luaL_addsize(&b, l - 1);  // do not include `eol'
327       luaL_pushresult(&b);  // close buffer
328       return 1;  // read at least an `eol'
329     }
330   }
331 }
332 
333 
334 static int read_chars (lua_State *L, Lua::LuaFileProxy *f, size_t n) {
335   size_t rlen;  // how much to read
336   size_t nr;  // number of chars actually read
337   luaL_Buffer b;
338   luaL_buffinit(L, &b);
339   rlen = LUAL_BUFFERSIZE;  // try to read that much each time
340   do {
341     char *p = luaL_prepbuffer(&b);
342     if (rlen > n) rlen = n;  // cannot read more than asked
343     nr = fread(p, sizeof(char), rlen, f);
344     luaL_addsize(&b, nr);
345     n -= nr;  // still have to read `n' chars
346   } while (n > 0 && nr == rlen);  // until end of count or eof
347   luaL_pushresult(&b);  // close buffer
348   return (n == 0 || lua_objlen(L, -1) > 0);
349 }
350 
351 
352 static int g_read (lua_State *L, Lua::LuaFileProxy *f, int first) {
353   int nargs = lua_gettop(L) - 1;
354   int success;
355   int n;
356   clearerr(f);
357   if (nargs == 0) {  // no arguments?
358     success = read_line(L, f);
359     n = first+1;  // to return 1 result
360   }
361   else {  // ensure stack space for all results and for auxlib's buffer
362     luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
363     success = 1;
364     for (n = first; nargs-- && success; n++) {
365       if (lua_type(L, n) == LUA_TNUMBER) {
366         size_t l = (size_t)lua_tointeger(L, n);
367         success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
368       }
369       else {
370         const char *p = lua_tostring(L, n);
371         luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
372         switch (p[1]) {
373           case 'n':  // number
374             success = read_number(L, f);
375             break;
376           case 'l':  // line
377             success = read_line(L, f);
378             break;
379           case 'a':  // file
380             read_chars(L, f, ~((size_t)0));  // read MAX_SIZE_T chars
381             success = 1; // always success
382             break;
383           default:
384             return luaL_argerror(L, n, "invalid format");
385         }
386       }
387     }
388   }
389   if (ferror(f))
390     return pushresult(L, 0, NULL);
391   if (!success) {
392     lua_pop(L, 1);  // remove last result
393     lua_pushnil(L);  // push nil instead
394   }
395   return n - first;
396 }
397 */
398 
io_read(lua_State * L)399 static int io_read (lua_State *L) {
400   error("LUA I/O has been removed in ScummVM");
401 //  return g_read(L, getiofile(L, IO_INPUT), 1);
402 }
403 
404 
f_read(lua_State * L)405 static int f_read (lua_State *L) {
406   error("LUA I/O has been removed in ScummVM");
407 //  return g_read(L, tofile(L), 2);
408 }
409 
410 
io_readline(lua_State * L)411 static int io_readline (lua_State *L) {
412   error("LUA I/O has been removed in ScummVM");
413 /*
414   FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
415   int sucess;
416   if (f == NULL)  // file is already closed?
417     luaL_error(L, "file is already closed");
418   sucess = read_line(L, f);
419   if (ferror(f))
420     return luaL_error(L, "%s", "LUA I/O error descriptions have been removed in ScummVM");
421   if (sucess) return 1;
422   else {  // EOF
423     if (lua_toboolean(L, lua_upvalueindex(2))) {  // generator created file?
424       lua_settop(L, 0);
425       lua_pushvalue(L, lua_upvalueindex(1));
426       aux_close(L);  // close it
427     }
428     return 0;
429   }
430 */
431 }
432 
433 /* }====================================================== */
434 
435 
g_write(lua_State * L,Lua::LuaFileProxy * f,int arg)436 static int g_write (lua_State *L, Lua::LuaFileProxy *f, int arg) {
437   int nargs = lua_gettop(L) - 1;
438   int status = 1;
439   for (; nargs--; arg++) {
440     if (lua_type(L, arg) == LUA_TNUMBER) {
441       // optimization: could be done exactly as for strings
442 	  if (status) {
443         char buffer[22];
444 		sprintf(buffer, LUA_NUMBER_FMT, lua_tonumber(L, arg));
445 		status = f->write(buffer, strlen(buffer)) == strlen(buffer);
446 	  }
447 	}
448     else {
449       size_t l;
450       const char *s = luaL_checklstring(L, arg, &l);
451       status = status && (f->write(s, l) == l);
452     }
453   }
454   return pushresult(L, status, NULL);
455 }
456 
457 
io_write(lua_State * L)458 static int io_write (lua_State *L) {
459   error("LUA I/O has been removed in ScummVM");
460 //  return g_write(L, getiofile(L, IO_OUTPUT), 1);
461 }
462 
463 
f_write(lua_State * L)464 static int f_write (lua_State *L) {
465   return g_write(L, tofile(L), 2);
466 }
467 
468 
f_seek(lua_State * L)469 static int f_seek (lua_State *L) {
470   error("LUA I/O has been removed in ScummVM");
471   /*
472   static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
473   static const char *const modenames[] = {"set", "cur", "end", NULL};
474   FILE *f = tofile(L);
475   int op = luaL_checkoption(L, 2, "cur", modenames);
476   long offset = luaL_optlong(L, 3, 0);
477   op = fseek(f, offset, mode[op]);
478   if (op)
479     return pushresult(L, 0, NULL);  // error
480   else {
481     lua_pushinteger(L, ftell(f));
482     return 1;
483   }
484 */
485 }
486 
487 
f_setvbuf(lua_State * L)488 static int f_setvbuf (lua_State *L) {
489   error("LUA I/O has been removed in ScummVM");
490   /*
491   static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
492   static const char *const modenames[] = {"no", "full", "line", NULL};
493   FILE *f = tofile(L);
494   int op = luaL_checkoption(L, 2, NULL, modenames);
495   lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
496   int res = setvbuf(f, NULL, mode[op], sz);
497   return pushresult(L, res == 0, NULL);
498   */
499 }
500 
501 
502 
io_flush(lua_State * L)503 static int io_flush (lua_State *L) {
504   error("LUA I/O has been removed in ScummVM");
505 //  return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
506 }
507 
508 
f_flush(lua_State * L)509 static int f_flush (lua_State *L) {
510   error("LUA I/O has been removed in ScummVM");
511 //  return pushresult(L, fflush(tofile(L)) == 0, NULL);
512 }
513 
514 
515 static const luaL_Reg iolib[] = {
516   {"close", io_close},
517   {"flush", io_flush},
518   {"input", io_input},
519   {"lines", io_lines},
520   {"open", io_open},
521   {"output", io_output},
522   {"popen", io_popen},
523   {"read", io_read},
524   {"tmpfile", io_tmpfile},
525   {"type", io_type},
526   {"write", io_write},
527   {NULL, NULL}
528 };
529 
530 
531 static const luaL_Reg flib[] = {
532   {"close", io_close},
533   {"flush", f_flush},
534   {"lines", f_lines},
535   {"read", f_read},
536   {"seek", f_seek},
537   {"setvbuf", f_setvbuf},
538   {"write", f_write},
539   {"__gc", io_gc},
540   {"__tostring", io_tostring},
541   {NULL, NULL}
542 };
543 
544 
createmeta(lua_State * L)545 static void createmeta (lua_State *L) {
546   luaL_newmetatable(L, LUA_FILEHANDLE);  /* create metatable for file handles */
547   lua_pushvalue(L, -1);  /* push metatable */
548   lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
549   luaL_register(L, NULL, flib);  /* file methods */
550 }
551 
552 /*
553 static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
554   *newfile(L) = f;
555   if (k > 0) {
556     lua_pushvalue(L, -1);
557     lua_rawseti(L, LUA_ENVIRONINDEX, k);
558   }
559   lua_pushvalue(L, -2);  // copy environment
560   lua_setfenv(L, -2);  // set it
561   lua_setfield(L, -3, fname);
562 }
563 */
564 
newfenv(lua_State * L,lua_CFunction cls)565 static void newfenv (lua_State *L, lua_CFunction cls) {
566   lua_createtable(L, 0, 1);
567   lua_pushcfunction(L, cls);
568   lua_setfield(L, -2, "__close");
569 }
570 
571 
luaopen_io(lua_State * L)572 LUALIB_API int luaopen_io (lua_State *L) {
573   createmeta(L);
574   /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
575   newfenv(L, io_fclose);
576   lua_replace(L, LUA_ENVIRONINDEX);
577   /* open library */
578   luaL_register(L, LUA_IOLIBNAME, iolib);
579   /* create (and set) default files */
580   newfenv(L, io_noclose);  /* close function for default files */
581 /*
582   createstdfile(L, stdin, IO_INPUT, "stdin");
583   createstdfile(L, stdout, IO_OUTPUT, "stdout");
584   createstdfile(L, stderr, 0, "stderr");
585 */
586   lua_pop(L, 1);  /* pop environment for default files */
587   lua_getfield(L, -1, "popen");
588   newfenv(L, io_pclose);  /* create environment for 'popen' */
589   lua_setfenv(L, -2);  /* set fenv for 'popen' */
590   lua_pop(L, 1);  /* pop 'popen' */
591   return 1;
592 }
593