1/*
2** I/O library.
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-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h
7*/
8
9#include <errno.h>
10#include <stdio.h>
11
12#define lib_io_c
13#define LUA_LIB
14
15#include "lua.h"
16#include "lauxlib.h"
17#include "lualib.h"
18
19#include "lj_obj.h"
20#include "lj_gc.h"
21#include "lj_err.h"
22#include "lj_buf.h"
23#include "lj_str.h"
24#include "lj_state.h"
25#include "lj_strfmt.h"
26#include "lj_ff.h"
27#include "lj_lib.h"
28
29/* Userdata payload for I/O file. */
30typedef struct IOFileUD {
31 FILE *fp; /* File handle. */
32 uint32_t type; /* File type. */
33} IOFileUD;
34
35#define IOFILE_TYPE_FILE 0 /* Regular file. */
36#define IOFILE_TYPE_PIPE 1 /* Pipe. */
37#define IOFILE_TYPE_STDF 2 /* Standard file handle. */
38#define IOFILE_TYPE_MASK 3
39
40#define IOFILE_FLAG_CLOSE 4 /* Close after io.lines() iterator. */
41
42#define IOSTDF_UD(L, id) (&gcref(G(L)->gcroot[(id)])->ud)
43#define IOSTDF_IOF(L, id) ((IOFileUD *)uddata(IOSTDF_UD(L, (id))))
44
45/* -- Open/close helpers -------------------------------------------------- */
46
47static IOFileUD *io_tofilep(lua_State *L)
48{
49 if (!(L->base < L->top && tvisudata(L->base) &&
50 udataV(L->base)->udtype == UDTYPE_IO_FILE))
51 lj_err_argtype(L, 1, "FILE*");
52 return (IOFileUD *)uddata(udataV(L->base));
53}
54
55static IOFileUD *io_tofile(lua_State *L)
56{
57 IOFileUD *iof = io_tofilep(L);
58 if (iof->fp == NULL)
59 lj_err_caller(L, LJ_ERR_IOCLFL);
60 return iof;
61}
62
63static FILE *io_stdfile(lua_State *L, ptrdiff_t id)
64{
65 IOFileUD *iof = IOSTDF_IOF(L, id);
66 if (iof->fp == NULL)
67 lj_err_caller(L, LJ_ERR_IOSTDCL);
68 return iof->fp;
69}
70
71static IOFileUD *io_file_new(lua_State *L)
72{
73 IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
74 GCudata *ud = udataV(L->top-1);
75 ud->udtype = UDTYPE_IO_FILE;
76 /* NOBARRIER: The GCudata is new (marked white). */
77 setgcrefr(ud->metatable, curr_func(L)->c.env);
78 iof->fp = NULL;
79 iof->type = IOFILE_TYPE_FILE;
80 return iof;
81}
82
83static IOFileUD *io_file_open(lua_State *L, const char *mode)
84{
85 const char *fname = strdata(lj_lib_checkstr(L, 1));
86 IOFileUD *iof = io_file_new(L);
87 iof->fp = fopen(fname, mode);
88 if (iof->fp == NULL)
89 luaL_argerror(L, 1, lj_strfmt_pushf(L, "%s: %s", fname, strerror(errno)));
90 return iof;
91}
92
93static int io_file_close(lua_State *L, IOFileUD *iof)
94{
95 int ok;
96 if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) {
97 ok = (fclose(iof->fp) == 0);
98 } else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) {
99 int stat = -1;
100#if LJ_TARGET_POSIX
101 stat = pclose(iof->fp);
102#elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP
103 stat = _pclose(iof->fp);
104#endif
105#if LJ_52
106 iof->fp = NULL;
107 return luaL_execresult(L, stat);
108#else
109 ok = (stat != -1);
110#endif
111 } else {
112 lj_assertL((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF,
113 "close of unknown FILE* type");
114 setnilV(L->top++);
115 lua_pushliteral(L, "cannot close standard file");
116 return 2;
117 }
118 iof->fp = NULL;
119 return luaL_fileresult(L, ok, NULL);
120}
121
122/* -- Read/write helpers -------------------------------------------------- */
123
124static int io_file_readnum(lua_State *L, FILE *fp)
125{
126 lua_Number d;
127 if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) {
128 if (LJ_DUALNUM) {
129 int32_t i = lj_num2int(d);
130 if (d == (lua_Number)i && !tvismzero((cTValue *)&d)) {
131 setintV(L->top++, i);
132 return 1;
133 }
134 }
135 setnumV(L->top++, d);
136 return 1;
137 } else {
138 setnilV(L->top++);
139 return 0;
140 }
141}
142
143static int io_file_readline(lua_State *L, FILE *fp, MSize chop)
144{
145 MSize m = LUAL_BUFFERSIZE, n = 0, ok = 0;
146 char *buf;
147 for (;;) {
148 buf = lj_buf_tmp(L, m);
149 if (fgets(buf+n, m-n, fp) == NULL) break;
150 n += (MSize)strlen(buf+n);
151 ok |= n;
152 if (n && buf[n-1] == '\n') { n -= chop; break; }
153 if (n >= m - 64) m += m;
154 }
155 setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
156 lj_gc_check(L);
157 return (int)ok;
158}
159
160static void io_file_readall(lua_State *L, FILE *fp)
161{
162 MSize m, n;
163 for (m = LUAL_BUFFERSIZE, n = 0; ; m += m) {
164 char *buf = lj_buf_tmp(L, m);
165 n += (MSize)fread(buf+n, 1, m-n, fp);
166 if (n != m) {
167 setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
168 lj_gc_check(L);
169 return;
170 }
171 }
172}
173
174static int io_file_readlen(lua_State *L, FILE *fp, MSize m)
175{
176 if (m) {
177 char *buf = lj_buf_tmp(L, m);
178 MSize n = (MSize)fread(buf, 1, m, fp);
179 setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
180 lj_gc_check(L);
181 return (n > 0 || m == 0);
182 } else {
183 int c = getc(fp);
184 ungetc(c, fp);
185 setstrV(L, L->top++, &G(L)->strempty);
186 return (c != EOF);
187 }
188}
189
190static int io_file_read(lua_State *L, FILE *fp, int start)
191{
192 int ok, n, nargs = (int)(L->top - L->base) - start;
193 clearerr(fp);
194 if (nargs == 0) {
195 ok = io_file_readline(L, fp, 1);
196 n = start+1; /* Return 1 result. */
197 } else {
198 /* The results plus the buffers go on top of the args. */
199 luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
200 ok = 1;
201 for (n = start; nargs-- && ok; n++) {
202 if (tvisstr(L->base+n)) {
203 const char *p = strVdata(L->base+n);
204 if (p[0] == '*') p++;
205 if (p[0] == 'n')
206 ok = io_file_readnum(L, fp);
207 else if ((p[0] & ~0x20) == 'L')
208 ok = io_file_readline(L, fp, (p[0] == 'l'));
209 else if (p[0] == 'a')
210 io_file_readall(L, fp);
211 else
212 lj_err_arg(L, n+1, LJ_ERR_INVFMT);
213 } else if (tvisnumber(L->base+n)) {
214 ok = io_file_readlen(L, fp, (MSize)lj_lib_checkint(L, n+1));
215 } else {
216 lj_err_arg(L, n+1, LJ_ERR_INVOPT);
217 }
218 }
219 }
220 if (ferror(fp))
221 return luaL_fileresult(L, 0, NULL);
222 if (!ok)
223 setnilV(L->top-1); /* Replace last result with nil. */
224 return n - start;
225}
226
227static int io_file_write(lua_State *L, FILE *fp, int start)
228{
229 cTValue *tv;
230 int status = 1;
231 for (tv = L->base+start; tv < L->top; tv++) {
232 MSize len;
233 const char *p = lj_strfmt_wstrnum(L, tv, &len);
234 if (!p)
235 lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING);
236 status = status && (fwrite(p, 1, len, fp) == len);
237 }
238 if (LJ_52 && status) {
239 L->top = L->base+1;
240 if (start == 0)
241 setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_OUTPUT));
242 return 1;
243 }
244 return luaL_fileresult(L, status, NULL);
245}
246
247static int io_file_iter(lua_State *L)
248{
249 GCfunc *fn = curr_func(L);
250 IOFileUD *iof = uddata(udataV(&fn->c.upvalue[0]));
251 int n = fn->c.nupvalues - 1;
252 if (iof->fp == NULL)
253 lj_err_caller(L, LJ_ERR_IOCLFL);
254 L->top = L->base;
255 if (n) { /* Copy upvalues with options to stack. */
256 if (n > LUAI_MAXCSTACK)
257 lj_err_caller(L, LJ_ERR_STKOV);
258 lj_state_checkstack(L, (MSize)n);
259 memcpy(L->top, &fn->c.upvalue[1], n*sizeof(TValue));
260 L->top += n;
261 }
262 n = io_file_read(L, iof->fp, 0);
263 if (ferror(iof->fp))
264 lj_err_callermsg(L, strVdata(L->top-2));
265 if (tvisnil(L->base) && (iof->type & IOFILE_FLAG_CLOSE)) {
266 io_file_close(L, iof); /* Return values are ignored. */
267 return 0;
268 }
269 return n;
270}
271
272static int io_file_lines(lua_State *L)
273{
274 int n = (int)(L->top - L->base);
275 if (n > LJ_MAX_UPVAL)
276 lj_err_caller(L, LJ_ERR_UNPACK);
277 lua_pushcclosure(L, io_file_iter, n);
278 return 1;
279}
280
281/* -- I/O file methods ---------------------------------------------------- */
282
283#define LJLIB_MODULE_io_method
284
285LJLIB_CF(io_method_close)
286{
287 IOFileUD *iof = L->base < L->top ? io_tofile(L) :
288 IOSTDF_IOF(L, GCROOT_IO_OUTPUT);
289 return io_file_close(L, iof);
290}
291
292LJLIB_CF(io_method_read)
293{
294 return io_file_read(L, io_tofile(L)->fp, 1);
295}
296
297LJLIB_CF(io_method_write) LJLIB_REC(io_write 0)
298{
299 return io_file_write(L, io_tofile(L)->fp, 1);
300}
301
302LJLIB_CF(io_method_flush) LJLIB_REC(io_flush 0)
303{
304 return luaL_fileresult(L, fflush(io_tofile(L)->fp) == 0, NULL);
305}
306
307#if LJ_32 && defined(__ANDROID__) && __ANDROID_API__ < 24
308/* The Android NDK is such an unmatched marvel of engineering. */
309extern int fseeko32(FILE *, long int, int) __asm__("fseeko");
310extern long int ftello32(FILE *) __asm__("ftello");
311#define fseeko(fp, pos, whence) (fseeko32((fp), (pos), (whence)))
312#define ftello(fp) (ftello32((fp)))
313#endif
314
315LJLIB_CF(io_method_seek)
316{
317 FILE *fp = io_tofile(L)->fp;
318 int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end");
319 int64_t ofs = 0;
320 cTValue *o;
321 int res;
322 if (opt == 0) opt = SEEK_SET;
323 else if (opt == 1) opt = SEEK_CUR;
324 else if (opt == 2) opt = SEEK_END;
325 o = L->base+2;
326 if (o < L->top) {
327 if (tvisint(o))
328 ofs = (int64_t)intV(o);
329 else if (tvisnum(o))
330 ofs = (int64_t)numV(o);
331 else if (!tvisnil(o))
332 lj_err_argt(L, 3, LUA_TNUMBER);
333 }
334#if LJ_TARGET_POSIX
335 res = fseeko(fp, ofs, opt);
336#elif _MSC_VER >= 1400
337 res = _fseeki64(fp, ofs, opt);
338#elif defined(__MINGW32__)
339 res = fseeko64(fp, ofs, opt);
340#else
341 res = fseek(fp, (long)ofs, opt);
342#endif
343 if (res)
344 return luaL_fileresult(L, 0, NULL);
345#if LJ_TARGET_POSIX
346 ofs = ftello(fp);
347#elif _MSC_VER >= 1400
348 ofs = _ftelli64(fp);
349#elif defined(__MINGW32__)
350 ofs = ftello64(fp);
351#else
352 ofs = (int64_t)ftell(fp);
353#endif
354 setint64V(L->top-1, ofs);
355 return 1;
356}
357
358LJLIB_CF(io_method_setvbuf)
359{
360 FILE *fp = io_tofile(L)->fp;
361 int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no");
362 size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE);
363 if (opt == 0) opt = _IOFBF;
364 else if (opt == 1) opt = _IOLBF;
365 else if (opt == 2) opt = _IONBF;
366 return luaL_fileresult(L, setvbuf(fp, NULL, opt, sz) == 0, NULL);
367}
368
369LJLIB_CF(io_method_lines)
370{
371 io_tofile(L);
372 return io_file_lines(L);
373}
374
375LJLIB_CF(io_method___gc)
376{
377 IOFileUD *iof = io_tofilep(L);
378 if (iof->fp != NULL && (iof->type & IOFILE_TYPE_MASK) != IOFILE_TYPE_STDF)
379 io_file_close(L, iof);
380 return 0;
381}
382
383LJLIB_CF(io_method___tostring)
384{
385 IOFileUD *iof = io_tofilep(L);
386 if (iof->fp != NULL)
387 lua_pushfstring(L, "file (%p)", iof->fp);
388 else
389 lua_pushliteral(L, "file (closed)");
390 return 1;
391}
392
393LJLIB_PUSH(top-1) LJLIB_SET(__index)
394
395#include "lj_libdef.h"
396
397/* -- I/O library functions ----------------------------------------------- */
398
399#define LJLIB_MODULE_io
400
401LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */
402
403LJLIB_CF(io_open)
404{
405 const char *fname = strdata(lj_lib_checkstr(L, 1));
406 GCstr *s = lj_lib_optstr(L, 2);
407 const char *mode = s ? strdata(s) : "r";
408 IOFileUD *iof = io_file_new(L);
409 iof->fp = fopen(fname, mode);
410 return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
411}
412
413LJLIB_CF(io_popen)
414{
415#if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP)
416 const char *fname = strdata(lj_lib_checkstr(L, 1));
417 GCstr *s = lj_lib_optstr(L, 2);
418 const char *mode = s ? strdata(s) : "r";
419 IOFileUD *iof = io_file_new(L);
420 iof->type = IOFILE_TYPE_PIPE;
421#if LJ_TARGET_POSIX
422 fflush(NULL);
423 iof->fp = popen(fname, mode);
424#else
425 iof->fp = _popen(fname, mode);
426#endif
427 return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
428#else
429 return luaL_error(L, LUA_QL("popen") " not supported");
430#endif
431}
432
433LJLIB_CF(io_tmpfile)
434{
435 IOFileUD *iof = io_file_new(L);
436#if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PSVITA
437 iof->fp = NULL; errno = ENOSYS;
438#else
439 iof->fp = tmpfile();
440#endif
441 return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, NULL);
442}
443
444LJLIB_CF(io_close)
445{
446 return lj_cf_io_method_close(L);
447}
448
449LJLIB_CF(io_read)
450{
451 return io_file_read(L, io_stdfile(L, GCROOT_IO_INPUT), 0);
452}
453
454LJLIB_CF(io_write) LJLIB_REC(io_write GCROOT_IO_OUTPUT)
455{
456 return io_file_write(L, io_stdfile(L, GCROOT_IO_OUTPUT), 0);
457}
458
459LJLIB_CF(io_flush) LJLIB_REC(io_flush GCROOT_IO_OUTPUT)
460{
461 return luaL_fileresult(L, fflush(io_stdfile(L, GCROOT_IO_OUTPUT)) == 0, NULL);
462}
463
464static int io_std_getset(lua_State *L, ptrdiff_t id, const char *mode)
465{
466 if (L->base < L->top && !tvisnil(L->base)) {
467 if (tvisudata(L->base)) {
468 io_tofile(L);
469 L->top = L->base+1;
470 } else {
471 io_file_open(L, mode);
472 }
473 /* NOBARRIER: The standard I/O handles are GC roots. */
474 setgcref(G(L)->gcroot[id], gcV(L->top-1));
475 } else {
476 setudataV(L, L->top++, IOSTDF_UD(L, id));
477 }
478 return 1;
479}
480
481LJLIB_CF(io_input)
482{
483 return io_std_getset(L, GCROOT_IO_INPUT, "r");
484}
485
486LJLIB_CF(io_output)
487{
488 return io_std_getset(L, GCROOT_IO_OUTPUT, "w");
489}
490
491LJLIB_CF(io_lines)
492{
493 if (L->base == L->top) setnilV(L->top++);
494 if (!tvisnil(L->base)) { /* io.lines(fname) */
495 IOFileUD *iof = io_file_open(L, "r");
496 iof->type = IOFILE_TYPE_FILE|IOFILE_FLAG_CLOSE;
497 L->top--;
498 setudataV(L, L->base, udataV(L->top));
499 } else { /* io.lines() iterates over stdin. */
500 setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_INPUT));
501 }
502 return io_file_lines(L);
503}
504
505LJLIB_CF(io_type)
506{
507 cTValue *o = lj_lib_checkany(L, 1);
508 if (!(tvisudata(o) && udataV(o)->udtype == UDTYPE_IO_FILE))
509 setnilV(L->top++);
510 else if (((IOFileUD *)uddata(udataV(o)))->fp != NULL)
511 lua_pushliteral(L, "file");
512 else
513 lua_pushliteral(L, "closed file");
514 return 1;
515}
516
517#include "lj_libdef.h"
518
519/* ------------------------------------------------------------------------ */
520
521static GCobj *io_std_new(lua_State *L, FILE *fp, const char *name)
522{
523 IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
524 GCudata *ud = udataV(L->top-1);
525 ud->udtype = UDTYPE_IO_FILE;
526 /* NOBARRIER: The GCudata is new (marked white). */
527 setgcref(ud->metatable, gcV(L->top-3));
528 iof->fp = fp;
529 iof->type = IOFILE_TYPE_STDF;
530 lua_setfield(L, -2, name);
531 return obj2gco(ud);
532}
533
534LUALIB_API int luaopen_io(lua_State *L)
535{
536 LJ_LIB_REG(L, NULL, io_method);
537 copyTV(L, L->top, L->top-1); L->top++;
538 lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
539 LJ_LIB_REG(L, LUA_IOLIBNAME, io);
540 setgcref(G(L)->gcroot[GCROOT_IO_INPUT], io_std_new(L, stdin, "stdin"));
541 setgcref(G(L)->gcroot[GCROOT_IO_OUTPUT], io_std_new(L, stdout, "stdout"));
542 io_std_new(L, stderr, "stderr");
543 return 1;
544}
545
546