3 ** Copyright (C) 2005-2025 Mike Pall. See Copyright Notice in luajit.h
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
25 #include "lj_strfmt.h"
29 /* Userdata payload for I/O file. */
30 typedef struct IOFileUD
{
31 FILE *fp
; /* File handle. */
32 uint32_t type
; /* File type. */
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
40 #define IOFILE_FLAG_CLOSE 4 /* Close after io.lines() iterator. */
42 #define IOSTDF_UD(L, id) (&gcref(G(L)->gcroot[(id)])->ud)
43 #define IOSTDF_IOF(L, id) ((IOFileUD *)uddata(IOSTDF_UD(L, (id))))
45 /* -- Open/close helpers -------------------------------------------------- */
47 static IOFileUD
*io_tofilep(lua_State
*L
)
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
));
55 static IOFileUD
*io_tofile(lua_State
*L
)
57 IOFileUD
*iof
= io_tofilep(L
);
59 lj_err_caller(L
, LJ_ERR_IOCLFL
);
63 static IOFileUD
*io_stdfile(lua_State
*L
, ptrdiff_t id
)
65 IOFileUD
*iof
= IOSTDF_IOF(L
, id
);
67 lj_err_caller(L
, LJ_ERR_IOSTDCL
);
71 static IOFileUD
*io_file_new(lua_State
*L
)
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
);
79 iof
->type
= IOFILE_TYPE_FILE
;
83 static IOFileUD
*io_file_open(lua_State
*L
, const char *mode
)
85 const char *fname
= strdata(lj_lib_checkstr(L
, 1));
86 IOFileUD
*iof
= io_file_new(L
);
87 iof
->fp
= fopen(fname
, mode
);
89 luaL_argerror(L
, 1, lj_strfmt_pushf(L
, "%s: %s", fname
, strerror(errno
)));
93 static int io_file_close(lua_State
*L
, IOFileUD
*iof
)
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
) {
101 stat
= pclose(iof
->fp
);
102 #elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP
103 stat
= _pclose(iof
->fp
);
107 return luaL_execresult(L
, stat
);
112 lj_assertL((iof
->type
& IOFILE_TYPE_MASK
) == IOFILE_TYPE_STDF
,
113 "close of unknown FILE* type");
115 lua_pushliteral(L
, "cannot close standard file");
119 return luaL_fileresult(L
, ok
, NULL
);
122 /* -- Read/write helpers -------------------------------------------------- */
124 static int io_file_readnum(lua_State
*L
, FILE *fp
)
127 if (fscanf(fp
, LUA_NUMBER_SCAN
, &d
) == 1) {
129 int32_t i
= lj_num2int(d
);
130 if (d
== (lua_Number
)i
&& !tvismzero((cTValue
*)&d
)) {
131 setintV(L
->top
++, i
);
135 setnumV(L
->top
++, d
);
143 static int io_file_readline(lua_State
*L
, FILE *fp
, MSize chop
)
145 MSize m
= LUAL_BUFFERSIZE
, n
= 0, ok
= 0;
148 buf
= lj_buf_tmp(L
, m
);
149 if (fgets(buf
+n
, m
-n
, fp
) == NULL
) break;
150 n
+= (MSize
)strlen(buf
+n
);
152 if (n
&& buf
[n
-1] == '\n') { n
-= chop
; break; }
153 if (n
>= m
- 64) m
+= m
;
155 setstrV(L
, L
->top
++, lj_str_new(L
, buf
, (size_t)n
));
160 static void io_file_readall(lua_State
*L
, FILE *fp
)
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
);
167 setstrV(L
, L
->top
++, lj_str_new(L
, buf
, (size_t)n
));
174 static int io_file_readlen(lua_State
*L
, FILE *fp
, MSize 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
));
185 setstrV(L
, L
->top
++, &G(L
)->strempty
);
190 static int io_file_read(lua_State
*L
, IOFileUD
*iof
, int start
)
193 int ok
, n
, nargs
= (int)(L
->top
- L
->base
) - start
;
196 ok
= io_file_readline(L
, fp
, 1);
197 n
= start
+1; /* Return 1 result. */
199 /* The results plus the buffers go on top of the args. */
200 luaL_checkstack(L
, nargs
+LUA_MINSTACK
, "too many arguments");
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
++;
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
);
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));
217 lj_err_arg(L
, n
+1, LJ_ERR_INVOPT
);
222 return luaL_fileresult(L
, 0, NULL
);
224 setnilV(L
->top
-1); /* Replace last result with nil. */
228 static int io_file_write(lua_State
*L
, IOFileUD
*iof
, int start
)
233 for (tv
= L
->base
+start
; tv
< L
->top
; tv
++) {
235 const char *p
= lj_strfmt_wstrnum(L
, tv
, &len
);
237 lj_err_argt(L
, (int)(tv
- L
->base
) + 1, LUA_TSTRING
);
238 status
= status
&& (fwrite(p
, 1, len
, fp
) == len
);
240 if (LJ_52
&& status
) {
243 setudataV(L
, L
->base
, IOSTDF_UD(L
, GCROOT_IO_OUTPUT
));
246 return luaL_fileresult(L
, status
, NULL
);
249 static int io_file_iter(lua_State
*L
)
251 GCfunc
*fn
= curr_func(L
);
252 IOFileUD
*iof
= uddata(udataV(&fn
->c
.upvalue
[0]));
253 int n
= fn
->c
.nupvalues
- 1;
255 lj_err_caller(L
, LJ_ERR_IOCLFL
);
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
));
262 n
= io_file_read(L
, iof
, 0);
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. */
272 static int io_file_lines(lua_State
*L
)
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
);
281 /* -- I/O file methods ---------------------------------------------------- */
283 #define LJLIB_MODULE_io_method
285 LJLIB_CF(io_method_close
)
288 if (L
->base
< L
->top
) {
291 iof
= IOSTDF_IOF(L
, GCROOT_IO_OUTPUT
);
293 lj_err_caller(L
, LJ_ERR_IOCLFL
);
295 return io_file_close(L
, iof
);
298 LJLIB_CF(io_method_read
)
300 return io_file_read(L
, io_tofile(L
), 1);
303 LJLIB_CF(io_method_write
) LJLIB_REC(io_write
0)
305 return io_file_write(L
, io_tofile(L
), 1);
308 LJLIB_CF(io_method_flush
) LJLIB_REC(io_flush
0)
310 return luaL_fileresult(L
, fflush(io_tofile(L
)->fp
) == 0, NULL
);
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)))
321 LJLIB_CF(io_method_seek
)
323 FILE *fp
= io_tofile(L
)->fp
;
324 int opt
= lj_lib_checkopt(L
, 2, 1, "\3set\3cur\3end");
328 if (opt
== 0) opt
= SEEK_SET
;
329 else if (opt
== 1) opt
= SEEK_CUR
;
330 else if (opt
== 2) opt
= SEEK_END
;
334 ofs
= (int64_t)intV(o
);
336 ofs
= (int64_t)numV(o
);
337 else if (!tvisnil(o
))
338 lj_err_argt(L
, 3, LUA_TNUMBER
);
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
);
347 res
= fseek(fp
, (long)ofs
, opt
);
350 return luaL_fileresult(L
, 0, NULL
);
353 #elif _MSC_VER >= 1400
355 #elif defined(__MINGW32__)
358 ofs
= (int64_t)ftell(fp
);
360 setint64V(L
->top
-1, ofs
);
364 LJLIB_CF(io_method_setvbuf
)
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
);
375 LJLIB_CF(io_method_lines
)
378 return io_file_lines(L
);
381 LJLIB_CF(io_method___gc
)
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
);
389 LJLIB_CF(io_method___tostring
)
391 IOFileUD
*iof
= io_tofilep(L
);
393 lua_pushfstring(L
, "file (%p)", iof
->fp
);
395 lua_pushliteral(L
, "file (closed)");
399 LJLIB_PUSH(top
-1) LJLIB_SET(__index
)
401 #include "lj_libdef.h"
403 /* -- I/O library functions ----------------------------------------------- */
405 #define LJLIB_MODULE_io
407 LJLIB_PUSH(top
-2) LJLIB_SET(!) /* Set environment. */
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
);
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
;
429 iof
->fp
= popen(fname
, mode
);
431 iof
->fp
= _popen(fname
, mode
);
433 return iof
->fp
!= NULL
? 1 : luaL_fileresult(L
, 0, fname
);
435 return luaL_error(L
, LUA_QL("popen") " not supported");
441 IOFileUD
*iof
= io_file_new(L
);
442 #if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA || LJ_TARGET_NX
443 iof
->fp
= NULL
; errno
= ENOSYS
;
447 return iof
->fp
!= NULL
? 1 : luaL_fileresult(L
, 0, NULL
);
452 return lj_cf_io_method_close(L
);
457 return io_file_read(L
, io_stdfile(L
, GCROOT_IO_INPUT
), 0);
460 LJLIB_CF(io_write
) LJLIB_REC(io_write GCROOT_IO_OUTPUT
)
462 return io_file_write(L
, io_stdfile(L
, GCROOT_IO_OUTPUT
), 0);
465 LJLIB_CF(io_flush
) LJLIB_REC(io_flush GCROOT_IO_OUTPUT
)
467 return luaL_fileresult(L
, fflush(io_stdfile(L
, GCROOT_IO_OUTPUT
)->fp
) == 0, NULL
);
470 static int io_std_getset(lua_State
*L
, ptrdiff_t id
, const char *mode
)
472 if (L
->base
< L
->top
&& !tvisnil(L
->base
)) {
473 if (tvisudata(L
->base
)) {
477 io_file_open(L
, mode
);
479 /* NOBARRIER: The standard I/O handles are GC roots. */
480 setgcref(G(L
)->gcroot
[id
], gcV(L
->top
-1));
482 setudataV(L
, L
->top
++, IOSTDF_UD(L
, id
));
489 return io_std_getset(L
, GCROOT_IO_INPUT
, "r");
494 return io_std_getset(L
, GCROOT_IO_OUTPUT
, "w");
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
;
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
));
508 return io_file_lines(L
);
513 cTValue
*o
= lj_lib_checkany(L
, 1);
514 if (!(tvisudata(o
) && udataV(o
)->udtype
== UDTYPE_IO_FILE
))
516 else if (((IOFileUD
*)uddata(udataV(o
)))->fp
!= NULL
)
517 lua_pushliteral(L
, "file");
519 lua_pushliteral(L
, "closed file");
523 #include "lj_libdef.h"
525 /* ------------------------------------------------------------------------ */
527 static GCobj
*io_std_new(lua_State
*L
, FILE *fp
, const char *name
)
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));
535 iof
->type
= IOFILE_TYPE_STDF
;
536 lua_setfield(L
, -2, name
);
540 LUALIB_API
int luaopen_io(lua_State
*L
)
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");