summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmil Renner Berthing <esmil@mailme.dk>2012-12-16 23:27:27 +0100
committerEmil Renner Berthing <esmil@mailme.dk>2012-12-18 17:35:32 +0100
commit86ccbbee7d038bcd9dfa2eb56551a383d84b908a (patch)
tree86c8124fe6af400e5e386186a5146a26cad07348
parent089b048e2e0f872a653b7b353f66f300578377aa (diff)
downloadlem-86ccbbee7d038bcd9dfa2eb56551a383d84b908a.tar.gz
lem-86ccbbee7d038bcd9dfa2eb56551a383d84b908a.tar.xz
lem-86ccbbee7d038bcd9dfa2eb56551a383d84b908a.zip
lfs: added
-rw-r--r--Makefile.in2
-rw-r--r--lem/lfs.lua54
-rw-r--r--lem/lfs/core.c777
-rwxr-xr-xtest/lfs.lua97
4 files changed, 930 insertions, 0 deletions
diff --git a/Makefile.in b/Makefile.in
index 77b80aa..a9e7901 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -27,12 +27,14 @@ llibs = \
lem/repl.lua \
lem/io.lua \
lem/io/queue.lua \
+ lem/lfs.lua \
lem/http.lua \
lem/hathaway.lua
clibs = \
lem/utils.so \
lem/io/core.so \
+ lem/lfs/core.so \
lem/http/core.so
ifdef V
diff --git a/lem/lfs.lua b/lem/lfs.lua
new file mode 100644
index 0000000..ea4e317
--- /dev/null
+++ b/lem/lfs.lua
@@ -0,0 +1,54 @@
+--
+-- This file is part of LEM, a Lua Event Machine.
+-- Copyright 2011-2012 Emil Renner Berthing
+--
+-- LEM is free software: you can redistribute it and/or
+-- modify it under the terms of the GNU 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 General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with LEM. If not, see <http://www.gnu.org/licenses/>.
+--
+
+local lfs = require 'lem.lfs.core'
+
+function lfs.lock(file, ...)
+ return file:lock(...)
+end
+
+function lfs.unlock(file, ...)
+ return file:lock('u', ...)
+end
+
+function lfs.setmode()
+ return 'binary'
+end
+
+do
+ local setmetatable, remove, link = setmetatable, lfs.remove, lfs.link
+
+ local Lock = { __index = true, free = true }
+ Lock.__index = Lock
+
+ function Lock:free()
+ return remove(self.filename)
+ end
+
+ function lfs.lock_dir(path)
+ local filename = path .. '/lockfile.lfs'
+ local ok, err = link('lock', filename, true)
+ if not ok then return ok, err end
+
+ return setmetatable({ filename = filename }, Lock)
+ end
+end
+
+return lfs
+
+-- vim: ts=2 sw=2 noet:
diff --git a/lem/lfs/core.c b/lem/lfs/core.c
new file mode 100644
index 0000000..6a95927
--- /dev/null
+++ b/lem/lfs/core.c
@@ -0,0 +1,777 @@
+/*
+ * This file is part of LEM, a Lua Event Machine.
+ * Copyright 2011-2012 Emil Renner Berthing
+ *
+ * LEM is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LEM. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <utime.h>
+#include <assert.h>
+
+#include <lem.h>
+
+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;
+ 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->a.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->path = path;
+ lem_async_do(&po->a, T, 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->path = path;
+ lem_async_do(&po->a, T, 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->path = path;
+ lem_async_do(&po->a, T, 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->path = path;
+ lem_async_do(&po->a, T, 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;
+ 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->a.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->old = old;
+ to->new = new;
+ lem_async_do(&to->a, T, 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->old = old;
+ to->new = new;
+ lem_async_do(&to->a, T, 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;
+ 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->a.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->path = path;
+ at->op = op;
+ lem_async_do(&at->a, T, 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->path = path;
+ lem_async_do(&at->a, T, lfs_lstat_work, lfs_attr_reap);
+
+ lua_settop(T, 1);
+ return lua_yield(T, 1);
+}
+
+/*
+ * lfs.touch()
+ */
+struct lfs_touch {
+ struct lem_async a;
+ 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->a.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->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, T, 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;
+ 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->a.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->a.T;
+
+ d->a.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->a.T != NULL)
+ return lfs_busy(T);
+
+ lem_async_do(&d->a, T, 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->a.T;
+
+ d->a.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->handle = NULL;
+ d->path = path;
+ lem_async_do(&d->a, T, 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 = <lfs_dir_gc> */
+ lua_pushcfunction(L, lfs_dir_gc);
+ lua_setfield(L, -2, "__gc");
+ /* mt.close = <lfs_dir_close> */
+ lua_pushcfunction(L, lfs_dir_close);
+ lua_setfield(L, -2, "close");
+ /* mt.next = <lfs_dir_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;
+}
diff --git a/test/lfs.lua b/test/lfs.lua
new file mode 100755
index 0000000..0f084fa
--- /dev/null
+++ b/test/lfs.lua
@@ -0,0 +1,97 @@
+#!bin/lem
+--
+-- This file is part of LEM, a Lua Event Machine.
+-- Copyright 2011-2012 Emil Renner Berthing
+--
+-- LEM is free software: you can redistribute it and/or
+-- modify it under the terms of the GNU 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 General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with LEM. If not, see <http://www.gnu.org/licenses/>.
+--
+
+local utils = require 'lem.utils'
+local io = require 'lem.io'
+local lfs = require 'lem.lfs'
+
+local testdir = 'testdir'
+local testfile = 'testfile'
+
+local spawnticker, stopticker
+do
+ local write, yield = io.write, utils.yield
+ local stop = true
+
+ local function ticker()
+ stop = false
+ repeat
+ write('.')
+ yield()
+ until stop
+ end
+
+ function spawnticker()
+ utils.spawn(ticker)
+ end
+
+ function stopticker()
+ stop = true
+ end
+end
+
+local sleeper = utils.newsleeper()
+local attr
+
+io.write('Current directory: '.. lfs.currentdir() ..'\n')
+
+spawnticker()
+
+for name in lfs.dir('.') do
+ io.write('\n"' .. name .. '"\n')
+end
+
+io.write('\nCreating testdir\n')
+assert(lfs.mkdir(testdir))
+io.write('\nLocking directory\n')
+local lock = assert(lfs.lock_dir(testdir))
+io.write('\nGetting attributes\n')
+attr = assert(lfs.attributes(testdir))
+io.write('\nAttributes:\n')
+for k, v in pairs(attr) do
+ print(k, v)
+end
+io.write('\nUnlocking testdir\n')
+assert(lock:free())
+io.write('\nRemoving testdir\n')
+assert(lfs.rmdir(testdir))
+io.write('\nCreating testfile\n')
+local file = assert(io.open(testfile, 'w'))
+io.write('\nLocking testfile\n')
+assert(lfs.lock(file, 'w'))
+io.write('\nGetting change time\n')
+attr = assert(lfs.attributes(testfile, 'change'))
+io.write('\nChange time: ' .. attr .. '\n')
+stopticker()
+io.write('\nSleeping two seconds..\n')
+sleeper:sleep(2)
+spawnticker()
+io.write('\nTouching testfile\n')
+assert(lfs.touch(testfile))
+io.write('\nGetting change time\n')
+attr = assert(lfs.attributes(testfile, 'change'))
+io.write('\nChange time: ' .. attr .. '\n')
+io.write('\nRenaming testfile\n')
+assert(lfs.rename(testfile, testfile..'2'))
+io.write('\nRemoving testfile\n')
+assert(lfs.remove(testfile..'2'))
+io.write('\nDone\n')
+stopticker()
+
+-- vim: syntax=lua ts=2 sw=2 noet: