luafilesystem 1.6.0

This commit is contained in:
Fabio Mascarenhas
2012-10-01 11:11:48 -03:00
parent 4a299c53b6
commit d71c63cdb7
5 changed files with 427 additions and 348 deletions

6
README
View File

@@ -22,6 +22,12 @@ Please check the documentation at doc/us/ for more information.
History History
------- -------
Version 1.6.0 [26/Sep/2012]
* getcwd fix for Android
* support for Lua 5.2
* add lfs.link
* other bug fixes
Version 1.5.0 [20/Oct/2009] Version 1.5.0 [20/Oct/2009]
* added explicit next and close methods to second return value of lfs.dir (the directory object), for explicit iteration or explicit closing. * added explicit next and close methods to second return value of lfs.dir (the directory object), for explicit iteration or explicit closing.
* added directory locking via lfs.lock_dir function (see the manual). * added directory locking via lfs.lock_dir function (see the manual).

View File

@@ -71,7 +71,8 @@ the underlying directory structure and file attributes.</p>
<h2><a name="status"></a>Status</h2> <h2><a name="status"></a>Status</h2>
<p>Current version is 1.5.0. It was developed for Lua 5.1.</p> <p>Current version is 1.6.0. It was developed for Lua 5.1 but also
works with Lua 5.2.</p>
<h2><a name="download"></a>Download</h2> <h2><a name="download"></a>Download</h2>
@@ -82,6 +83,12 @@ page.</p>
<h2><a name="history"></a>History</h2> <h2><a name="history"></a>History</h2>
<dl class="history"> <dl class="history">
<dt><strong>Version 1.6.0</strong> [26/Sep/2012]</dt>
<li>getcwd fix for Android</li>
<li>support for Lua 5.2</li>
<li>add lfs.link</li>
<li>other bug fixes</li>
<dt><strong>Version 1.5.0</strong> [20/Oct/2009]</dt> <dt><strong>Version 1.5.0</strong> [20/Oct/2009]</dt>
<li>Added explicit next and close methods to second return value of lfs.dir <li>Added explicit next and close methods to second return value of lfs.dir
(the directory object), for explicit iteration or explicit closing.</li> (the directory object), for explicit iteration or explicit closing.</li>

View File

@@ -0,0 +1,27 @@
package = "LuaFileSystem"
version = "1.6.0-1"
source = {
url = "https://github.com/downloads/keplerproject/luafilesystem/luafilesystem-1.6.0.tar.gz",
}
description = {
summary = "File System Library for the Lua Programming Language",
detailed = [[
LuaFileSystem is a Lua library developed to complement the set of
functions related to file systems offered by the standard Lua
distribution. LuaFileSystem offers a portable way to access the
underlying directory structure and file attributes.
]]
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "builtin",
modules = { lfs = "src/lfs.c" },
copy_directories = { "doc", "tests" }
}

642
src/lfs.c
View File

@@ -67,21 +67,21 @@
#if (LUA_VERSION_NUM == 502) #if (LUA_VERSION_NUM == 502)
#undef luaL_register #undef luaL_register
#define luaL_register(L,n,f) \ #define luaL_register(L,n,f) \
{ if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); } { if ((n) == NULL) luaL_setfuncs(L,f,0); else luaL_newlib(L,f); }
#endif #endif
/* Define 'strerror' for systems that do not implement it */ /* Define 'strerror' for systems that do not implement it */
#ifdef NO_STRERROR #ifdef NO_STRERROR
#define strerror(_) "System unable to describe the error" #define strerror(_) "System unable to describe the error"
#endif #endif
/* Define 'getcwd' for systems that do not implement it */ /* Define 'getcwd' for systems that do not implement it */
#ifdef NO_GETCWD #ifdef NO_GETCWD
#define getcwd(p,s) NULL #define getcwd(p,s) NULL
#define getcwd_error "Function 'getcwd' not provided by system" #define getcwd_error "Function 'getcwd' not provided by system"
#else #else
#define getcwd_error strerror(errno) #define getcwd_error strerror(errno)
#ifdef _WIN32 #ifdef _WIN32
#define LFS_MAXPATHLEN MAX_PATH // MAX_PATH seems to be 260. Seems kind of small. Is there a better one? #define LFS_MAXPATHLEN MAX_PATH // MAX_PATH seems to be 260. Seems kind of small. Is there a better one?
#else #else
@@ -92,12 +92,12 @@
#define DIR_METATABLE "directory metatable" #define DIR_METATABLE "directory metatable"
typedef struct dir_data { typedef struct dir_data {
int closed; int closed;
#ifdef _WIN32 #ifdef _WIN32
long hFile; long hFile;
char pattern[MAX_PATH+1]; char pattern[MAX_PATH+1];
#else #else
DIR *dir; DIR *dir;
#endif #endif
} dir_data; } dir_data;
@@ -127,21 +127,21 @@ typedef struct dir_data {
*/ */
static int pusherror(lua_State *L, const char *info) static int pusherror(lua_State *L, const char *info)
{ {
lua_pushnil(L); lua_pushnil(L);
if (info==NULL) if (info==NULL)
lua_pushstring(L, strerror(errno)); lua_pushstring(L, strerror(errno));
else else
lua_pushfstring(L, "%s: %s", info, strerror(errno)); lua_pushfstring(L, "%s: %s", info, strerror(errno));
lua_pushinteger(L, errno); lua_pushinteger(L, errno);
return 3; return 3;
} }
static int pushresult(lua_State *L, int i, const char *info) static int pushresult(lua_State *L, int i, const char *info)
{ {
if (i==-1) if (i==-1)
return pusherror(L, info); return pusherror(L, info);
lua_pushinteger(L, i); lua_pushinteger(L, i);
return 1; return 1;
} }
@@ -149,16 +149,16 @@ static int pushresult(lua_State *L, int i, const char *info)
** This function changes the working (current) directory ** This function changes the working (current) directory
*/ */
static int change_dir (lua_State *L) { static int change_dir (lua_State *L) {
const char *path = luaL_checkstring(L, 1); const char *path = luaL_checkstring(L, 1);
if (chdir(path)) { if (chdir(path)) {
lua_pushnil (L); lua_pushnil (L);
lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n",
path, chdir_error); path, chdir_error);
return 2; return 2;
} else { } else {
lua_pushboolean (L, 1); lua_pushboolean (L, 1);
return 1; return 1;
} }
} }
/* /*
@@ -185,15 +185,15 @@ static int get_dir (lua_State *L) {
** Check if the given element on the stack is a file and returns it. ** Check if the given element on the stack is a file and returns it.
*/ */
static FILE *check_file (lua_State *L, int idx, const char *funcname) { static FILE *check_file (lua_State *L, int idx, const char *funcname) {
FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*");
if (fh == NULL) { if (fh == NULL) {
luaL_error (L, "%s: not a file", funcname); luaL_error (L, "%s: not a file", funcname);
return 0; return 0;
} else if (*fh == NULL) { } else if (*fh == NULL) {
luaL_error (L, "%s: closed file", funcname); luaL_error (L, "%s: closed file", funcname);
return 0; return 0;
} else } else
return *fh; return *fh;
} }
@@ -201,50 +201,50 @@ static FILE *check_file (lua_State *L, int idx, const char *funcname) {
** **
*/ */
static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) {
int code; int code;
#ifdef _WIN32 #ifdef _WIN32
/* lkmode valid values are: /* lkmode valid values are:
LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error.
LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error.
LK_NBRLCK Same as _LK_NBLCK. LK_NBRLCK Same as _LK_NBLCK.
LK_RLCK Same as _LK_LOCK. LK_RLCK Same as _LK_LOCK.
LK_UNLCK Unlocks the specified bytes, which must have been previously locked. LK_UNLCK Unlocks the specified bytes, which must have been previously locked.
Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. Regions should be locked only briefly and should be unlocked before closing a file or exiting the program.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp
*/ */
int lkmode; int lkmode;
switch (*mode) { switch (*mode) {
case 'r': lkmode = LK_NBLCK; break; case 'r': lkmode = LK_NBLCK; break;
case 'w': lkmode = LK_NBLCK; break; case 'w': lkmode = LK_NBLCK; break;
case 'u': lkmode = LK_UNLCK; break; case 'u': lkmode = LK_UNLCK; break;
default : return luaL_error (L, "%s: invalid mode", funcname); default : return luaL_error (L, "%s: invalid mode", funcname);
} }
if (!len) { if (!len) {
fseek (fh, 0L, SEEK_END); fseek (fh, 0L, SEEK_END);
len = ftell (fh); len = ftell (fh);
} }
fseek (fh, start, SEEK_SET); fseek (fh, start, SEEK_SET);
#ifdef __BORLANDC__ #ifdef __BORLANDC__
code = locking (fileno(fh), lkmode, len); code = locking (fileno(fh), lkmode, len);
#else #else
code = _locking (fileno(fh), lkmode, len); code = _locking (fileno(fh), lkmode, len);
#endif #endif
#else #else
struct flock f; struct flock f;
switch (*mode) { switch (*mode) {
case 'w': f.l_type = F_WRLCK; break; case 'w': f.l_type = F_WRLCK; break;
case 'r': f.l_type = F_RDLCK; break; case 'r': f.l_type = F_RDLCK; break;
case 'u': f.l_type = F_UNLCK; break; case 'u': f.l_type = F_UNLCK; break;
default : return luaL_error (L, "%s: invalid mode", funcname); default : return luaL_error (L, "%s: invalid mode", funcname);
} }
f.l_whence = SEEK_SET; f.l_whence = SEEK_SET;
f.l_start = (off_t)start; f.l_start = (off_t)start;
f.l_len = (off_t)len; f.l_len = (off_t)len;
code = fcntl (fileno(fh), F_SETLK, &f); code = fcntl (fileno(fh), F_SETLK, &f);
#endif #endif
return (code != -1); return (code != -1);
} }
#ifdef _WIN32 #ifdef _WIN32
@@ -263,14 +263,14 @@ static int lfs_lock_dir(lua_State *L) {
} }
strcpy(ln, path); strcat(ln, lockfile); strcpy(ln, path); strcat(ln, lockfile);
if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) {
int en = GetLastError(); int en = GetLastError();
free(ln); lua_pushnil(L); free(ln); lua_pushnil(L);
if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION)
lua_pushstring(L, "File exists"); lua_pushstring(L, "File exists");
else else
lua_pushstring(L, strerror(en)); lua_pushstring(L, strerror(en));
return 2; return 2;
} }
free(ln); free(ln);
lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));
@@ -358,18 +358,18 @@ static int lfs_f_setmode(lua_State *L) {
** @param #4 Number with length (optional). ** @param #4 Number with length (optional).
*/ */
static int file_lock (lua_State *L) { static int file_lock (lua_State *L) {
FILE *fh = check_file (L, 1, "lock"); FILE *fh = check_file (L, 1, "lock");
const char *mode = luaL_checkstring (L, 2); const char *mode = luaL_checkstring (L, 2);
const long start = luaL_optlong (L, 3, 0); const long start = luaL_optlong (L, 3, 0);
long len = luaL_optlong (L, 4, 0); long len = luaL_optlong (L, 4, 0);
if (_file_lock (L, fh, mode, start, len, "lock")) { if (_file_lock (L, fh, mode, start, len, "lock")) {
lua_pushboolean (L, 1); lua_pushboolean (L, 1);
return 1; return 1;
} else { } else {
lua_pushnil (L); lua_pushnil (L);
lua_pushfstring (L, "%s", strerror(errno)); lua_pushfstring (L, "%s", strerror(errno));
return 2; return 2;
} }
} }
@@ -380,17 +380,17 @@ static int file_lock (lua_State *L) {
** @param #3 Number with length (optional). ** @param #3 Number with length (optional).
*/ */
static int file_unlock (lua_State *L) { static int file_unlock (lua_State *L) {
FILE *fh = check_file (L, 1, "unlock"); FILE *fh = check_file (L, 1, "unlock");
const long start = luaL_optlong (L, 2, 0); const long start = luaL_optlong (L, 2, 0);
long len = luaL_optlong (L, 3, 0); long len = luaL_optlong (L, 3, 0);
if (_file_lock (L, fh, "u", start, len, "unlock")) { if (_file_lock (L, fh, "u", start, len, "unlock")) {
lua_pushboolean (L, 1); lua_pushboolean (L, 1);
return 1; return 1;
} else { } else {
lua_pushnil (L); lua_pushnil (L);
lua_pushfstring (L, "%s", strerror(errno)); lua_pushfstring (L, "%s", strerror(errno));
return 2; return 2;
} }
} }
@@ -403,10 +403,10 @@ static int file_unlock (lua_State *L) {
static int make_link(lua_State *L) static int make_link(lua_State *L)
{ {
#ifndef _WIN32 #ifndef _WIN32
const char *oldpath = luaL_checkstring(L, 1); const char *oldpath = luaL_checkstring(L, 1);
const char *newpath = luaL_checkstring(L, 2); const char *newpath = luaL_checkstring(L, 2);
return pushresult(L, return pushresult(L,
(lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL); (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL);
#else #else
pusherror(L, "make_link is not supported on Windows"); pusherror(L, "make_link is not supported on Windows");
#endif #endif
@@ -418,21 +418,21 @@ static int make_link(lua_State *L)
** @param #1 Directory path. ** @param #1 Directory path.
*/ */
static int make_dir (lua_State *L) { static int make_dir (lua_State *L) {
const char *path = luaL_checkstring (L, 1); const char *path = luaL_checkstring (L, 1);
int fail; int fail;
#ifdef _WIN32 #ifdef _WIN32
fail = _mkdir (path); fail = _mkdir (path);
#else #else
fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH ); S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH );
#endif #endif
if (fail) { if (fail) {
lua_pushnil (L); lua_pushnil (L);
lua_pushfstring (L, "%s", strerror(errno)); lua_pushfstring (L, "%s", strerror(errno));
return 2; return 2;
} }
lua_pushboolean (L, 1); lua_pushboolean (L, 1);
return 1; return 1;
} }
/* /*
@@ -440,18 +440,18 @@ static int make_dir (lua_State *L) {
** @param #1 Directory path. ** @param #1 Directory path.
*/ */
static int remove_dir (lua_State *L) { static int remove_dir (lua_State *L) {
const char *path = luaL_checkstring (L, 1); const char *path = luaL_checkstring (L, 1);
int fail; int fail;
fail = rmdir (path); fail = rmdir (path);
if (fail) { if (fail) {
lua_pushnil (L); lua_pushnil (L);
lua_pushfstring (L, "%s", strerror(errno)); lua_pushfstring (L, "%s", strerror(errno));
return 2; return 2;
} }
lua_pushboolean (L, 1); lua_pushboolean (L, 1);
return 1; return 1;
} }
/* /*
@@ -459,44 +459,44 @@ static int remove_dir (lua_State *L) {
*/ */
static int dir_iter (lua_State *L) { static int dir_iter (lua_State *L) {
#ifdef _WIN32 #ifdef _WIN32
struct _finddata_t c_file; struct _finddata_t c_file;
#else #else
struct dirent *entry; struct dirent *entry;
#endif #endif
dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE);
luaL_argcheck (L, d->closed == 0, 1, "closed directory"); luaL_argcheck (L, d->closed == 0, 1, "closed directory");
#ifdef _WIN32 #ifdef _WIN32
if (d->hFile == 0L) { /* first entry */ if (d->hFile == 0L) { /* first entry */
if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) {
lua_pushnil (L); lua_pushnil (L);
lua_pushstring (L, strerror (errno)); lua_pushstring (L, strerror (errno));
d->closed = 1; d->closed = 1;
return 2; return 2;
} else { } else {
lua_pushstring (L, c_file.name); lua_pushstring (L, c_file.name);
return 1; return 1;
} }
} else { /* next entry */ } else { /* next entry */
if (_findnext (d->hFile, &c_file) == -1L) { if (_findnext (d->hFile, &c_file) == -1L) {
/* no more entries => close directory */ /* no more entries => close directory */
_findclose (d->hFile); _findclose (d->hFile);
d->closed = 1; d->closed = 1;
return 0; return 0;
} else { } else {
lua_pushstring (L, c_file.name); lua_pushstring (L, c_file.name);
return 1; return 1;
} }
} }
#else #else
if ((entry = readdir (d->dir)) != NULL) { if ((entry = readdir (d->dir)) != NULL) {
lua_pushstring (L, entry->d_name); lua_pushstring (L, entry->d_name);
return 1; return 1;
} else { } else {
/* no more entries => close directory */ /* no more entries => close directory */
closedir (d->dir); closedir (d->dir);
d->closed = 1; d->closed = 1;
return 0; return 0;
} }
#endif #endif
} }
@@ -505,18 +505,18 @@ static int dir_iter (lua_State *L) {
** Closes directory iterators ** Closes directory iterators
*/ */
static int dir_close (lua_State *L) { static int dir_close (lua_State *L) {
dir_data *d = (dir_data *)lua_touserdata (L, 1); dir_data *d = (dir_data *)lua_touserdata (L, 1);
#ifdef _WIN32 #ifdef _WIN32
if (!d->closed && d->hFile) { if (!d->closed && d->hFile) {
_findclose (d->hFile); _findclose (d->hFile);
} }
#else #else
if (!d->closed && d->dir) { if (!d->closed && d->dir) {
closedir (d->dir); closedir (d->dir);
} }
#endif #endif
d->closed = 1; d->closed = 1;
return 0; return 0;
} }
@@ -524,25 +524,25 @@ static int dir_close (lua_State *L) {
** Factory of directory iterators ** Factory of directory iterators
*/ */
static int dir_iter_factory (lua_State *L) { static int dir_iter_factory (lua_State *L) {
const char *path = luaL_checkstring (L, 1); const char *path = luaL_checkstring (L, 1);
dir_data *d; dir_data *d;
lua_pushcfunction (L, dir_iter); lua_pushcfunction (L, dir_iter);
d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); d = (dir_data *) lua_newuserdata (L, sizeof(dir_data));
luaL_getmetatable (L, DIR_METATABLE); luaL_getmetatable (L, DIR_METATABLE);
lua_setmetatable (L, -2); lua_setmetatable (L, -2);
d->closed = 0; d->closed = 0;
#ifdef _WIN32 #ifdef _WIN32
d->hFile = 0L; d->hFile = 0L;
if (strlen(path) > MAX_PATH-2) if (strlen(path) > MAX_PATH-2)
luaL_error (L, "path too long: %s", path); luaL_error (L, "path too long: %s", path);
else else
sprintf (d->pattern, "%s/*", path); sprintf (d->pattern, "%s/*", path);
#else #else
d->dir = opendir (path); d->dir = opendir (path);
if (d->dir == NULL) if (d->dir == NULL)
luaL_error (L, "cannot open %s: %s", path, strerror (errno)); luaL_error (L, "cannot open %s: %s", path, strerror (errno));
#endif #endif
return 2; return 2;
} }
@@ -550,38 +550,38 @@ static int dir_iter_factory (lua_State *L) {
** Creates directory metatable. ** Creates directory metatable.
*/ */
static int dir_create_meta (lua_State *L) { static int dir_create_meta (lua_State *L) {
luaL_newmetatable (L, DIR_METATABLE); luaL_newmetatable (L, DIR_METATABLE);
/* Method table */ /* Method table */
lua_newtable(L); lua_newtable(L);
lua_pushcfunction (L, dir_iter); lua_pushcfunction (L, dir_iter);
lua_setfield(L, -2, "next"); lua_setfield(L, -2, "next");
lua_pushcfunction (L, dir_close); lua_pushcfunction (L, dir_close);
lua_setfield(L, -2, "close"); lua_setfield(L, -2, "close");
/* Metamethods */ /* Metamethods */
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
lua_pushcfunction (L, dir_close); lua_pushcfunction (L, dir_close);
lua_setfield (L, -2, "__gc"); lua_setfield (L, -2, "__gc");
return 1; return 1;
} }
/* /*
** Creates lock metatable. ** Creates lock metatable.
*/ */
static int lock_create_meta (lua_State *L) { static int lock_create_meta (lua_State *L) {
luaL_newmetatable (L, LOCK_METATABLE); luaL_newmetatable (L, LOCK_METATABLE);
/* Method table */ /* Method table */
lua_newtable(L); lua_newtable(L);
lua_pushcfunction(L, lfs_unlock_dir); lua_pushcfunction(L, lfs_unlock_dir);
lua_setfield(L, -2, "free"); lua_setfield(L, -2, "free");
/* Metamethods */ /* Metamethods */
lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lfs_unlock_dir); lua_pushcfunction(L, lfs_unlock_dir);
lua_setfield(L, -2, "__gc"); lua_setfield(L, -2, "__gc");
return 1; return 1;
} }
@@ -621,17 +621,17 @@ static const char *mode2string (mode_t mode) {
else if ( S_ISDIR(mode) ) else if ( S_ISDIR(mode) )
return "directory"; return "directory";
else if ( S_ISLNK(mode) ) else if ( S_ISLNK(mode) )
return "link"; return "link";
else if ( S_ISSOCK(mode) ) else if ( S_ISSOCK(mode) )
return "socket"; return "socket";
else if ( S_ISFIFO(mode) ) else if ( S_ISFIFO(mode) )
return "named pipe"; return "named pipe";
else if ( S_ISCHR(mode) ) else if ( S_ISCHR(mode) )
return "char device"; return "char device";
else if ( S_ISBLK(mode) ) else if ( S_ISBLK(mode) )
return "block device"; return "block device";
else else
return "other"; return "other";
} }
@@ -639,78 +639,78 @@ static const char *mode2string (mode_t mode) {
** Set access time and modification values for file ** Set access time and modification values for file
*/ */
static int file_utime (lua_State *L) { static int file_utime (lua_State *L) {
const char *file = luaL_checkstring (L, 1); const char *file = luaL_checkstring (L, 1);
struct utimbuf utb, *buf; struct utimbuf utb, *buf;
if (lua_gettop (L) == 1) /* set to current date/time */ if (lua_gettop (L) == 1) /* set to current date/time */
buf = NULL; buf = NULL;
else { else {
utb.actime = (time_t)luaL_optnumber (L, 2, 0); utb.actime = (time_t)luaL_optnumber (L, 2, 0);
utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime); utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime);
buf = &utb; buf = &utb;
} }
if (utime (file, buf)) { if (utime (file, buf)) {
lua_pushnil (L); lua_pushnil (L);
lua_pushfstring (L, "%s", strerror (errno)); lua_pushfstring (L, "%s", strerror (errno));
return 2; return 2;
} }
lua_pushboolean (L, 1); lua_pushboolean (L, 1);
return 1; return 1;
} }
/* inode protection mode */ /* inode protection mode */
static void push_st_mode (lua_State *L, STAT_STRUCT *info) { static void push_st_mode (lua_State *L, STAT_STRUCT *info) {
lua_pushstring (L, mode2string (info->st_mode)); lua_pushstring (L, mode2string (info->st_mode));
} }
/* device inode resides on */ /* device inode resides on */
static void push_st_dev (lua_State *L, STAT_STRUCT *info) { static void push_st_dev (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_dev); lua_pushnumber (L, (lua_Number)info->st_dev);
} }
/* inode's number */ /* inode's number */
static void push_st_ino (lua_State *L, STAT_STRUCT *info) { static void push_st_ino (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_ino); lua_pushnumber (L, (lua_Number)info->st_ino);
} }
/* number of hard links to the file */ /* number of hard links to the file */
static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { static void push_st_nlink (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_nlink); lua_pushnumber (L, (lua_Number)info->st_nlink);
} }
/* user-id of owner */ /* user-id of owner */
static void push_st_uid (lua_State *L, STAT_STRUCT *info) { static void push_st_uid (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_uid); lua_pushnumber (L, (lua_Number)info->st_uid);
} }
/* group-id of owner */ /* group-id of owner */
static void push_st_gid (lua_State *L, STAT_STRUCT *info) { static void push_st_gid (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_gid); lua_pushnumber (L, (lua_Number)info->st_gid);
} }
/* device type, for special file inode */ /* device type, for special file inode */
static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { static void push_st_rdev (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_rdev); lua_pushnumber (L, (lua_Number)info->st_rdev);
} }
/* time of last access */ /* time of last access */
static void push_st_atime (lua_State *L, STAT_STRUCT *info) { static void push_st_atime (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, info->st_atime); lua_pushnumber (L, info->st_atime);
} }
/* time of last data modification */ /* time of last data modification */
static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { static void push_st_mtime (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, info->st_mtime); lua_pushnumber (L, info->st_mtime);
} }
/* time of last file status change */ /* time of last file status change */
static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { static void push_st_ctime (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, info->st_ctime); lua_pushnumber (L, info->st_ctime);
} }
/* file size, in bytes */ /* file size, in bytes */
static void push_st_size (lua_State *L, STAT_STRUCT *info) { static void push_st_size (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_size); lua_pushnumber (L, (lua_Number)info->st_size);
} }
#ifndef _WIN32 #ifndef _WIN32
/* blocks allocated for file */ /* blocks allocated for file */
static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { static void push_st_blocks (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_blocks); lua_pushnumber (L, (lua_Number)info->st_blocks);
} }
/* optimal file system I/O blocksize */ /* optimal file system I/O blocksize */
static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { static void push_st_blksize (lua_State *L, STAT_STRUCT *info) {
lua_pushnumber (L, (lua_Number)info->st_blksize); lua_pushnumber (L, (lua_Number)info->st_blksize);
} }
#endif #endif
static void push_invalid (lua_State *L, STAT_STRUCT *info) { static void push_invalid (lua_State *L, STAT_STRUCT *info) {
@@ -763,68 +763,68 @@ static void push_st_perm (lua_State *L, STAT_STRUCT *info) {
typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info);
struct _stat_members { struct _stat_members {
const char *name; const char *name;
_push_function push; _push_function push;
}; };
struct _stat_members members[] = { struct _stat_members members[] = {
{ "mode", push_st_mode }, { "mode", push_st_mode },
{ "dev", push_st_dev }, { "dev", push_st_dev },
{ "ino", push_st_ino }, { "ino", push_st_ino },
{ "nlink", push_st_nlink }, { "nlink", push_st_nlink },
{ "uid", push_st_uid }, { "uid", push_st_uid },
{ "gid", push_st_gid }, { "gid", push_st_gid },
{ "rdev", push_st_rdev }, { "rdev", push_st_rdev },
{ "access", push_st_atime }, { "access", push_st_atime },
{ "modification", push_st_mtime }, { "modification", push_st_mtime },
{ "change", push_st_ctime }, { "change", push_st_ctime },
{ "size", push_st_size }, { "size", push_st_size },
{ "permissions", push_st_perm }, { "permissions", push_st_perm },
#ifndef _WIN32 #ifndef _WIN32
{ "blocks", push_st_blocks }, { "blocks", push_st_blocks },
{ "blksize", push_st_blksize }, { "blksize", push_st_blksize },
#endif #endif
{ NULL, push_invalid } { NULL, push_invalid }
}; };
/* /*
** Get file or symbolic link information ** Get file or symbolic link information
*/ */
static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {
int i; int i;
STAT_STRUCT info; STAT_STRUCT info;
const char *file = luaL_checkstring (L, 1); const char *file = luaL_checkstring (L, 1);
if (st(file, &info)) { if (st(file, &info)) {
lua_pushnil (L); lua_pushnil (L);
lua_pushfstring (L, "cannot obtain information from file `%s'", file); lua_pushfstring (L, "cannot obtain information from file `%s'", file);
return 2; return 2;
} }
if (lua_isstring (L, 2)) { if (lua_isstring (L, 2)) {
int v; int v;
const char *member = lua_tostring (L, 2); const char *member = lua_tostring (L, 2);
if (strcmp (member, "mode") == 0) v = 0; if (strcmp (member, "mode") == 0) v = 0;
#ifndef _WIN32 #ifndef _WIN32
else if (strcmp (member, "blocks") == 0) v = 11; else if (strcmp (member, "blocks") == 0) v = 11;
else if (strcmp (member, "blksize") == 0) v = 12; else if (strcmp (member, "blksize") == 0) v = 12;
#endif #endif
else /* look for member */ else /* look for member */
for (v = 1; members[v].name; v++) for (v = 1; members[v].name; v++)
if (*members[v].name == *member) if (*members[v].name == *member)
break; break;
/* push member value and return */ /* push member value and return */
members[v].push (L, &info); members[v].push (L, &info);
return 1; return 1;
} else if (!lua_istable (L, 2)) } else if (!lua_istable (L, 2))
/* creates a table if none is given */ /* creates a table if none is given */
lua_newtable (L); lua_newtable (L);
/* stores all members in table on top of the stack */ /* stores all members in table on top of the stack */
for (i = 0; members[i].name; i++) { for (i = 0; members[i].name; i++) {
lua_pushstring (L, members[i].name); lua_pushstring (L, members[i].name);
members[i].push (L, &info); members[i].push (L, &info);
lua_rawset (L, -3); lua_rawset (L, -3);
} }
return 1; return 1;
} }
@@ -832,7 +832,7 @@ static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {
** Get file information using stat. ** Get file information using stat.
*/ */
static int file_info (lua_State *L) { static int file_info (lua_State *L) {
return _file_info_ (L, STAT_FUNC); return _file_info_ (L, STAT_FUNC);
} }
@@ -840,7 +840,7 @@ static int file_info (lua_State *L) {
** Get symbolic link information using lstat. ** Get symbolic link information using lstat.
*/ */
static int link_info (lua_State *L) { static int link_info (lua_State *L) {
return _file_info_ (L, LSTAT_FUNC); return _file_info_ (L, LSTAT_FUNC);
} }
@@ -848,48 +848,48 @@ static int link_info (lua_State *L) {
** Assumes the table is on top of the stack. ** Assumes the table is on top of the stack.
*/ */
static void set_info (lua_State *L) { static void set_info (lua_State *L) {
lua_pushliteral (L, "_COPYRIGHT"); lua_pushliteral (L, "_COPYRIGHT");
lua_pushliteral (L, "Copyright (C) 2003-2009 Kepler Project"); lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project");
lua_settable (L, -3); lua_settable (L, -3);
lua_pushliteral (L, "_DESCRIPTION"); lua_pushliteral (L, "_DESCRIPTION");
lua_pushliteral (L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); lua_pushliteral (L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution");
lua_settable (L, -3); lua_settable (L, -3);
lua_pushliteral (L, "_VERSION"); lua_pushliteral (L, "_VERSION");
lua_pushliteral (L, "LuaFileSystem 1.5.0"); lua_pushliteral (L, "LuaFileSystem 1.6.0");
lua_settable (L, -3); lua_settable (L, -3);
} }
static const struct luaL_Reg fslib[] = { static const struct luaL_Reg fslib[] = {
{"attributes", file_info}, {"attributes", file_info},
{"chdir", change_dir}, {"chdir", change_dir},
{"currentdir", get_dir}, {"currentdir", get_dir},
{"dir", dir_iter_factory}, {"dir", dir_iter_factory},
{"link", make_link}, {"link", make_link},
{"lock", file_lock}, {"lock", file_lock},
{"mkdir", make_dir}, {"mkdir", make_dir},
{"rmdir", remove_dir}, {"rmdir", remove_dir},
{"symlinkattributes", link_info}, {"symlinkattributes", link_info},
{"setmode", lfs_f_setmode}, {"setmode", lfs_f_setmode},
{"touch", file_utime}, {"touch", file_utime},
{"unlock", file_unlock}, {"unlock", file_unlock},
{"lock_dir", lfs_lock_dir}, {"lock_dir", lfs_lock_dir},
{NULL, NULL}, {NULL, NULL},
}; };
#if LUA_VERSION_NUM > 501 #if LUA_VERSION_NUM > 501
static void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l) static void luaL_register (lua_State *L, const char *libname, const luaL_Reg *l)
{ {
luaL_newlib (L, l); luaL_newlib (L, l);
lua_pushvalue (L, -1); lua_pushvalue (L, -1);
lua_setglobal (L, libname); lua_setglobal (L, libname);
} }
#endif #endif
int luaopen_lfs (lua_State *L) { int luaopen_lfs (lua_State *L) {
dir_create_meta (L); dir_create_meta (L);
lock_create_meta (L); lock_create_meta (L);
luaL_register (L, "lfs", fslib); luaL_register (L, "lfs", fslib);
set_info (L); set_info (L);
return 1; return 1;
} }

View File

@@ -7,22 +7,25 @@ local upper = ".."
local lfs = require"lfs" local lfs = require"lfs"
print (lfs._VERSION) print (lfs._VERSION)
io.write(".")
io.flush()
function attrdir (path) function attrdir (path)
for file in lfs.dir(path) do for file in lfs.dir(path) do
if file ~= "." and file ~= ".." then if file ~= "." and file ~= ".." then
local f = path..sep..file local f = path..sep..file
print ("\t=> "..f.." <=") print ("\t=> "..f.." <=")
local attr = lfs.attributes (f) local attr = lfs.attributes (f)
assert (type(attr) == "table") assert (type(attr) == "table")
if attr.mode == "directory" then if attr.mode == "directory" then
attrdir (f) attrdir (f)
else else
for name, value in pairs(attr) do for name, value in pairs(attr) do
print (name, value) print (name, value)
end end
end end
end end
end end
end end
-- Checking changing directories -- Checking changing directories
@@ -33,6 +36,9 @@ assert (lfs.chdir (reldir), "could not change back to current directory")
assert (lfs.currentdir() == current, "error trying to change directories") assert (lfs.currentdir() == current, "error trying to change directories")
assert (lfs.chdir ("this couldn't be an actual directory") == nil, "could change to a non-existent directory") assert (lfs.chdir ("this couldn't be an actual directory") == nil, "could change to a non-existent directory")
io.write(".")
io.flush()
-- Changing creating and removing directories -- Changing creating and removing directories
local tmpdir = current..sep.."lfs_tmp_dir" local tmpdir = current..sep.."lfs_tmp_dir"
local tmpfile = tmpdir..sep.."tmp_file" local tmpfile = tmpdir..sep.."tmp_file"
@@ -44,15 +50,21 @@ if lfs.chdir (tmpdir) then
assert (lfs.rmdir (tmpdir), "could not remove directory from previous test") assert (lfs.rmdir (tmpdir), "could not remove directory from previous test")
end end
io.write(".")
io.flush()
-- tries to create a directory -- tries to create a directory
assert (lfs.mkdir (tmpdir), "could not make a new directory") assert (lfs.mkdir (tmpdir), "could not make a new directory")
local attrib, errmsg = lfs.attributes (tmpdir) local attrib, errmsg = lfs.attributes (tmpdir)
if not attrib then if not attrib then
error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg) error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg)
end end
local f = io.open(tmpfile, "w") local f = io.open(tmpfile, "w")
f:close() f:close()
io.write(".")
io.flush()
-- Change access time -- Change access time
local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0}) local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0})
assert (lfs.touch (tmpfile, testdate)) assert (lfs.touch (tmpfile, testdate))
@@ -60,6 +72,9 @@ local new_att = assert (lfs.attributes (tmpfile))
assert (new_att.access == testdate, "could not set access time") assert (new_att.access == testdate, "could not set access time")
assert (new_att.modification == testdate, "could not set modification time") assert (new_att.modification == testdate, "could not set modification time")
io.write(".")
io.flush()
-- Change access and modification time -- Change access and modification time
local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0}) local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0})
local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0}) local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0})
@@ -69,6 +84,9 @@ local new_att = assert (lfs.attributes (tmpfile))
assert (new_att.access == testdate2, "could not set access time") assert (new_att.access == testdate2, "could not set access time")
assert (new_att.modification == testdate1, "could not set modification time") assert (new_att.modification == testdate1, "could not set modification time")
io.write(".")
io.flush()
-- Checking link (does not work on Windows) -- Checking link (does not work on Windows)
if lfs.link (tmpfile, "_a_link_for_test_", true) then if lfs.link (tmpfile, "_a_link_for_test_", true) then
assert (lfs.attributes"_a_link_for_test_".mode == "file") assert (lfs.attributes"_a_link_for_test_".mode == "file")
@@ -79,6 +97,9 @@ if lfs.link (tmpfile, "_a_link_for_test_", true) then
assert (os.remove"_a_hard_link_for_test_") assert (os.remove"_a_hard_link_for_test_")
end end
io.write(".")
io.flush()
-- Checking text/binary modes (only has an effect in Windows) -- Checking text/binary modes (only has an effect in Windows)
local f = io.open(tmpfile, "w") local f = io.open(tmpfile, "w")
local result, mode = lfs.setmode(f, "binary") local result, mode = lfs.setmode(f, "binary")
@@ -87,29 +108,44 @@ result, mode = lfs.setmode(f, "text")
assert(result and mode == "binary") assert(result and mode == "binary")
f:close() f:close()
io.write(".")
io.flush()
-- Restore access time to current value -- Restore access time to current value
assert (lfs.touch (tmpfile, attrib.access, attrib.modification)) assert (lfs.touch (tmpfile, attrib.access, attrib.modification))
new_att = assert (lfs.attributes (tmpfile)) new_att = assert (lfs.attributes (tmpfile))
assert (new_att.access == attrib.access) assert (new_att.access == attrib.access)
assert (new_att.modification == attrib.modification) assert (new_att.modification == attrib.modification)
io.write(".")
io.flush()
-- Remove new file and directory -- Remove new file and directory
assert (os.remove (tmpfile), "could not remove new file") assert (os.remove (tmpfile), "could not remove new file")
assert (lfs.rmdir (tmpdir), "could not remove new directory") assert (lfs.rmdir (tmpdir), "could not remove new directory")
assert (lfs.mkdir (tmpdir..sep.."lfs_tmp_dir") == nil, "could create a directory inside a non-existent one") assert (lfs.mkdir (tmpdir..sep.."lfs_tmp_dir") == nil, "could create a directory inside a non-existent one")
io.write(".")
io.flush()
-- Trying to get attributes of a non-existent file -- Trying to get attributes of a non-existent file
assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file") assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file")
assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory")
io.write(".")
io.flush()
-- Stressing directory iterator -- Stressing directory iterator
count = 0 count = 0
for i = 1, 4000 do for i = 1, 4000 do
for file in lfs.dir (tmp) do for file in lfs.dir (tmp) do
count = count + 1 count = count + 1
end end
end end
io.write(".")
io.flush()
-- Stressing directory iterator, explicit version -- Stressing directory iterator, explicit version
count = 0 count = 0
for i = 1, 4000 do for i = 1, 4000 do
@@ -122,6 +158,9 @@ for i = 1, 4000 do
assert(not pcall(dir.next, dir)) assert(not pcall(dir.next, dir))
end end
io.write(".")
io.flush()
-- directory explicit close -- directory explicit close
local iter, dir = lfs.dir(tmp) local iter, dir = lfs.dir(tmp)
dir:close() dir:close()