| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- /*
- ** I/O library.
- ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
- **
- ** Major portions taken verbatim or adapted from the Lua interpreter.
- ** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h
- */
- #include <errno.h>
- #include <stdio.h>
- #define lib_io_c
- #define LUA_LIB
- #include "lua.h"
- #include "lauxlib.h"
- #include "lualib.h"
- #include "lj_obj.h"
- #include "lj_gc.h"
- #include "lj_err.h"
- #include "lj_buf.h"
- #include "lj_str.h"
- #include "lj_state.h"
- #include "lj_strfmt.h"
- #include "lj_ff.h"
- #include "lj_lib.h"
- /* Userdata payload for I/O file. */
- typedef struct IOFileUD {
- FILE *fp; /* File handle. */
- uint32_t type; /* File type. */
- } IOFileUD;
- #define IOFILE_TYPE_FILE 0 /* Regular file. */
- #define IOFILE_TYPE_PIPE 1 /* Pipe. */
- #define IOFILE_TYPE_STDF 2 /* Standard file handle. */
- #define IOFILE_TYPE_MASK 3
- #define IOFILE_FLAG_CLOSE 4 /* Close after io.lines() iterator. */
- #define IOSTDF_UD(L, id) (&gcref(G(L)->gcroot[(id)])->ud)
- #define IOSTDF_IOF(L, id) ((IOFileUD *)uddata(IOSTDF_UD(L, (id))))
- /* -- Open/close helpers -------------------------------------------------- */
- static IOFileUD *io_tofilep(lua_State *L)
- {
- if (!(L->base < L->top && tvisudata(L->base) &&
- udataV(L->base)->udtype == UDTYPE_IO_FILE))
- lj_err_argtype(L, 1, "FILE*");
- return (IOFileUD *)uddata(udataV(L->base));
- }
- static IOFileUD *io_tofile(lua_State *L)
- {
- IOFileUD *iof = io_tofilep(L);
- if (iof->fp == NULL)
- lj_err_caller(L, LJ_ERR_IOCLFL);
- return iof;
- }
- static IOFileUD *io_stdfile(lua_State *L, ptrdiff_t id)
- {
- IOFileUD *iof = IOSTDF_IOF(L, id);
- if (iof->fp == NULL)
- lj_err_caller(L, LJ_ERR_IOSTDCL);
- return iof;
- }
- static IOFileUD *io_file_new(lua_State *L)
- {
- IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
- GCudata *ud = udataV(L->top-1);
- ud->udtype = UDTYPE_IO_FILE;
- /* NOBARRIER: The GCudata is new (marked white). */
- setgcrefr(ud->metatable, curr_func(L)->c.env);
- iof->fp = NULL;
- iof->type = IOFILE_TYPE_FILE;
- return iof;
- }
- static IOFileUD *io_file_open(lua_State *L, const char *mode)
- {
- const char *fname = strdata(lj_lib_checkstr(L, 1));
- IOFileUD *iof = io_file_new(L);
- iof->fp = fopen(fname, mode);
- if (iof->fp == NULL)
- luaL_argerror(L, 1, lj_strfmt_pushf(L, "%s: %s", fname, strerror(errno)));
- return iof;
- }
- static int io_file_close(lua_State *L, IOFileUD *iof)
- {
- int ok;
- if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) {
- ok = (fclose(iof->fp) == 0);
- } else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) {
- int stat = -1;
- #if LJ_TARGET_POSIX
- stat = pclose(iof->fp);
- #elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP
- stat = _pclose(iof->fp);
- #endif
- #if LJ_52
- iof->fp = NULL;
- return luaL_execresult(L, stat);
- #else
- ok = (stat != -1);
- #endif
- } else {
- lj_assertL((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF,
- "close of unknown FILE* type");
- setnilV(L->top++);
- lua_pushliteral(L, "cannot close standard file");
- return 2;
- }
- iof->fp = NULL;
- return luaL_fileresult(L, ok, NULL);
- }
- /* -- Read/write helpers -------------------------------------------------- */
- static int io_file_readnum(lua_State *L, FILE *fp)
- {
- lua_Number d;
- if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) {
- if (LJ_DUALNUM) {
- int32_t i = lj_num2int(d);
- if (d == (lua_Number)i && !tvismzero((cTValue *)&d)) {
- setintV(L->top++, i);
- return 1;
- }
- }
- setnumV(L->top++, d);
- return 1;
- } else {
- setnilV(L->top++);
- return 0;
- }
- }
- static int io_file_readline(lua_State *L, FILE *fp, MSize chop)
- {
- MSize m = LUAL_BUFFERSIZE, n = 0, ok = 0;
- char *buf;
- for (;;) {
- buf = lj_buf_tmp(L, m);
- if (fgets(buf+n, m-n, fp) == NULL) break;
- n += (MSize)strlen(buf+n);
- ok |= n;
- if (n && buf[n-1] == '\n') { n -= chop; break; }
- if (n >= m - 64) m += m;
- }
- setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
- lj_gc_check(L);
- return (int)ok;
- }
- static void io_file_readall(lua_State *L, FILE *fp)
- {
- MSize m, n;
- for (m = LUAL_BUFFERSIZE, n = 0; ; m += m) {
- char *buf = lj_buf_tmp(L, m);
- n += (MSize)fread(buf+n, 1, m-n, fp);
- if (n != m) {
- setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
- lj_gc_check(L);
- return;
- }
- }
- }
- static int io_file_readlen(lua_State *L, FILE *fp, MSize m)
- {
- if (m) {
- char *buf = lj_buf_tmp(L, m);
- MSize n = (MSize)fread(buf, 1, m, fp);
- setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
- lj_gc_check(L);
- return n > 0;
- } else {
- int c = getc(fp);
- ungetc(c, fp);
- setstrV(L, L->top++, &G(L)->strempty);
- return (c != EOF);
- }
- }
- static int io_file_read(lua_State *L, IOFileUD *iof, int start)
- {
- FILE *fp = iof->fp;
- int ok, n, nargs = (int)(L->top - L->base) - start;
- clearerr(fp);
- if (nargs == 0) {
- ok = io_file_readline(L, fp, 1);
- n = start+1; /* Return 1 result. */
- } else {
- /* The results plus the buffers go on top of the args. */
- luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
- ok = 1;
- for (n = start; nargs-- && ok; n++) {
- if (tvisstr(L->base+n)) {
- const char *p = strVdata(L->base+n);
- if (p[0] == '*') p++;
- if (p[0] == 'n')
- ok = io_file_readnum(L, fp);
- else if ((p[0] & ~0x20) == 'L')
- ok = io_file_readline(L, fp, (p[0] == 'l'));
- else if (p[0] == 'a')
- io_file_readall(L, fp);
- else
- lj_err_arg(L, n+1, LJ_ERR_INVFMT);
- } else if (tvisnumber(L->base+n)) {
- ok = io_file_readlen(L, fp, (MSize)lj_lib_checkint(L, n+1));
- } else {
- lj_err_arg(L, n+1, LJ_ERR_INVOPT);
- }
- }
- }
- if (ferror(fp))
- return luaL_fileresult(L, 0, NULL);
- if (!ok)
- setnilV(L->top-1); /* Replace last result with nil. */
- return n - start;
- }
- static int io_file_write(lua_State *L, IOFileUD *iof, int start)
- {
- FILE *fp = iof->fp;
- cTValue *tv;
- int status = 1;
- for (tv = L->base+start; tv < L->top; tv++) {
- MSize len;
- const char *p = lj_strfmt_wstrnum(L, tv, &len);
- if (!p)
- lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING);
- status = status && (fwrite(p, 1, len, fp) == len);
- }
- if (LJ_52 && status) {
- L->top = L->base+1;
- if (start == 0)
- setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_OUTPUT));
- return 1;
- }
- return luaL_fileresult(L, status, NULL);
- }
- static int io_file_iter(lua_State *L)
- {
- GCfunc *fn = curr_func(L);
- IOFileUD *iof = uddata(udataV(&fn->c.upvalue[0]));
- int n = fn->c.nupvalues - 1;
- if (iof->fp == NULL)
- lj_err_caller(L, LJ_ERR_IOCLFL);
- L->top = L->base;
- if (n) { /* Copy upvalues with options to stack. */
- lj_state_checkstack(L, (MSize)n);
- memcpy(L->top, &fn->c.upvalue[1], n*sizeof(TValue));
- L->top += n;
- }
- n = io_file_read(L, iof, 0);
- if (ferror(iof->fp))
- lj_err_callermsg(L, strVdata(L->top-2));
- if (tvisnil(L->base) && (iof->type & IOFILE_FLAG_CLOSE)) {
- io_file_close(L, iof); /* Return values are ignored. */
- return 0;
- }
- return n;
- }
- static int io_file_lines(lua_State *L)
- {
- int n = (int)(L->top - L->base);
- if (n > LJ_MAX_UPVAL)
- lj_err_caller(L, LJ_ERR_UNPACK);
- lua_pushcclosure(L, io_file_iter, n);
- return 1;
- }
- /* -- I/O file methods ---------------------------------------------------- */
- #define LJLIB_MODULE_io_method
- LJLIB_CF(io_method_close)
- {
- IOFileUD *iof;
- if (L->base < L->top) {
- iof = io_tofile(L);
- } else {
- iof = IOSTDF_IOF(L, GCROOT_IO_OUTPUT);
- if (iof->fp == NULL)
- lj_err_caller(L, LJ_ERR_IOCLFL);
- }
- return io_file_close(L, iof);
- }
- LJLIB_CF(io_method_read)
- {
- return io_file_read(L, io_tofile(L), 1);
- }
- LJLIB_CF(io_method_write) LJLIB_REC(io_write 0)
- {
- return io_file_write(L, io_tofile(L), 1);
- }
- LJLIB_CF(io_method_flush) LJLIB_REC(io_flush 0)
- {
- return luaL_fileresult(L, fflush(io_tofile(L)->fp) == 0, NULL);
- }
- #if LJ_32 && defined(__ANDROID__) && __ANDROID_API__ < 24
- /* The Android NDK is such an unmatched marvel of engineering. */
- extern int fseeko32(FILE *, long int, int) __asm__("fseeko");
- extern long int ftello32(FILE *) __asm__("ftello");
- #define fseeko(fp, pos, whence) (fseeko32((fp), (pos), (whence)))
- #define ftello(fp) (ftello32((fp)))
- #endif
- LJLIB_CF(io_method_seek)
- {
- FILE *fp = io_tofile(L)->fp;
- int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end");
- int64_t ofs = 0;
- cTValue *o;
- int res;
- if (opt == 0) opt = SEEK_SET;
- else if (opt == 1) opt = SEEK_CUR;
- else if (opt == 2) opt = SEEK_END;
- o = L->base+2;
- if (o < L->top) {
- if (tvisint(o))
- ofs = (int64_t)intV(o);
- else if (tvisnum(o))
- ofs = (int64_t)numV(o);
- else if (!tvisnil(o))
- lj_err_argt(L, 3, LUA_TNUMBER);
- }
- #if LJ_TARGET_POSIX
- res = fseeko(fp, ofs, opt);
- #elif _MSC_VER >= 1400
- res = _fseeki64(fp, ofs, opt);
- #elif defined(__MINGW32__)
- res = fseeko64(fp, ofs, opt);
- #else
- res = fseek(fp, (long)ofs, opt);
- #endif
- if (res)
- return luaL_fileresult(L, 0, NULL);
- #if LJ_TARGET_POSIX
- ofs = ftello(fp);
- #elif _MSC_VER >= 1400
- ofs = _ftelli64(fp);
- #elif defined(__MINGW32__)
- ofs = ftello64(fp);
- #else
- ofs = (int64_t)ftell(fp);
- #endif
- setint64V(L->top-1, ofs);
- return 1;
- }
- LJLIB_CF(io_method_setvbuf)
- {
- FILE *fp = io_tofile(L)->fp;
- int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no");
- size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE);
- if (opt == 0) opt = _IOFBF;
- else if (opt == 1) opt = _IOLBF;
- else if (opt == 2) opt = _IONBF;
- return luaL_fileresult(L, setvbuf(fp, NULL, opt, sz) == 0, NULL);
- }
- LJLIB_CF(io_method_lines)
- {
- io_tofile(L);
- return io_file_lines(L);
- }
- LJLIB_CF(io_method___gc)
- {
- IOFileUD *iof = io_tofilep(L);
- if (iof->fp != NULL && (iof->type & IOFILE_TYPE_MASK) != IOFILE_TYPE_STDF)
- io_file_close(L, iof);
- return 0;
- }
- LJLIB_CF(io_method___tostring)
- {
- IOFileUD *iof = io_tofilep(L);
- if (iof->fp != NULL)
- lua_pushfstring(L, "file (%p)", iof->fp);
- else
- lua_pushliteral(L, "file (closed)");
- return 1;
- }
- LJLIB_PUSH(top-1) LJLIB_SET(__index)
- #include "lj_libdef.h"
- /* -- I/O library functions ----------------------------------------------- */
- #define LJLIB_MODULE_io
- LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */
- LJLIB_CF(io_open)
- {
- const char *fname = strdata(lj_lib_checkstr(L, 1));
- GCstr *s = lj_lib_optstr(L, 2);
- const char *mode = s ? strdata(s) : "r";
- IOFileUD *iof = io_file_new(L);
- iof->fp = fopen(fname, mode);
- return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
- }
- LJLIB_CF(io_popen)
- {
- #if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP)
- const char *fname = strdata(lj_lib_checkstr(L, 1));
- GCstr *s = lj_lib_optstr(L, 2);
- const char *mode = s ? strdata(s) : "r";
- IOFileUD *iof = io_file_new(L);
- iof->type = IOFILE_TYPE_PIPE;
- #if LJ_TARGET_POSIX
- fflush(NULL);
- iof->fp = popen(fname, mode);
- #else
- iof->fp = _popen(fname, mode);
- #endif
- return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
- #else
- return luaL_error(L, LUA_QL("popen") " not supported");
- #endif
- }
- LJLIB_CF(io_tmpfile)
- {
- IOFileUD *iof = io_file_new(L);
- #if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA || LJ_TARGET_NX
- iof->fp = NULL; errno = ENOSYS;
- #else
- iof->fp = tmpfile();
- #endif
- return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, NULL);
- }
- LJLIB_CF(io_close)
- {
- return lj_cf_io_method_close(L);
- }
- LJLIB_CF(io_read)
- {
- return io_file_read(L, io_stdfile(L, GCROOT_IO_INPUT), 0);
- }
- LJLIB_CF(io_write) LJLIB_REC(io_write GCROOT_IO_OUTPUT)
- {
- return io_file_write(L, io_stdfile(L, GCROOT_IO_OUTPUT), 0);
- }
- LJLIB_CF(io_flush) LJLIB_REC(io_flush GCROOT_IO_OUTPUT)
- {
- return luaL_fileresult(L, fflush(io_stdfile(L, GCROOT_IO_OUTPUT)->fp) == 0, NULL);
- }
- static int io_std_getset(lua_State *L, ptrdiff_t id, const char *mode)
- {
- if (L->base < L->top && !tvisnil(L->base)) {
- if (tvisudata(L->base)) {
- io_tofile(L);
- L->top = L->base+1;
- } else {
- io_file_open(L, mode);
- }
- /* NOBARRIER: The standard I/O handles are GC roots. */
- setgcref(G(L)->gcroot[id], gcV(L->top-1));
- } else {
- setudataV(L, L->top++, IOSTDF_UD(L, id));
- }
- return 1;
- }
- LJLIB_CF(io_input)
- {
- return io_std_getset(L, GCROOT_IO_INPUT, "r");
- }
- LJLIB_CF(io_output)
- {
- return io_std_getset(L, GCROOT_IO_OUTPUT, "w");
- }
- LJLIB_CF(io_lines)
- {
- if (L->base == L->top) setnilV(L->top++);
- if (!tvisnil(L->base)) { /* io.lines(fname) */
- IOFileUD *iof = io_file_open(L, "r");
- iof->type = IOFILE_TYPE_FILE|IOFILE_FLAG_CLOSE;
- L->top--;
- setudataV(L, L->base, udataV(L->top));
- } else { /* io.lines() iterates over stdin. */
- setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_INPUT));
- }
- return io_file_lines(L);
- }
- LJLIB_CF(io_type)
- {
- cTValue *o = lj_lib_checkany(L, 1);
- if (!(tvisudata(o) && udataV(o)->udtype == UDTYPE_IO_FILE))
- setnilV(L->top++);
- else if (((IOFileUD *)uddata(udataV(o)))->fp != NULL)
- lua_pushliteral(L, "file");
- else
- lua_pushliteral(L, "closed file");
- return 1;
- }
- #include "lj_libdef.h"
- /* ------------------------------------------------------------------------ */
- static GCobj *io_std_new(lua_State *L, FILE *fp, const char *name)
- {
- IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
- GCudata *ud = udataV(L->top-1);
- ud->udtype = UDTYPE_IO_FILE;
- /* NOBARRIER: The GCudata is new (marked white). */
- setgcref(ud->metatable, gcV(L->top-3));
- iof->fp = fp;
- iof->type = IOFILE_TYPE_STDF;
- lua_setfield(L, -2, name);
- return obj2gco(ud);
- }
- LUALIB_API int luaopen_io(lua_State *L)
- {
- LJ_LIB_REG(L, NULL, io_method);
- copyTV(L, L->top, L->top-1); L->top++;
- lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
- LJ_LIB_REG(L, LUA_IOLIBNAME, io);
- setgcref(G(L)->gcroot[GCROOT_IO_INPUT], io_std_new(L, stdin, "stdin"));
- setgcref(G(L)->gcroot[GCROOT_IO_OUTPUT], io_std_new(L, stdout, "stdout"));
- io_std_new(L, stderr, "stderr");
- return 1;
- }
|