#include <sys/cdefs.h>
#ifndef _STANDALONE
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#endif
#include <lua.h>
#include "lauxlib.h"
#include "lfs.h"
#ifdef _STANDALONE
#include "lstd.h"
#include "lutils.h"
#endif
#include "bootstrap.h"
#ifndef nitems
#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
#endif
#define DIR_METATABLE "directory iterator metatable"
static int
lua_dir_iter_pushtype(lua_State *L __unused, const struct dirent *ent __unused)
{
#ifdef _STANDALONE
lua_pushinteger(L, ent->d_type);
return 1;
#else
return 0;
#endif
}
static int
lua_dir_iter_next(lua_State *L)
{
struct dirent *entry;
DIR *dp, **dpp;
dpp = (DIR **)luaL_checkudata(L, 1, DIR_METATABLE);
dp = *dpp;
luaL_argcheck(L, dp != NULL, 1, "closed directory");
#ifdef _STANDALONE
entry = readdirfd(dp->fd);
#else
entry = readdir(dp);
#endif
if (entry == NULL) {
closedir(dp);
*dpp = NULL;
return 0;
}
lua_pushstring(L, entry->d_name);
return 1 + lua_dir_iter_pushtype(L, entry);
}
static int
lua_dir_iter_close(lua_State *L)
{
DIR *dp, **dpp;
dpp = (DIR **)lua_touserdata(L, 1);
dp = *dpp;
if (dp == NULL)
return 0;
closedir(dp);
*dpp = NULL;
return 0;
}
static int
lua_dir(lua_State *L)
{
const char *path;
DIR *dp;
if (lua_gettop(L) != 1) {
lua_pushnil(L);
return 1;
}
path = luaL_checkstring(L, 1);
dp = opendir(path);
if (dp == NULL) {
lua_pushnil(L);
return 1;
}
lua_pushcfunction(L, lua_dir_iter_next);
*(DIR **)lua_newuserdata(L, sizeof(DIR **)) = dp;
luaL_getmetatable(L, DIR_METATABLE);
lua_setmetatable(L, -2);
return 2;
}
static void
register_metatable(lua_State *L)
{
luaL_newmetatable(L, DIR_METATABLE);
lua_newtable(L);
lua_pushcfunction(L, lua_dir_iter_next);
lua_setfield(L, -2, "next");
lua_pushcfunction(L, lua_dir_iter_close);
lua_setfield(L, -2, "close");
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lua_dir_iter_close);
lua_setfield(L, -2, "__gc");
lua_pop(L, 1);
}
#define PUSH_INTEGER(lname, stname) \
static void \
push_st_ ## lname (lua_State *L, struct stat *sb) \
{ \
lua_pushinteger(L, (lua_Integer)sb->st_ ## stname); \
}
PUSH_INTEGER(dev, dev)
PUSH_INTEGER(ino, ino)
PUSH_INTEGER(nlink, nlink)
PUSH_INTEGER(uid, uid)
PUSH_INTEGER(gid, gid)
PUSH_INTEGER(rdev, rdev)
PUSH_INTEGER(access, atime)
PUSH_INTEGER(modification, mtime)
PUSH_INTEGER(change, ctime)
PUSH_INTEGER(size, size)
#undef PUSH_INTEGER
static void
push_st_mode(lua_State *L, struct stat *sb)
{
const char *mode_s;
mode_t mode;
mode = (sb->st_mode & S_IFMT);
if (S_ISREG(mode))
mode_s = "file";
else if (S_ISDIR(mode))
mode_s = "directory";
else if (S_ISLNK(mode))
mode_s = "link";
else if (S_ISSOCK(mode))
mode_s = "socket";
else if (S_ISFIFO(mode))
mode_s = "fifo";
else if (S_ISCHR(mode))
mode_s = "char device";
else if (S_ISBLK(mode))
mode_s = "block device";
else
mode_s = "other";
lua_pushstring(L, mode_s);
}
static void
push_st_permissions(lua_State *L, struct stat *sb)
{
char buf[20];
snprintf(buf, sizeof(buf), "%o", sb->st_mode & ~S_IFMT);
lua_pushstring(L, buf);
}
#define PUSH_ENTRY(n) { #n, push_st_ ## n }
struct stat_members {
const char *name;
void (*push)(lua_State *, struct stat *);
} members[] = {
PUSH_ENTRY(mode),
PUSH_ENTRY(dev),
PUSH_ENTRY(ino),
PUSH_ENTRY(nlink),
PUSH_ENTRY(uid),
PUSH_ENTRY(gid),
PUSH_ENTRY(rdev),
PUSH_ENTRY(access),
PUSH_ENTRY(modification),
PUSH_ENTRY(change),
PUSH_ENTRY(size),
PUSH_ENTRY(permissions),
};
#undef PUSH_ENTRY
static int
lua_attributes(lua_State *L)
{
struct stat sb;
const char *path, *member;
size_t i;
int rc;
path = luaL_checkstring(L, 1);
if (path == NULL) {
lua_pushnil(L);
lua_pushfstring(L, "cannot convert first argument to string");
lua_pushinteger(L, EINVAL);
return 3;
}
rc = stat(path, &sb);
if (rc != 0) {
lua_pushnil(L);
lua_pushfstring(L,
"cannot obtain information from file '%s': %s", path,
strerror(errno));
lua_pushinteger(L, errno);
return 3;
}
if (lua_isstring(L, 2)) {
member = lua_tostring(L, 2);
for (i = 0; i < nitems(members); i++) {
if (strcmp(members[i].name, member) != 0)
continue;
members[i].push(L, &sb);
return 1;
}
return luaL_error(L, "invalid attribute name '%s'", member);
}
lua_settop(L, 2);
if (!lua_istable(L, 2))
lua_newtable(L);
for (i = 0; i < nitems(members); i++) {
lua_pushstring(L, members[i].name);
members[i].push(L, &sb);
lua_rawset(L, -3);
}
return 1;
}
#ifndef _STANDALONE
#define lfs_mkdir_impl(path) (mkdir((path), \
S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | \
S_IROTH | S_IXOTH))
static int
lua_mkdir(lua_State *L)
{
const char *path;
int error, serrno;
path = luaL_checkstring(L, 1);
if (path == NULL) {
lua_pushnil(L);
lua_pushfstring(L, "cannot convert first argument to string");
lua_pushinteger(L, EINVAL);
return 3;
}
error = lfs_mkdir_impl(path);
if (error == -1) {
serrno = errno;
lua_pushnil(L);
lua_pushfstring(L, strerror(serrno));
lua_pushinteger(L, serrno);
return 3;
}
lua_pushboolean(L, 1);
return 1;
}
static int
lua_rmdir(lua_State *L)
{
const char *path;
int error, serrno;
path = luaL_checkstring(L, 1);
if (path == NULL) {
lua_pushnil(L);
lua_pushfstring(L, "cannot convert first argument to string");
lua_pushinteger(L, EINVAL);
return 3;
}
error = rmdir(path);
if (error == -1) {
serrno = errno;
lua_pushnil(L);
lua_pushfstring(L, strerror(serrno));
lua_pushinteger(L, serrno);
return 3;
}
lua_pushboolean(L, 1);
return 1;
}
#endif
#define REG_SIMPLE(n) { #n, lua_ ## n }
static const struct luaL_Reg fslib[] = {
REG_SIMPLE(attributes),
REG_SIMPLE(dir),
#ifndef _STANDALONE
REG_SIMPLE(mkdir),
REG_SIMPLE(rmdir),
#endif
{ NULL, NULL },
};
#undef REG_SIMPLE
int
luaopen_lfs(lua_State *L)
{
register_metatable(L);
luaL_newlib(L, fslib);
#ifdef _STANDALONE
lua_pushinteger(L, DT_DIR);
lua_setfield(L, -2, "DT_DIR");
lua_pushinteger(L, DT_REG);
lua_setfield(L, -2, "DT_REG");
lua_pushinteger(L, DT_LNK);
lua_setfield(L, -2, "DT_LNK");
#endif
return 1;
}
#ifndef _STANDALONE
FLUA_MODULE(lfs);
#endif