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. */
30 typedef 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 
io_tofilep(lua_State * L)47 static 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 
io_tofile(lua_State * L)55 static 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 
io_stdfile(lua_State * L,ptrdiff_t id)63 static IOFileUD *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;
69 }
70 
io_file_new(lua_State * L)71 static 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 
io_file_open(lua_State * L,const char * mode)83 static 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 
io_file_close(lua_State * L,IOFileUD * iof)93 static 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 
io_file_readnum(lua_State * L,FILE * fp)124 static 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 
io_file_readline(lua_State * L,FILE * fp,MSize chop)143 static 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 
io_file_readall(lua_State * L,FILE * fp)160 static 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 
io_file_readlen(lua_State * L,FILE * fp,MSize m)174 static 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;
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 
io_file_read(lua_State * L,IOFileUD * iof,int start)190 static int io_file_read(lua_State *L, IOFileUD *iof, int start)
191 {
192   FILE *fp = iof->fp;
193   int ok, n, nargs = (int)(L->top - L->base) - start;
194   clearerr(fp);
195   if (nargs == 0) {
196     ok = io_file_readline(L, fp, 1);
197     n = start+1;  /* Return 1 result. */
198   } else {
199     /* The results plus the buffers go on top of the args. */
200     luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
201     ok = 1;
202     for (n = start; nargs-- && ok; n++) {
203       if (tvisstr(L->base+n)) {
204 	const char *p = strVdata(L->base+n);
205 	if (p[0] == '*') p++;
206 	if (p[0] == 'n')
207 	  ok = io_file_readnum(L, fp);
208 	else if ((p[0] & ~0x20) == 'L')
209 	  ok = io_file_readline(L, fp, (p[0] == 'l'));
210 	else if (p[0] == 'a')
211 	  io_file_readall(L, fp);
212 	else
213 	  lj_err_arg(L, n+1, LJ_ERR_INVFMT);
214       } else if (tvisnumber(L->base+n)) {
215 	ok = io_file_readlen(L, fp, (MSize)lj_lib_checkint(L, n+1));
216       } else {
217 	lj_err_arg(L, n+1, LJ_ERR_INVOPT);
218       }
219     }
220   }
221   if (ferror(fp))
222     return luaL_fileresult(L, 0, NULL);
223   if (!ok)
224     setnilV(L->top-1);  /* Replace last result with nil. */
225   return n - start;
226 }
227 
io_file_write(lua_State * L,IOFileUD * iof,int start)228 static int io_file_write(lua_State *L, IOFileUD *iof, int start)
229 {
230   FILE *fp = iof->fp;
231   cTValue *tv;
232   int status = 1;
233   for (tv = L->base+start; tv < L->top; tv++) {
234     MSize len;
235     const char *p = lj_strfmt_wstrnum(L, tv, &len);
236     if (!p)
237       lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING);
238     status = status && (fwrite(p, 1, len, fp) == len);
239   }
240   if (LJ_52 && status) {
241     L->top = L->base+1;
242     if (start == 0)
243       setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_OUTPUT));
244     return 1;
245   }
246   return luaL_fileresult(L, status, NULL);
247 }
248 
io_file_iter(lua_State * L)249 static int io_file_iter(lua_State *L)
250 {
251   GCfunc *fn = curr_func(L);
252   IOFileUD *iof = uddata(udataV(&fn->c.upvalue[0]));
253   int n = fn->c.nupvalues - 1;
254   if (iof->fp == NULL)
255     lj_err_caller(L, LJ_ERR_IOCLFL);
256   L->top = L->base;
257   if (n) {  /* Copy upvalues with options to stack. */
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, 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 
io_file_lines(lua_State * L)272 static 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 
LJLIB_CF(io_method_close)285 LJLIB_CF(io_method_close)
286 {
287   IOFileUD *iof;
288   if (L->base < L->top) {
289     iof = io_tofile(L);
290   } else {
291     iof = IOSTDF_IOF(L, GCROOT_IO_OUTPUT);
292     if (iof->fp == NULL)
293       lj_err_caller(L, LJ_ERR_IOCLFL);
294   }
295   return io_file_close(L, iof);
296 }
297 
LJLIB_CF(io_method_read)298 LJLIB_CF(io_method_read)
299 {
300   return io_file_read(L, io_tofile(L), 1);
301 }
302 
303 LJLIB_CF(io_method_write)		LJLIB_REC(io_write 0)
304 {
305   return io_file_write(L, io_tofile(L), 1);
306 }
307 
308 LJLIB_CF(io_method_flush)		LJLIB_REC(io_flush 0)
309 {
310   return luaL_fileresult(L, fflush(io_tofile(L)->fp) == 0, NULL);
311 }
312 
313 #if LJ_32 && defined(__ANDROID__) && __ANDROID_API__ < 24
314 /* The Android NDK is such an unmatched marvel of engineering. */
315 extern int fseeko32(FILE *, long int, int) __asm__("fseeko");
316 extern long int ftello32(FILE *) __asm__("ftello");
317 #define fseeko(fp, pos, whence)	(fseeko32((fp), (pos), (whence)))
318 #define ftello(fp)		(ftello32((fp)))
319 #endif
320 
LJLIB_CF(io_method_seek)321 LJLIB_CF(io_method_seek)
322 {
323   FILE *fp = io_tofile(L)->fp;
324   int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end");
325   int64_t ofs = 0;
326   cTValue *o;
327   int res;
328   if (opt == 0) opt = SEEK_SET;
329   else if (opt == 1) opt = SEEK_CUR;
330   else if (opt == 2) opt = SEEK_END;
331   o = L->base+2;
332   if (o < L->top) {
333     if (tvisint(o))
334       ofs = (int64_t)intV(o);
335     else if (tvisnum(o))
336       ofs = (int64_t)numV(o);
337     else if (!tvisnil(o))
338       lj_err_argt(L, 3, LUA_TNUMBER);
339   }
340 #if LJ_TARGET_POSIX
341   res = fseeko(fp, ofs, opt);
342 #elif _MSC_VER >= 1400
343   res = _fseeki64(fp, ofs, opt);
344 #elif defined(__MINGW32__)
345   res = fseeko64(fp, ofs, opt);
346 #else
347   res = fseek(fp, (long)ofs, opt);
348 #endif
349   if (res)
350     return luaL_fileresult(L, 0, NULL);
351 #if LJ_TARGET_POSIX
352   ofs = ftello(fp);
353 #elif _MSC_VER >= 1400
354   ofs = _ftelli64(fp);
355 #elif defined(__MINGW32__)
356   ofs = ftello64(fp);
357 #else
358   ofs = (int64_t)ftell(fp);
359 #endif
360   setint64V(L->top-1, ofs);
361   return 1;
362 }
363 
LJLIB_CF(io_method_setvbuf)364 LJLIB_CF(io_method_setvbuf)
365 {
366   FILE *fp = io_tofile(L)->fp;
367   int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no");
368   size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE);
369   if (opt == 0) opt = _IOFBF;
370   else if (opt == 1) opt = _IOLBF;
371   else if (opt == 2) opt = _IONBF;
372   return luaL_fileresult(L, setvbuf(fp, NULL, opt, sz) == 0, NULL);
373 }
374 
LJLIB_CF(io_method_lines)375 LJLIB_CF(io_method_lines)
376 {
377   io_tofile(L);
378   return io_file_lines(L);
379 }
380 
LJLIB_CF(io_method___gc)381 LJLIB_CF(io_method___gc)
382 {
383   IOFileUD *iof = io_tofilep(L);
384   if (iof->fp != NULL && (iof->type & IOFILE_TYPE_MASK) != IOFILE_TYPE_STDF)
385     io_file_close(L, iof);
386   return 0;
387 }
388 
LJLIB_CF(io_method___tostring)389 LJLIB_CF(io_method___tostring)
390 {
391   IOFileUD *iof = io_tofilep(L);
392   if (iof->fp != NULL)
393     lua_pushfstring(L, "file (%p)", iof->fp);
394   else
395     lua_pushliteral(L, "file (closed)");
396   return 1;
397 }
398 
LJLIB_SET(__index)399 LJLIB_PUSH(top-1) LJLIB_SET(__index)
400 
401 #include "lj_libdef.h"
402 
403 /* -- I/O library functions ----------------------------------------------- */
404 
405 #define LJLIB_MODULE_io
406 
407 LJLIB_PUSH(top-2) LJLIB_SET(!)  /* Set environment. */
408 
409 LJLIB_CF(io_open)
410 {
411   const char *fname = strdata(lj_lib_checkstr(L, 1));
412   GCstr *s = lj_lib_optstr(L, 2);
413   const char *mode = s ? strdata(s) : "r";
414   IOFileUD *iof = io_file_new(L);
415   iof->fp = fopen(fname, mode);
416   return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
417 }
418 
LJLIB_CF(io_popen)419 LJLIB_CF(io_popen)
420 {
421 #if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP)
422   const char *fname = strdata(lj_lib_checkstr(L, 1));
423   GCstr *s = lj_lib_optstr(L, 2);
424   const char *mode = s ? strdata(s) : "r";
425   IOFileUD *iof = io_file_new(L);
426   iof->type = IOFILE_TYPE_PIPE;
427 #if LJ_TARGET_POSIX
428   fflush(NULL);
429   iof->fp = popen(fname, mode);
430 #else
431   iof->fp = _popen(fname, mode);
432 #endif
433   return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
434 #else
435   return luaL_error(L, LUA_QL("popen") " not supported");
436 #endif
437 }
438 
LJLIB_CF(io_tmpfile)439 LJLIB_CF(io_tmpfile)
440 {
441   IOFileUD *iof = io_file_new(L);
442 #if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PSVITA
443   iof->fp = NULL; errno = ENOSYS;
444 #else
445   iof->fp = tmpfile();
446 #endif
447   return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, NULL);
448 }
449 
LJLIB_CF(io_close)450 LJLIB_CF(io_close)
451 {
452   return lj_cf_io_method_close(L);
453 }
454 
LJLIB_CF(io_read)455 LJLIB_CF(io_read)
456 {
457   return io_file_read(L, io_stdfile(L, GCROOT_IO_INPUT), 0);
458 }
459 
LJLIB_REC(io_write GCROOT_IO_OUTPUT)460 LJLIB_CF(io_write)		LJLIB_REC(io_write GCROOT_IO_OUTPUT)
461 {
462   return io_file_write(L, io_stdfile(L, GCROOT_IO_OUTPUT), 0);
463 }
464 
LJLIB_REC(io_flush GCROOT_IO_OUTPUT)465 LJLIB_CF(io_flush)		LJLIB_REC(io_flush GCROOT_IO_OUTPUT)
466 {
467   return luaL_fileresult(L, fflush(io_stdfile(L, GCROOT_IO_OUTPUT)->fp) == 0, NULL);
468 }
469 
io_std_getset(lua_State * L,ptrdiff_t id,const char * mode)470 static int io_std_getset(lua_State *L, ptrdiff_t id, const char *mode)
471 {
472   if (L->base < L->top && !tvisnil(L->base)) {
473     if (tvisudata(L->base)) {
474       io_tofile(L);
475       L->top = L->base+1;
476     } else {
477       io_file_open(L, mode);
478     }
479     /* NOBARRIER: The standard I/O handles are GC roots. */
480     setgcref(G(L)->gcroot[id], gcV(L->top-1));
481   } else {
482     setudataV(L, L->top++, IOSTDF_UD(L, id));
483   }
484   return 1;
485 }
486 
LJLIB_CF(io_input)487 LJLIB_CF(io_input)
488 {
489   return io_std_getset(L, GCROOT_IO_INPUT, "r");
490 }
491 
LJLIB_CF(io_output)492 LJLIB_CF(io_output)
493 {
494   return io_std_getset(L, GCROOT_IO_OUTPUT, "w");
495 }
496 
LJLIB_CF(io_lines)497 LJLIB_CF(io_lines)
498 {
499   if (L->base == L->top) setnilV(L->top++);
500   if (!tvisnil(L->base)) {  /* io.lines(fname) */
501     IOFileUD *iof = io_file_open(L, "r");
502     iof->type = IOFILE_TYPE_FILE|IOFILE_FLAG_CLOSE;
503     L->top--;
504     setudataV(L, L->base, udataV(L->top));
505   } else {  /* io.lines() iterates over stdin. */
506     setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_INPUT));
507   }
508   return io_file_lines(L);
509 }
510 
LJLIB_CF(io_type)511 LJLIB_CF(io_type)
512 {
513   cTValue *o = lj_lib_checkany(L, 1);
514   if (!(tvisudata(o) && udataV(o)->udtype == UDTYPE_IO_FILE))
515     setnilV(L->top++);
516   else if (((IOFileUD *)uddata(udataV(o)))->fp != NULL)
517     lua_pushliteral(L, "file");
518   else
519     lua_pushliteral(L, "closed file");
520   return 1;
521 }
522 
523 #include "lj_libdef.h"
524 
525 /* ------------------------------------------------------------------------ */
526 
io_std_new(lua_State * L,FILE * fp,const char * name)527 static GCobj *io_std_new(lua_State *L, FILE *fp, const char *name)
528 {
529   IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
530   GCudata *ud = udataV(L->top-1);
531   ud->udtype = UDTYPE_IO_FILE;
532   /* NOBARRIER: The GCudata is new (marked white). */
533   setgcref(ud->metatable, gcV(L->top-3));
534   iof->fp = fp;
535   iof->type = IOFILE_TYPE_STDF;
536   lua_setfield(L, -2, name);
537   return obj2gco(ud);
538 }
539 
luaopen_io(lua_State * L)540 LUALIB_API int luaopen_io(lua_State *L)
541 {
542   LJ_LIB_REG(L, NULL, io_method);
543   copyTV(L, L->top, L->top-1); L->top++;
544   lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
545   LJ_LIB_REG(L, LUA_IOLIBNAME, io);
546   setgcref(G(L)->gcroot[GCROOT_IO_INPUT], io_std_new(L, stdin, "stdin"));
547   setgcref(G(L)->gcroot[GCROOT_IO_OUTPUT], io_std_new(L, stdout, "stdout"));
548   io_std_new(L, stderr, "stderr");
549   return 1;
550 }
551 
552