/* * This file is part of LEM, a Lua Event Machine. * Copyright 2012 Emil Renner Berthing * * LEM is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * LEM is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with LEM. If not, see . */ #include #include #include #include #include #include #include #include #include #include static int lfs_closed(lua_State *T) { lua_pushnil(T); lua_pushliteral(T, "closed"); return 2; } static int lfs_busy(lua_State *T) { lua_pushnil(T); lua_pushliteral(T, "busy"); return 2; } static int lfs_strerror(lua_State *T, int err) { lua_pushnil(T); lua_pushstring(T, strerror(err)); return 2; } struct lfs_pathop { struct lem_async a; lua_State *T; union { const char *path; int ret; }; }; static void lfs_pathop_reap(struct lem_async *a) { struct lfs_pathop *po = (struct lfs_pathop *)a; lua_State *T = po->T; int ret = po->ret; free(po); if (ret) { lem_queue(T, lfs_strerror(T, ret)); return; } lua_pushboolean(T, 1); lem_queue(T, 1); } /* * lfs.chdir() */ static void lfs_chdir_work(struct lem_async *a) { struct lfs_pathop *po = (struct lfs_pathop *)a; if (chdir(po->path)) po->ret = errno; else po->ret = 0; } static int lfs_chdir(lua_State *T) { const char *path = luaL_checkstring(T, 1); struct lfs_pathop *po; po = lem_xmalloc(sizeof(struct lfs_pathop)); po->T = T; po->path = path; lem_async_do(&po->a, lfs_chdir_work, lfs_pathop_reap); lua_settop(T, 1); return lua_yield(T, 1); } /* * lfs.mkdir() */ static void lfs_mkdir_work(struct lem_async *a) { struct lfs_pathop *po = (struct lfs_pathop *)a; if (mkdir(po->path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH )) po->ret = errno; else po->ret = 0; } static int lfs_mkdir(lua_State *T) { const char *path = luaL_checkstring(T, 1); struct lfs_pathop *po; po = lem_xmalloc(sizeof(struct lfs_pathop)); po->T = T; po->path = path; lem_async_do(&po->a, lfs_mkdir_work, lfs_pathop_reap); lua_settop(T, 1); return lua_yield(T, 1); } /* * lfs.rmdir() */ static void lfs_rmdir_work(struct lem_async *a) { struct lfs_pathop *po = (struct lfs_pathop *)a; if (rmdir(po->path)) po->ret = errno; else po->ret = 0; } static int lfs_rmdir(lua_State *T) { const char *path = luaL_checkstring(T, 1); struct lfs_pathop *po; po = lem_xmalloc(sizeof(struct lfs_pathop)); po->T = T; po->path = path; lem_async_do(&po->a, lfs_rmdir_work, lfs_pathop_reap); lua_settop(T, 1); return lua_yield(T, 1); } /* * lfs.remove() */ static void lfs_remove_work(struct lem_async *a) { struct lfs_pathop *po = (struct lfs_pathop *)a; if (unlink(po->path)) po->ret = errno; else po->ret = 0; } static int lfs_remove(lua_State *T) { const char *path = luaL_checkstring(T, 1); struct lfs_pathop *po; po = lem_xmalloc(sizeof(struct lfs_pathop)); po->T = T; po->path = path; lem_async_do(&po->a, lfs_remove_work, lfs_pathop_reap); lua_settop(T, 1); return lua_yield(T, 1); } /* * lfs.link() and lfs.rename() */ struct lfs_twoop { struct lem_async a; lua_State *T; union { struct { const char *old; const char *new; }; int ret; }; }; static void lfs_twoop_reap(struct lem_async *a) { struct lfs_twoop *to = (struct lfs_twoop *)a; lua_State *T = to->T; int ret = to->ret; free(to); if (ret) { lem_queue(T, lfs_strerror(T, ret)); return; } lua_pushboolean(T, 1); lem_queue(T, 1); } static void lfs_link_work(struct lem_async *a) { struct lfs_twoop *to = (struct lfs_twoop *)a; if (link(to->old, to->new)) to->ret = errno; else to->ret = 0; } static void lfs_symlink_work(struct lem_async *a) { struct lfs_twoop *to = (struct lfs_twoop *)a; if (symlink(to->old, to->new)) to->ret = errno; else to->ret = 0; } static int lfs_link(lua_State *T) { const char *old = luaL_checkstring(T, 1); const char *new = luaL_checkstring(T, 2); int symlink = lua_toboolean(T, 3); struct lfs_twoop *to; to = lem_xmalloc(sizeof(struct lfs_twoop)); to->T = T; to->old = old; to->new = new; lem_async_do(&to->a, symlink ? lfs_symlink_work : lfs_link_work, lfs_twoop_reap); lua_settop(T, 2); return lua_yield(T, 2); } static void lfs_rename_work(struct lem_async *a) { struct lfs_twoop *to = (struct lfs_twoop *)a; if (rename(to->old, to->new)) to->ret = errno; else to->ret = 0; } static int lfs_rename(lua_State *T) { const char *old = luaL_checkstring(T, 1); const char *new = luaL_checkstring(T, 2); struct lfs_twoop *to; to = lem_xmalloc(sizeof(struct lfs_twoop)); to->T = T; to->old = old; to->new = new; lem_async_do(&to->a, lfs_rename_work, lfs_twoop_reap); lua_settop(T, 2); return lua_yield(T, 2); } /* * lfs.attributes() and lfs.symlinkattributes() */ struct lfs_attr { struct lem_async a; struct stat st; lua_State *T; const char *path; int op; int ret; }; static void lfs_stat_work(struct lem_async *a) { struct lfs_attr *at = (struct lfs_attr *)a; if (stat(at->path, &at->st)) at->ret = errno; else at->ret = 0; } static void lfs_lstat_work(struct lem_async *a) { struct lfs_attr *at = (struct lfs_attr *)a; if (lstat(at->path, &at->st)) at->ret = errno; else at->ret = 0; } static const char *const lfs_attrs[] = { "dev", "ino", "mode", "permissions", "nlink", "uid", "gid", "rdev", "size", "blksize", "blocks", "access", "modification", "change", "*", NULL }; static void lfs_attr_pushmode(lua_State *T, mode_t mode) { if (S_ISREG(mode)) lua_pushliteral(T, "file"); else if (S_ISDIR(mode)) lua_pushliteral(T, "directory"); else if (S_ISLNK(mode)) lua_pushliteral(T, "link"); else if (S_ISSOCK(mode)) lua_pushliteral(T, "socket"); else if (S_ISFIFO(mode)) lua_pushliteral(T, "named pipe"); else if (S_ISCHR(mode)) lua_pushliteral(T, "char device"); else if (S_ISBLK(mode)) lua_pushliteral(T, "block device"); else lua_pushliteral(T, "other"); } static void lfs_attr_pushperm(lua_State *T, mode_t mode) { static const char sign[3] = { 'r', 'w', 'x' }; char str[9]; mode_t mask = 0400; int i; for (i = 0; i < 9; i++) { if (mode & mask) str[i] = sign[i % 3]; else str[i] = '-'; mask >>= 1; } lua_pushlstring(T, str, 9); } static void lfs_attr_push(lua_State *T, struct stat *st, int i) { switch (i) { case 0: lua_pushnumber(T, st->st_dev); break; case 1: lua_pushnumber(T, st->st_ino); break; case 2: lfs_attr_pushmode(T, st->st_mode); break; case 3: lfs_attr_pushperm(T, st->st_mode); break; case 4: lua_pushnumber(T, st->st_nlink); break; case 5: lua_pushnumber(T, st->st_uid); break; case 6: lua_pushnumber(T, st->st_gid); break; case 7: lua_pushnumber(T, st->st_rdev); break; case 8: lua_pushnumber(T, st->st_size); break; case 9: lua_pushnumber(T, st->st_blksize); break; case 10: lua_pushnumber(T, st->st_blocks); break; case 11: lua_pushnumber(T, st->st_atime); break; case 12: lua_pushnumber(T, st->st_mtime); break; case 13: lua_pushnumber(T, st->st_ctime); break; } } static void lfs_attr_reap(struct lem_async *a) { struct lfs_attr *at = (struct lfs_attr *)a; lua_State *T = at->T; struct stat *st = &at->st; if (at->ret) { lem_queue(T, lfs_strerror(T, at->ret)); free(at); return; } if (at->op == 14) { int i; lua_createtable(T, 0, 14); for (i = 0; i < 14; i++) { lfs_attr_push(T, st, i); lua_setfield(T, -2, lfs_attrs[i]); } } else lfs_attr_push(T, st, at->op); free(at); lem_queue(T, 1); } static int lfs_attr(lua_State *T) { const char *path = luaL_checkstring(T, 1); int op = luaL_checkoption(T, 2, "*", lfs_attrs); struct lfs_attr *at; at = lem_xmalloc(sizeof(struct lfs_attr)); at->T = T; at->path = path; at->op = op; lem_async_do(&at->a, lfs_stat_work, lfs_attr_reap); lua_settop(T, 1); return lua_yield(T, 1); } static int lfs_symattr(lua_State *T) { const char *path = luaL_checkstring(T, 1); struct lfs_attr *at; at = lem_xmalloc(sizeof(struct lfs_attr)); at->T = T; at->path = path; lem_async_do(&at->a, lfs_lstat_work, lfs_attr_reap); lua_settop(T, 1); return lua_yield(T, 1); } /* * lfs.touch() */ struct lfs_touch { struct lem_async a; lua_State *T; union { struct { struct utimbuf utb; struct utimbuf *buf; const char *path; }; int ret; }; }; static void lfs_touch_work(struct lem_async *a) { struct lfs_touch *t = (struct lfs_touch *)a; if (utime(t->path, t->buf)) t->ret = errno; else t->ret = 0; } static void lfs_touch_reap(struct lem_async *a) { struct lfs_touch *t = (struct lfs_touch *)a; lua_State *T = t->T; int ret = t->ret; free(t); if (ret) { lem_queue(T, lfs_strerror(T, ret)); return; } lua_pushboolean(T, 1); lem_queue(T, 1); } static int lfs_touch(lua_State *T) { const char *path = luaL_checkstring(T, 1); struct lfs_touch *t; t = lem_xmalloc(sizeof(struct lfs_touch)); t->T = T; t->path = path; if (lua_gettop(T) == 1) { t->buf = NULL; } else { t->utb.actime = luaL_optnumber(T, 2, 0); t->utb.modtime = luaL_optnumber(T, 3, t->utb.actime); t->buf = &t->utb; } lem_async_do(&t->a, lfs_touch_work, lfs_touch_reap); lua_settop(T, 1); return lua_yield(T, 1); } /* * lfs.currentdir() */ static int lfs_currentdir(lua_State *T) { char stbuf[128]; size_t len = 128; char *buf = NULL; char *path; path = getcwd(stbuf, len); while (path == NULL && errno == ERANGE) { free(buf); len *= 2; buf = lem_xmalloc(len); path = getcwd(buf, len); } if (path == NULL) { free(buf); return lfs_strerror(T, errno); } lua_pushstring(T, path); free(buf); return 1; } /* * dir */ struct lfs_dir { struct lem_async a; lua_State *T; DIR *handle; struct dirent *entry; union { const char *path; int ret; }; }; /* * dir:__gc() */ static int lfs_dir_gc(lua_State *T) { struct lfs_dir *d = lua_touserdata(T, 1); if (d->handle != NULL) (void)closedir(d->handle); return 0; } /* * dir:close() */ static int lfs_dir_close(lua_State *T) { struct lfs_dir *d; int ret; luaL_checktype(T, 1, LUA_TUSERDATA); d = lua_touserdata(T, 1); if (d->handle == NULL) return lfs_closed(T); if (d->T != NULL) return lfs_busy(T); ret = closedir(d->handle); d->handle = NULL; if (ret) return lfs_strerror(T, errno); lua_pushboolean(T, 1); return 1; } /* * dir:next() */ static void lfs_dir_next_work(struct lem_async *a) { struct lfs_dir *d = (struct lfs_dir *)a; errno = 0; d->entry = readdir(d->handle); d->ret = errno; if (d->entry == NULL) { int ret = closedir(d->handle); d->handle = NULL; if (ret) d->ret = errno; } } static void lfs_dir_next_reap(struct lem_async *a) { struct lfs_dir *d = (struct lfs_dir *)a; lua_State *T = d->T; d->T = NULL; if (d->ret) { lem_queue(T, lfs_strerror(T, d->ret)); return; } if (d->entry == NULL) lua_pushnil(T); else lua_pushstring(T, d->entry->d_name); lem_queue(T, 1); } static int lfs_dir_next(lua_State *T) { struct lfs_dir *d; luaL_checktype(T, 1, LUA_TUSERDATA); d = lua_touserdata(T, 1); if (d->handle == NULL) return lfs_closed(T); if (d->T != NULL) return lfs_busy(T); d->T = T; lem_async_do(&d->a, lfs_dir_next_work, lfs_dir_next_reap); lua_settop(T, 1); return lua_yield(T, 1); } /* * lfs.dir() */ static void lfs_dir_work(struct lem_async *a) { struct lfs_dir *d = (struct lfs_dir *)a; d->handle = opendir(d->path); if (d->handle == NULL) d->ret = errno; else d->ret = 0; } static void lfs_dir_reap(struct lem_async *a) { struct lfs_dir *d = (struct lfs_dir *)a; lua_State *T = d->T; d->T = NULL; if (d->ret) lem_queue(T, lfs_strerror(T, d->ret)); else lem_queue(T, 2); } static int lfs_dir(lua_State *T) { const char *path = luaL_checkstring(T, 1); struct lfs_dir *d; lua_settop(T, 1); lua_pushvalue(T, lua_upvalueindex(1)); /* create dir object and set metatable */ d = lua_newuserdata(T, sizeof(struct lfs_dir)); lua_pushvalue(T, lua_upvalueindex(2)); lua_setmetatable(T, -2); d->T = T; d->handle = NULL; d->path = path; lem_async_do(&d->a, lfs_dir_work, lfs_dir_reap); return lua_yield(T, 3); } int luaopen_lem_lfs_core(lua_State *L) { /* create module table */ lua_newtable(L); /* push dir:next() method */ lua_pushcfunction(L, lfs_dir_next); /* create dir object metatable */ lua_newtable(L); /* mt.__index = mt */ lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); /* mt.__gc = */ lua_pushcfunction(L, lfs_dir_gc); lua_setfield(L, -2, "__gc"); /* mt.close = */ lua_pushcfunction(L, lfs_dir_close); lua_setfield(L, -2, "close"); /* mt.next = */ lua_pushvalue(L, -2); /* already on the stack */ lua_setfield(L, -2, "next"); /* insert dir function */ /* upvalue 1: next function */ /* upvalue 2: dir metatable */ lua_pushcclosure(L, lfs_dir, 2); lua_setfield(L, -2, "dir"); /* insert chdir function */ lua_pushcfunction(L, lfs_chdir); lua_setfield(L, -2, "chdir"); /* insert mkdir function */ lua_pushcfunction(L, lfs_mkdir); lua_setfield(L, -2, "mkdir"); /* insert rmdir function */ lua_pushcfunction(L, lfs_rmdir); lua_setfield(L, -2, "rmdir"); /* insert remove function */ lua_pushcfunction(L, lfs_remove); lua_setfield(L, -2, "remove"); /* insert link function */ lua_pushcfunction(L, lfs_link); lua_setfield(L, -2, "link"); /* insert rename function */ lua_pushcfunction(L, lfs_rename); lua_setfield(L, -2, "rename"); /* insert attributes function */ lua_pushcfunction(L, lfs_attr); lua_setfield(L, -2, "attributes"); /* insert attributes function */ lua_pushcfunction(L, lfs_symattr); lua_setfield(L, -2, "symlinkattributes"); /* insert touch function */ lua_pushcfunction(L, lfs_touch); lua_setfield(L, -2, "touch"); /* insert currentdir function */ lua_pushcfunction(L, lfs_currentdir); lua_setfield(L, -2, "currentdir"); return 1; }