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 = new Lua::LuaFileProxy(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