/* ** Code implementing read only functionality copied from ** src/lfs.c at commit 2fd989cd6c777583be1c93616018c55b2cbb1bcf: ** ** LuaFileSystem 1.6.2 ** Copyright 2003-2014 Kepler Project ** http://www.keplerproject.org/luafilesystem ** ** File system manipulation library. ** This library offers these functions: ** lfs.attributes (filepath [, attributename]) ** lfs.chdir (path) ** lfs.currentdir () ** lfs.dir (path) ** ** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $ */ #include <dirent.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "lua.h" #include "lauxlib.h" #include "lualib.h" #define chdir_error strerror(errno) /* Size of path buffer string, stolen from pwd.c */ #ifndef PATH_MAX # ifdef NAME_MAX # define PATH_MAX NAME_MAX # elif FILENAME_MAX # define PATH_MAX FILENAME_MAX # else # define PATH_MAX 256 # endif /* NAME_MAX */ #endif /* PATH_MAX */ #define DIR_METATABLE "directory metatable" typedef struct dir_data { int closed; DIR *dir; } dir_data; #define STAT_STRUCT struct stat #define STAT_FUNC stat_via_fstat /* Emulate stat via fstat */ int stat_via_fstat (const char *path, struct stat *buf) { int fd = open (path, O_RDONLY); if (fd == -1) { DIR *dir = opendir (path); if (!dir) return -1; closedir (dir); buf->st_mode=S_IFDIR; buf->st_size=0; return 0; } if (fstat (fd, buf) == -1) { int err = errno; close (fd); errno = err; return -1; } close (fd); return 0; } /* ** This function changes the working (current) directory */ static int change_dir (lua_State *L) { const char *path = luaL_checkstring(L, 1); if (chdir(path)) { lua_pushnil (L); lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", path, chdir_error); return 2; } else { lua_pushboolean (L, 1); return 1; } } /* ** This function returns the current directory ** If unable to get the current directory, it returns nil ** and a string describing the error */ static int get_dir (lua_State *L) { char *path; /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ char buf[PATH_MAX]; if ((path = getcwd(buf, PATH_MAX)) == NULL) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } else { lua_pushstring(L, path); return 1; } } /* ** Directory iterator */ static int dir_iter (lua_State *L) { struct dirent *entry; dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); luaL_argcheck (L, d->closed == 0, 1, "closed directory"); if ((entry = readdir (d->dir)) != NULL) { lua_pushstring (L, entry->d_name); return 1; } else { /* no more entries => close directory */ closedir (d->dir); d->closed = 1; return 0; } } /* ** Closes directory iterators */ static int dir_close (lua_State *L) { dir_data *d = (dir_data *)lua_touserdata (L, 1); if (!d->closed && d->dir) { closedir (d->dir); } d->closed = 1; return 0; } /* ** Factory of directory iterators */ static int dir_iter_factory (lua_State *L) { const char *path = luaL_checkstring (L, 1); dir_data *d; lua_pushcfunction (L, dir_iter); d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); luaL_getmetatable (L, DIR_METATABLE); lua_setmetatable (L, -2); d->closed = 0; d->dir = opendir (path); if (d->dir == NULL) luaL_error (L, "cannot open %s: %s", path, strerror (errno)); return 2; } /* ** Creates directory metatable. */ static int dir_create_meta (lua_State *L) { luaL_newmetatable (L, DIR_METATABLE); /* Method table */ lua_newtable(L); lua_pushcfunction (L, dir_iter); lua_setfield(L, -2, "next"); lua_pushcfunction (L, dir_close); lua_setfield(L, -2, "close"); /* Metamethods */ lua_setfield(L, -2, "__index"); lua_pushcfunction (L, dir_close); lua_setfield (L, -2, "__gc"); return 1; } /* ** Convert the inode protection mode to a string. */ static const char *mode2string (mode_t mode) { if ( S_ISREG(mode) ) return "file"; else if ( S_ISDIR(mode) ) return "directory"; else if ( S_ISLNK(mode) ) return "link"; else if ( S_ISSOCK(mode) ) return "socket"; else if ( S_ISFIFO(mode) ) return "named pipe"; else if ( S_ISCHR(mode) ) return "char device"; else if ( S_ISBLK(mode) ) return "block device"; else return "other"; } /* inode protection mode */ static void push_st_mode (lua_State *L, STAT_STRUCT *info) { lua_pushstring (L, mode2string (info->st_mode)); } /* file size, in bytes */ static void push_st_size (lua_State *L, STAT_STRUCT *info) { lua_pushnumber (L, (lua_Number)info->st_size); } static void push_invalid (lua_State *L, STAT_STRUCT *info) { luaL_error(L, "invalid attribute name"); info->st_size = 0; /* never reached */ } typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); struct _stat_members { const char *name; _push_function push; }; struct _stat_members members[] = { { "mode", push_st_mode }, { "size", push_st_size }, { NULL, push_invalid } }; /* ** Get file or symbolic link information */ static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { int i; STAT_STRUCT info; const char *file = luaL_checkstring (L, 1); if (st(file, &info)) { lua_pushnil (L); lua_pushfstring (L, "cannot obtain information from file `%s'", file); return 2; } if (lua_isstring (L, 2)) { int v; const char *member = lua_tostring (L, 2); if (strcmp (member, "mode") == 0) v = 0; #ifndef _WIN32 else if (strcmp (member, "blocks") == 0) v = 11; else if (strcmp (member, "blksize") == 0) v = 12; #endif else /* look for member */ for (v = 1; members[v].name; v++) if (*members[v].name == *member) break; /* push member value and return */ members[v].push (L, &info); return 1; } else if (!lua_istable (L, 2)) /* creates a table if none is given */ lua_newtable (L); /* stores all members in table on top of the stack */ for (i = 0; members[i].name; i++) { lua_pushstring (L, members[i].name); members[i].push (L, &info); lua_rawset (L, -3); } return 1; } /* ** Get file information using stat. */ static int file_info (lua_State *L) { return _file_info_ (L, STAT_FUNC); } static const struct luaL_Reg fslib[] = { {"attributes", file_info}, {"chdir", change_dir}, {"currentdir", get_dir}, {"dir", dir_iter_factory}, {NULL, NULL}, }; LUALIB_API int luaopen_lfs (lua_State *L) { dir_create_meta (L); luaL_newlib (L, fslib); return 1; }