File: //home/ubuntu/neovim/.deps/build/src/luv/src/fs.c
/*
* Copyright 2014 The Luvit Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "private.h"
#if LUV_UV_VERSION_GEQ(1, 28, 0)
typedef struct {
uv_dir_t* handle;
int dirents_ref; /* handle has been closed if this is LUA_NOREF */
} luv_dir_t;
#endif
typedef struct {
uv_fs_t* req;
} luv_fs_scandir_t;
static uv_fs_t* luv_check_fs(lua_State* L, int index) {
if (luaL_testudata(L, index, "uv_fs_scandir") != NULL) {
luv_fs_scandir_t* scandir_req = (luv_fs_scandir_t*)lua_touserdata(L, index);
return (uv_fs_t*)(scandir_req->req);
}
uv_fs_t* req = (uv_fs_t*)luaL_checkudata(L, index, "uv_req");
luaL_argcheck(L, req->type == UV_FS && req->data, index, "Expected uv_fs_t");
return req;
}
static int luv_fs_gc(lua_State* L) {
uv_fs_t* req = luv_check_fs(L, 1);
luv_cleanup_req(L, (luv_req_t*)req->data);
req->data = NULL;
uv_fs_req_cleanup(req);
return 0;
}
static void luv_push_timespec_table(lua_State* L, const uv_timespec_t* t) {
lua_createtable(L, 0, 2);
lua_pushinteger(L, t->tv_sec);
lua_setfield(L, -2, "sec");
lua_pushinteger(L, t->tv_nsec);
lua_setfield(L, -2, "nsec");
}
static void luv_push_stats_table(lua_State* L, const uv_stat_t* s) {
const char* type = NULL;
lua_createtable(L, 0, 23);
lua_pushinteger(L, s->st_dev);
lua_setfield(L, -2, "dev");
lua_pushinteger(L, s->st_mode);
lua_setfield(L, -2, "mode");
lua_pushinteger(L, s->st_nlink);
lua_setfield(L, -2, "nlink");
lua_pushinteger(L, s->st_uid);
lua_setfield(L, -2, "uid");
lua_pushinteger(L, s->st_gid);
lua_setfield(L, -2, "gid");
lua_pushinteger(L, s->st_rdev);
lua_setfield(L, -2, "rdev");
lua_pushinteger(L, s->st_ino);
lua_setfield(L, -2, "ino");
lua_pushinteger(L, s->st_size);
lua_setfield(L, -2, "size");
lua_pushinteger(L, s->st_blksize);
lua_setfield(L, -2, "blksize");
lua_pushinteger(L, s->st_blocks);
lua_setfield(L, -2, "blocks");
lua_pushinteger(L, s->st_flags);
lua_setfield(L, -2, "flags");
lua_pushinteger(L, s->st_gen);
lua_setfield(L, -2, "gen");
luv_push_timespec_table(L, &s->st_atim);
lua_setfield(L, -2, "atime");
luv_push_timespec_table(L, &s->st_mtim);
lua_setfield(L, -2, "mtime");
luv_push_timespec_table(L, &s->st_ctim);
lua_setfield(L, -2, "ctime");
luv_push_timespec_table(L, &s->st_birthtim);
lua_setfield(L, -2, "birthtime");
if (S_ISREG(s->st_mode)) {
type = "file";
}
else if (S_ISDIR(s->st_mode)) {
type = "directory";
}
else if (S_ISLNK(s->st_mode)) {
type = "link";
}
else if (S_ISFIFO(s->st_mode)) {
type = "fifo";
}
#ifdef S_ISSOCK
else if (S_ISSOCK(s->st_mode)) {
type = "socket";
}
#endif
else if (S_ISCHR(s->st_mode)) {
type = "char";
}
else if (S_ISBLK(s->st_mode)) {
type = "block";
}
if (type) {
lua_pushstring(L, type);
lua_setfield(L, -2, "type");
}
}
static int luv_push_dirent(lua_State* L, const uv_dirent_t* ent, int table) {
const char* type;
if (table) {
lua_newtable(L);
}
lua_pushstring(L, ent->name);
if (table) {
lua_setfield(L, -2, "name");
}
switch (ent->type) {
case UV_DIRENT_UNKNOWN: return 1;
case UV_DIRENT_FILE: type = "file"; break;
case UV_DIRENT_DIR: type = "directory"; break;
case UV_DIRENT_LINK: type = "link"; break;
case UV_DIRENT_FIFO: type = "fifo"; break;
case UV_DIRENT_SOCKET: type = "socket"; break;
case UV_DIRENT_CHAR: type = "char"; break;
case UV_DIRENT_BLOCK: type = "block"; break;
default: type = "unknown"; break;
}
lua_pushstring(L, type);
if (table)
lua_setfield(L, -2, "type");
return table ? 1 : 2;
}
static int luv_check_flags(lua_State* L, int index) {
const char* string;
if (lua_isnumber(L, index)) {
return lua_tointeger(L, index);
}
else if (!lua_isstring(L, index)) {
return luaL_argerror(L, index, "Expected string or integer for file open mode");
}
string = lua_tostring(L, index);
if (strcmp(string, "r") == 0) return O_RDONLY;
#ifdef O_SYNC
if (strcmp(string, "rs") == 0 ||
strcmp(string, "sr") == 0) return O_RDONLY | O_SYNC;
#endif
if (strcmp(string, "r+") == 0) return O_RDWR;
#ifdef O_SYNC
if (strcmp(string, "rs+") == 0 ||
strcmp(string, "sr+") == 0) return O_RDWR | O_SYNC;
#endif
if (strcmp(string, "w") == 0) return O_TRUNC | O_CREAT | O_WRONLY;
if (strcmp(string, "wx") == 0 ||
strcmp(string, "xw") == 0) return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
if (strcmp(string, "w+") == 0) return O_TRUNC | O_CREAT | O_RDWR;
if (strcmp(string, "wx+") == 0 ||
strcmp(string, "xw+") == 0) return O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
if (strcmp(string, "a") == 0) return O_APPEND | O_CREAT | O_WRONLY;
if (strcmp(string, "ax") == 0 ||
strcmp(string, "xa") == 0) return O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
if (strcmp(string, "a+") == 0) return O_APPEND | O_CREAT | O_RDWR;
if (strcmp(string, "ax+") == 0 ||
strcmp(string, "xa+") == 0) return O_APPEND | O_CREAT | O_RDWR | O_EXCL;
return luaL_error(L, "Unknown file open flag '%s'", string);
}
static int luv_check_amode(lua_State* L, int index) {
size_t i;
int mode;
const char* string;
if (lua_isnumber(L, index)) {
return lua_tointeger(L, index);
}
else if (!lua_isstring(L, index)) {
return luaL_argerror(L, index, "Expected string or integer for file access mode check");
}
string = lua_tostring(L, index);
mode = 0;
for (i = 0; i < strlen(string); ++i) {
switch (string[i]) {
case 'r': case 'R':
mode |= R_OK;
break;
case 'w': case 'W':
mode |= W_OK;
break;
case 'x': case 'X':
mode |= X_OK;
break;
default:
return luaL_argerror(L, index, "Unknown character in access mode string");
}
}
return mode;
}
#if LUV_UV_VERSION_GEQ(1, 31, 0)
static void luv_push_statfs_table(lua_State* L, const uv_statfs_t* s) {
lua_createtable(L, 0, 8);
lua_pushinteger(L, s->f_type);
lua_setfield(L, -2, "type");
lua_pushinteger(L, s->f_bsize);
lua_setfield(L, -2, "bsize");
lua_pushinteger(L, s->f_blocks);
lua_setfield(L, -2, "blocks");
lua_pushinteger(L, s->f_bfree);
lua_setfield(L, -2, "bfree");
lua_pushinteger(L, s->f_bavail);
lua_setfield(L, -2, "bavail");
lua_pushinteger(L, s->f_files);
lua_setfield(L, -2, "files");
lua_pushinteger(L, s->f_ffree);
lua_setfield(L, -2, "ffree");
};
#endif
static int fs_req_has_dest_path(uv_fs_t* req) {
switch (req->fs_type) {
case UV_FS_RENAME:
case UV_FS_SYMLINK:
case UV_FS_LINK:
#if LUV_UV_VERSION_GEQ(1, 14, 0)
case UV_FS_COPYFILE:
#endif
return 1;
default:
return 0;
}
}
/* Processes a result and pushes the data onto the stack
returns the number of items pushed */
static int push_fs_result(lua_State* L, uv_fs_t* req) {
luv_req_t* data = (luv_req_t*)req->data;
if (req->fs_type == UV_FS_ACCESS) {
lua_pushboolean(L, req->result >= 0);
return 1;
}
if (req->result < 0) {
if (req->fs_type == UV_FS_SCANDIR) {
// We need to unref the luv_fs_scandir_t userdata to allow it to be garbage collected.
// The scandir callback can only be called once, so we now know that the
// req can be safely garbage collected.
luaL_unref(L, LUA_REGISTRYINDEX, data->data_ref);
data->data_ref = LUA_NOREF;
}
lua_pushnil(L);
if (fs_req_has_dest_path(req)) {
lua_rawgeti(L, LUA_REGISTRYINDEX, data->data_ref);
const char* dest_path = lua_tostring(L, -1);
lua_pop(L, 1);
lua_pushfstring(L, "%s: %s: %s -> %s", uv_err_name(req->result), uv_strerror(req->result), req->path, dest_path);
}
else if (req->path) {
lua_pushfstring(L, "%s: %s: %s", uv_err_name(req->result), uv_strerror(req->result), req->path);
}
else {
lua_pushfstring(L, "%s: %s", uv_err_name(req->result), uv_strerror(req->result));
}
return 2;
}
switch (req->fs_type) {
case UV_FS_CLOSE:
case UV_FS_RENAME:
case UV_FS_UNLINK:
case UV_FS_RMDIR:
case UV_FS_MKDIR:
case UV_FS_FTRUNCATE:
case UV_FS_FSYNC:
case UV_FS_FDATASYNC:
case UV_FS_LINK:
case UV_FS_SYMLINK:
case UV_FS_CHMOD:
case UV_FS_FCHMOD:
case UV_FS_CHOWN:
case UV_FS_FCHOWN:
#if LUV_UV_VERSION_GEQ(1, 21, 0)
case UV_FS_LCHOWN:
#endif
case UV_FS_UTIME:
case UV_FS_FUTIME:
#if LUV_UV_VERSION_GEQ(1, 36, 0)
case UV_FS_LUTIME:
#endif
#if LUV_UV_VERSION_GEQ(1, 14, 0)
case UV_FS_COPYFILE:
#endif
lua_pushboolean(L, 1);
return 1;
case UV_FS_OPEN:
case UV_FS_SENDFILE:
case UV_FS_WRITE:
lua_pushinteger(L, req->result);
return 1;
case UV_FS_STAT:
case UV_FS_LSTAT:
case UV_FS_FSTAT:
luv_push_stats_table(L, &req->statbuf);
return 1;
#if LUV_UV_VERSION_GEQ(1, 31, 0)
case UV_FS_STATFS:
luv_push_statfs_table(L, req->ptr);
return 1;
#endif
case UV_FS_MKDTEMP:
lua_pushstring(L, req->path);
return 1;
#if LUV_UV_VERSION_GEQ(1, 34, 0)
case UV_FS_MKSTEMP:
lua_pushinteger(L, req->result);
lua_pushstring(L, req->path);
return 2;
#endif
case UV_FS_READLINK:
#if LUV_UV_VERSION_GEQ(1, 8, 0)
case UV_FS_REALPATH:
#endif
lua_pushstring(L, (char*)req->ptr);
return 1;
case UV_FS_READ:
lua_pushlstring(L, (const char*)data->data, req->result);
return 1;
case UV_FS_SCANDIR:
// The luv_fs_scandir_t userdata is stored in data_ref.
// We want to return this instead of the uv_req_t because the
// luv_fs_scandir_t userdata has a gc method.
lua_rawgeti(L, LUA_REGISTRYINDEX, data->data_ref);
// We now want to unref the userdata to allow it to be garbage collected.
// The scandir callback can only be called once, so we now know that the
// req can be safely garbage collected.
luaL_unref(L, LUA_REGISTRYINDEX, data->data_ref);
data->data_ref = LUA_NOREF;
return 1;
#if LUV_UV_VERSION_GEQ(1, 28, 0)
case UV_FS_OPENDIR: {
int nentries;
uv_dir_t* dir = (uv_dir_t*)req->ptr;
lua_rawgeti(L, LUA_REGISTRYINDEX, data->data_ref);
nentries = luaL_checkinteger(L, -1);
lua_pop(L, 1);
luaL_unref(L, LUA_REGISTRYINDEX, data->data_ref);
data->data_ref = LUA_NOREF;
luv_dir_t* luv_dir = lua_newuserdata(L, sizeof(*luv_dir));
luaL_getmetatable(L, "uv_dir");
lua_setmetatable(L, -2);
luv_dir->handle = dir;
luv_dir->handle->dirents = lua_newuserdata(L, sizeof(uv_dirent_t)*nentries);
luv_dir->dirents_ref = luaL_ref(L, LUA_REGISTRYINDEX);
luv_dir->handle->nentries = nentries;
return 1;
}
case UV_FS_READDIR: {
luaL_unref(L, LUA_REGISTRYINDEX, data->data_ref);
data->data_ref = LUA_NOREF;
if(req->result > 0) {
size_t i;
uv_dir_t *dir = (uv_dir_t*)req->ptr;
lua_newtable(L);
for(i=0; i<req->result; i++) {
luv_push_dirent(L, dir->dirents+i, 1);
lua_rawseti(L, -2, i+1);
}
} else
lua_pushnil(L);
return 1;
}
case UV_FS_CLOSEDIR:
lua_pushboolean(L, 1);
return 1;
#endif
default:
lua_pushnil(L);
lua_pushfstring(L, "UNKNOWN FS TYPE %d\n", req->fs_type);
return 2;
}
}
static void luv_fs_cb(uv_fs_t* req) {
luv_req_t* data = (luv_req_t*)req->data;
// This can happen if luv_fs_cb is called during loop gc. For example, this can happen
// when the async version of fs_scandir is called but the loop is never run before the process exits.
//
// TODO: A more comprehensive fix for this problem would be related to https://github.com/luvit/luv/issues/437
if (data == NULL) return;
lua_State* L = data->ctx->L;
int nargs = push_fs_result(L, req);
if (nargs == 2 && lua_isnil(L, -nargs)) {
// If it was an error, convert to (err, value) format.
lua_remove(L, -nargs);
nargs--;
}
else {
// Otherwise insert a nil in front to convert to (err, value) format.
lua_pushnil(L);
lua_insert(L, -nargs - 1);
nargs++;
}
if (req->fs_type == UV_FS_SCANDIR) {
luv_fulfill_req(L, data, nargs);
}
else {
// cleanup the uv_fs_t before the callback is called to avoid
// a race condition when fs_close is called from within
// a fs_readdir callback, see https://github.com/luvit/luv/issues/384
uv_fs_req_cleanup(req);
req->data = NULL;
luv_fulfill_req(L, data, nargs);
luv_cleanup_req(L, data);
}
}
// handle the FS call but don't return, instead set the local
// variable 'nargs' to the number of return values
#define FS_CALL_NORETURN(func, req, ...) { \
int ret, sync; \
luv_req_t* lreq = (luv_req_t*)req->data; \
sync = lreq->callback_ref == LUA_NOREF; \
ret = func(lreq->ctx->loop, req, __VA_ARGS__, \
sync ? NULL : luv_fs_cb); \
if (req->fs_type != UV_FS_ACCESS && ret < 0) { \
lua_pushnil(L); \
if (fs_req_has_dest_path(req)) { \
lua_rawgeti(L, LUA_REGISTRYINDEX, lreq->data_ref); \
const char* dest_path = lua_tostring(L, -1); \
lua_pop(L, 1); \
lua_pushfstring(L, "%s: %s: %s -> %s", \
uv_err_name(req->result), \
uv_strerror(req->result), \
req->path, dest_path); \
} \
else if (req->path) { \
lua_pushfstring(L, "%s: %s: %s", \
uv_err_name(req->result), \
uv_strerror(req->result), req->path); \
} \
else { \
lua_pushfstring(L, "%s: %s", \
uv_err_name(req->result), \
uv_strerror(req->result)); \
} \
lua_pushstring(L, uv_err_name(req->result)); \
if(req->fs_type != UV_FS_SCANDIR) { \
luv_cleanup_req(L, lreq); \
req->data = NULL; \
uv_fs_req_cleanup(req); \
} \
nargs = 3; \
} \
else if (sync) { \
nargs = push_fs_result(L, req); \
if(req->fs_type != UV_FS_SCANDIR) { \
luv_cleanup_req(L, lreq); \
req->data = NULL; \
uv_fs_req_cleanup(req); \
} \
} \
else { \
lua_rawgeti(L, LUA_REGISTRYINDEX, lreq->req_ref); \
nargs = 1; \
} \
}
#define FS_CALL(func, req, ...) { \
int nargs; \
FS_CALL_NORETURN(func, req, __VA_ARGS__) \
return nargs; \
}
static int luv_fs_close(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_close, req, file);
}
static int luv_fs_open(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int flags = luv_check_flags(L, 2);
int mode = luaL_checkinteger(L, 3);
int ref = luv_check_continuation(L, 4);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_open, req, path, flags, mode);
}
static int luv_fs_read(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
int64_t len = luaL_checkinteger(L, 2);
// -1 offset means "the current file offset is used and updated"
int64_t offset = -1;
int ref;
char* data;
// both offset and callback are optional
if (luv_is_callable(L, 3) && lua_isnoneornil(L, 4)) {
ref = luv_check_continuation(L, 3);
}
else {
offset = luaL_optinteger(L, 3, offset);
ref = luv_check_continuation(L, 4);
}
data = (char*)malloc(len);
if (!data) {
luaL_unref(L, LUA_REGISTRYINDEX, ref);
return luaL_error(L, "Failure to allocate buffer");
}
uv_buf_t buf = uv_buf_init(data, len);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
// TODO: find out why we can't just use req->ptr for the base
((luv_req_t*)req->data)->data = buf.base;
FS_CALL(uv_fs_read, req, file, &buf, 1, offset);
}
static int luv_fs_unlink(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_unlink, req, path);
}
static int luv_fs_write(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
// -1 offset means "the current file offset is used and updated"
int64_t offset = -1;
int ref;
// both offset and callback are optional
if (luv_is_callable(L, 3) && lua_isnoneornil(L, 4)) {
ref = luv_check_continuation(L, 3);
}
else {
offset = luaL_optinteger(L, 3, offset);
ref = luv_check_continuation(L, 4);
}
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
size_t count;
uv_buf_t* bufs = luv_check_bufs(L, 2, &count, (luv_req_t*)req->data);
int nargs;
FS_CALL_NORETURN(uv_fs_write, req, file, bufs, count, offset);
free(bufs);
return nargs;
}
static int luv_fs_mkdir(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int mode = luaL_checkinteger(L, 2);
int ref = luv_check_continuation(L, 3);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_mkdir, req, path, mode);
}
static int luv_fs_mkdtemp(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* tpl = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_mkdtemp, req, tpl);
}
#if LUV_UV_VERSION_GEQ(1, 34, 0)
static int luv_fs_mkstemp(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* tpl = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_mkstemp, req, tpl);
}
#endif
static int luv_fs_rmdir(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_rmdir, req, path);
}
static int luv_fs_scandir(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int flags = 0; // TODO: find out what these flags are.
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
int sync = ref == LUA_NOREF;
// Wrap the req in a garbage-collectable wrapper.
// This allows us to separate the lifetime of the uv_req_t from the lifetime
// of the userdata that gets returned here, since otherwise the returned uv_req_t
// would hold a reference to itself making it ineligible for garbage collection before
// the process ends.
luv_fs_scandir_t* scandir_req = (luv_fs_scandir_t*)lua_newuserdata(L, sizeof(luv_fs_scandir_t));
scandir_req->req = req;
luaL_getmetatable(L, "uv_fs_scandir");
lua_setmetatable(L, -2);
int scandir_req_index = lua_gettop(L);
int nargs;
FS_CALL_NORETURN(uv_fs_scandir, req, path, flags);
// This indicates an error, so we want to return immediately
if (nargs != 1) return nargs;
// Ref the return if this is async, since we don't want this to be garbage collected
// before the callback is called.
if (!sync) {
lua_pushvalue(L, scandir_req_index);
((luv_req_t*)req->data)->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}
lua_pushvalue(L, scandir_req_index);
return 1;
}
static int luv_fs_scandir_next(lua_State* L) {
uv_fs_t* req = luv_check_fs(L, 1);
uv_dirent_t ent;
int ret = uv_fs_scandir_next(req, &ent);
if (ret == UV_EOF) return 0;
if (ret < 0) return luv_error(L, ret);
return luv_push_dirent(L, &ent, 0);
}
static int luv_fs_stat(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_stat, req, path);
}
static int luv_fs_fstat(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_fstat, req, file);
}
static int luv_fs_lstat(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_lstat, req, path);
}
static int luv_fs_rename(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
const char* new_path = luaL_checkstring(L, 2);
int ref = luv_check_continuation(L, 3);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
// ref the dest path so that we can print it in the error message
lua_pushvalue(L, 2);
((luv_req_t*)req->data)->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
FS_CALL(uv_fs_rename, req, path, new_path);
}
static int luv_fs_fsync(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_fsync, req, file);
}
static int luv_fs_fdatasync(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_fdatasync, req, file);
}
static int luv_fs_ftruncate(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
int64_t offset = luaL_checkinteger(L, 2);
int ref = luv_check_continuation(L, 3);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_ftruncate, req, file, offset);
}
static int luv_fs_sendfile(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file out_fd = luaL_checkinteger(L, 1);
uv_file in_fd = luaL_checkinteger(L, 2);
int64_t in_offset = luaL_checkinteger(L, 3);
size_t length = luaL_checkinteger(L, 4);
int ref = luv_check_continuation(L, 5);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_sendfile, req, out_fd, in_fd, in_offset, length);
}
static int luv_fs_access(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int amode = luv_check_amode(L, 2);
int ref = luv_check_continuation(L, 3);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_access, req, path, amode);
}
static int luv_fs_chmod(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int mode = luaL_checkinteger(L, 2);
int ref = luv_check_continuation(L, 3);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_chmod, req, path, mode);
}
static int luv_fs_fchmod(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
int mode = luaL_checkinteger(L, 2);
int ref = luv_check_continuation(L, 3);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_fchmod, req, file, mode);
}
static int luv_fs_utime(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
double atime = luaL_checknumber(L, 2);
double mtime = luaL_checknumber(L, 3);
int ref = luv_check_continuation(L, 4);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_utime, req, path, atime, mtime);
}
static int luv_fs_futime(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
double atime = luaL_checknumber(L, 2);
double mtime = luaL_checknumber(L, 3);
int ref = luv_check_continuation(L, 4);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_futime, req, file, atime, mtime);
}
#if LUV_UV_VERSION_GEQ(1, 36, 0)
static int luv_fs_lutime(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
double atime = luaL_checknumber(L, 2);
double mtime = luaL_checknumber(L, 3);
int ref = luv_check_continuation(L, 4);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_lutime, req, path, atime, mtime);
}
#endif
static int luv_fs_link(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
const char* new_path = luaL_checkstring(L, 2);
int ref = luv_check_continuation(L, 3);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
// ref the dest path so that we can print it in the error message
lua_pushvalue(L, 2);
((luv_req_t*)req->data)->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
FS_CALL(uv_fs_link, req, path, new_path);
}
static int luv_fs_symlink(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
const char* new_path = luaL_checkstring(L, 2);
int flags = 0, ref;
uv_fs_t* req;
// callback can be the 3rd parameter
if (luv_is_callable(L, 3) && lua_isnone(L, 4)) {
ref = luv_check_continuation(L, 3);
} else {
if (lua_type(L, 3) == LUA_TTABLE) {
lua_getfield(L, 3, "dir");
if (lua_toboolean(L, -1)) flags |= UV_FS_SYMLINK_DIR;
lua_pop(L, 1);
lua_getfield(L, 3, "junction");
if (lua_toboolean(L, -1)) flags |= UV_FS_SYMLINK_JUNCTION;
lua_pop(L, 1);
}
else if (lua_type(L, 3) == LUA_TNUMBER) {
flags = lua_tointeger(L, 3);
}
else if (!lua_isnoneornil(L, 3)) {
return luv_arg_type_error(L, 3, "table, integer, or nil expected, got %s");
}
ref = luv_check_continuation(L, 4);
}
req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
// ref the dest path so that we can print it in the error message
lua_pushvalue(L, 2);
((luv_req_t*)req->data)->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
FS_CALL(uv_fs_symlink, req, path, new_path, flags);
}
static int luv_fs_readlink(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_readlink, req, path);
}
#if LUV_UV_VERSION_GEQ(1, 8, 0)
static int luv_fs_realpath(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_realpath, req, path);
}
#endif
static int luv_fs_chown(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
uv_uid_t uid = luaL_checkinteger(L, 2);
uv_uid_t gid = luaL_checkinteger(L, 3);
int ref = luv_check_continuation(L, 4);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_chown, req, path, uid, gid);
}
static int luv_fs_fchown(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_file file = luaL_checkinteger(L, 1);
uv_uid_t uid = luaL_checkinteger(L, 2);
uv_uid_t gid = luaL_checkinteger(L, 3);
int ref = luv_check_continuation(L, 4);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_fchown, req, file, uid, gid);
}
#if LUV_UV_VERSION_GEQ(1, 21, 0)
static int luv_fs_lchown(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
uv_uid_t uid = luaL_checkinteger(L, 2);
uv_uid_t gid = luaL_checkinteger(L, 3);
int ref = luv_check_continuation(L, 4);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_lchown, req, path, uid, gid);
}
#endif
#if LUV_UV_VERSION_GEQ(1, 14, 0)
static int luv_fs_copyfile(lua_State*L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
const char* new_path = luaL_checkstring(L, 2);
int flags = 0, ref;
uv_fs_t* req;
// callback can be the 3rd parameter
if (luv_is_callable(L, 3) && lua_isnone(L, 4)) {
ref = luv_check_continuation(L, 3);
} else {
if (lua_type(L, 3) == LUA_TTABLE) {
lua_getfield(L, 3, "excl");
if (lua_toboolean(L, -1)) flags |= UV_FS_COPYFILE_EXCL;
lua_pop(L, 1);
#if LUV_UV_VERSION_GEQ(1, 20, 0)
lua_getfield(L, 3, "ficlone");
if (lua_toboolean(L, -1)) flags |= UV_FS_COPYFILE_FICLONE;
lua_pop(L, 1);
lua_getfield(L, 3, "ficlone_force");
if (lua_toboolean(L, -1)) flags |= UV_FS_COPYFILE_FICLONE_FORCE;
lua_pop(L, 1);
#endif
}
else if (lua_type(L, 3) == LUA_TNUMBER) {
flags = lua_tointeger(L, 3);
}
else if (!lua_isnoneornil(L, 3)) {
return luv_arg_type_error(L, 3, "table, integer, or nil expected, got %s");
}
ref = luv_check_continuation(L, 4);
}
req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
// ref the dest path so that we can print it in the error message
lua_pushvalue(L, 2);
((luv_req_t*)req->data)->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
FS_CALL(uv_fs_copyfile, req, path, new_path, flags);
}
#endif
#if LUV_UV_VERSION_GEQ(1, 28, 0)
static luv_dir_t* luv_check_dir(lua_State* L, int idx) {
luv_dir_t* dir = (luv_dir_t*)luaL_checkudata(L, idx, "uv_dir");
return dir;
}
static int luv_fs_opendir(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
size_t nentries = luaL_optinteger(L, 3, 1);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
//make data_ref to nentries
lua_pushinteger(L, nentries);
((luv_req_t*)req->data)->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
FS_CALL(uv_fs_opendir, req, path);
}
static int luv_fs_readdir(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
uv_fs_t *req;
luv_dir_t* dir = luv_check_dir(L, 1);
int ref = luv_check_continuation(L, 2);
req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
// ref the luv_dir_t so it doesn't get garbage collected before the readdir cb
lua_pushvalue(L, 1);
((luv_req_t*)req->data)->data_ref = luaL_ref(L, LUA_REGISTRYINDEX);
FS_CALL(uv_fs_readdir, req, dir->handle);
}
static int luv_fs_closedir(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
luv_dir_t* dir = luv_check_dir(L, 1);
int ref = luv_check_continuation(L, 2);
luaL_unref(L, LUA_REGISTRYINDEX, dir->dirents_ref);
dir->dirents_ref = LUA_NOREF;
uv_fs_t *req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_closedir, req, dir->handle);
}
static int luv_fs_dir_tostring(lua_State* L) {
luv_dir_t* dir = luv_check_dir(L, 1);
lua_pushfstring(L, "uv_dir_t: %p", dir);
return 1;
}
static int luv_fs_dir_gc(lua_State* L) {
luv_dir_t* dir = luv_check_dir(L, 1);
if (dir->dirents_ref != LUA_NOREF) {
uv_fs_t req;
luv_ctx_t* ctx = luv_context(L);
luaL_unref(L, LUA_REGISTRYINDEX, dir->dirents_ref);
dir->dirents_ref = LUA_NOREF;
uv_fs_closedir(ctx->loop, &req, dir->handle, NULL);
uv_fs_req_cleanup(&req);
}
lua_pop(L, 1);
return 0;
}
#endif
#if LUV_UV_VERSION_GEQ(1, 31, 0)
static int luv_fs_statfs(lua_State* L) {
luv_ctx_t* ctx = luv_context(L);
const char* path = luaL_checkstring(L, 1);
int ref = luv_check_continuation(L, 2);
uv_fs_t* req = (uv_fs_t*)lua_newuserdata(L, uv_req_size(UV_FS));
req->data = luv_setup_req(L, ctx, ref);
FS_CALL(uv_fs_statfs, req, path);
}
#endif