summaryrefslogblamecommitdiffstats
path: root/lem/lfs/core.c
blob: 087a27c7c04c58413e9c5825f9d145546ea825e2 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                 
                                      
  

                                                                 


                                                                 

                                                             
                                                                
                                                      
  

                                                                       







































                                         
                     









                                                       
                             
































                                                       
                  
                        
                                                              


























                                                                        
                  
                        
                                                              

























                                                       
                  
                        
                                                              

























                                                       
                  
                        
                                                               









                               
                     












                                                     
                             










































                                                     
                  

                      
                                                                        
























                                                     
                  

                      
                                                              










                                               
                     























































































                                                      

                                                       

                                                          









                                                           






                                                   
                             






























                                                          
                  

                        
                                                           











                                                  
                  
                        
                                                            









                               
                     
























                                                    
                            


















                                                   
                 







                                                                     
                                                            






































                                                 
                     


































                                                 
                         


































                                                
                            
 
                    




















                                                      
                         

                                   

                                                                  























                                                
                            
 
                    



















                                                       
                 

                         
                                                        






































































                                                        
/*
 * 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 <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;
	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_pushinteger(T, st->st_dev); break;
	case 1:  lua_pushinteger(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_pushinteger(T, st->st_nlink); break;
	case 5:  lua_pushinteger(T, st->st_uid); break;
	case 6:  lua_pushinteger(T, st->st_gid); break;
	case 7:  lua_pushinteger(T, st->st_rdev); break;
	case 8:  lua_pushinteger(T, st->st_size); break;
	case 9:  lua_pushinteger(T, st->st_blksize); break;
	case 10: lua_pushinteger(T, st->st_blocks); break;
	case 11: lua_pushinteger(T, st->st_atime); break;
	case 12: lua_pushinteger(T, st->st_mtime); break;
	case 13: lua_pushinteger(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 = <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;
}