diff --git a/csrc/lfs/WHAT b/csrc/lfs/WHAT index a7269b3..be9050e 100644 --- a/csrc/lfs/WHAT +++ b/csrc/lfs/WHAT @@ -1,3 +1,2 @@ -LuaFileSystem 1.6.2 from https://github.com/keplerproject/luafilesystem.git (MIT License) +LuaFileSystem e5f06c2 from https://github.com/keplerproject/luafilesystem (MIT License) -NOTE: lfs.link crashes on Windows (commented the test) diff --git a/csrc/lfs/lfs.c b/csrc/lfs/lfs.c index b270ee8..f510ab5 100644 --- a/csrc/lfs/lfs.c +++ b/csrc/lfs/lfs.c @@ -1,25 +1,25 @@ /* ** LuaFileSystem -** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem) +** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) ** ** File system manipulation library. ** This library offers these functions: -** lfs.attributes (filepath [, attributename]) +** lfs.attributes (filepath [, attributename | attributetable]) ** lfs.chdir (path) ** lfs.currentdir () ** lfs.dir (path) +** lfs.link (old, new[, symlink]) ** lfs.lock (fh, mode) ** lfs.lock_dir (path) ** lfs.mkdir (path) ** lfs.rmdir (path) ** lfs.setmode (filepath, mode) -** lfs.symlinkattributes (filepath [, attributename]) -- thanks to Sam Roberts +** lfs.symlinkattributes (filepath [, attributename]) ** lfs.touch (filepath [, atime [, mtime]]) ** lfs.unlock (fh) -** -** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $ */ +#ifndef LFS_DO_NOT_USE_LARGE_FILE #ifndef _WIN32 #ifndef _AIX #define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ @@ -27,8 +27,11 @@ #define _LARGE_FILES 1 /* AIX */ #endif #endif +#endif +#ifndef LFS_DO_NOT_USE_LARGE_FILE #define _LARGEFILE64_SOURCE +#endif #include #include @@ -38,22 +41,26 @@ #include #ifdef _WIN32 -#include -#include -#include -#include -#ifdef __BORLANDC__ - #include + #include + #include + #include + #include + #ifdef __BORLANDC__ + #include + #else + #include + #endif + #include + /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ + #define LFS_MAXPATHLEN MAX_PATH #else - #include -#endif -#include -#else -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include + #include /* for MAXPATHLEN */ + #define LFS_MAXPATHLEN MAXPATHLEN #endif #include @@ -62,11 +69,21 @@ #include "lfs.h" -#define LFS_VERSION "1.6.2" +#define LFS_VERSION "1.7.0" #define LFS_LIBNAME "lfs" -#if LUA_VERSION_NUM < 502 -# define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l)) +#if LUA_VERSION_NUM >= 503 /* Lua 5.3 */ + +#ifndef luaL_optlong +#define luaL_optlong luaL_optinteger +#endif + +#endif + +#if LUA_VERSION_NUM >= 502 +# define new_lib(L, l) (luaL_newlib(L, l)) +#else +# define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l)) #endif /* Define 'strerror' for systems that do not implement it */ @@ -74,27 +91,11 @@ #define strerror(_) "System unable to describe the error" #endif -/* Define 'getcwd' for systems that do not implement it */ -#ifdef NO_GETCWD -#define getcwd(p,s) NULL -#define getcwd_error "Function 'getcwd' not provided by system" -#else -#define getcwd_error strerror(errno) - #ifdef _WIN32 - /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ - #define LFS_MAXPATHLEN MAX_PATH - #else - /* For MAXPATHLEN: */ - #include - #define LFS_MAXPATHLEN MAXPATHLEN - #endif -#endif - #define DIR_METATABLE "directory metatable" typedef struct dir_data { int closed; #ifdef _WIN32 - long hFile; + intptr_t hFile; char pattern[MAX_PATH+1]; #else DIR *dir; @@ -105,10 +106,10 @@ typedef struct dir_data { #ifdef _WIN32 #ifdef __BORLANDC__ - #define lfs_setmode(L,file,m) ((void)L, setmode(_fileno(file), m)) + #define lfs_setmode(file, m) (setmode(_fileno(file), m)) #define STAT_STRUCT struct stati64 #else - #define lfs_setmode(L,file,m) ((void)L, _setmode(_fileno(file), m)) + #define lfs_setmode(file, m) (_setmode(_fileno(file), m)) #define STAT_STRUCT struct _stati64 #endif #define STAT_FUNC _stati64 @@ -116,12 +117,19 @@ typedef struct dir_data { #else #define _O_TEXT 0 #define _O_BINARY 0 -#define lfs_setmode(L,file,m) ((void)L, (void)file, (void)m, 0) +#define lfs_setmode(file, m) ((void)file, (void)m, 0) #define STAT_STRUCT struct stat #define STAT_FUNC stat #define LSTAT_FUNC lstat #endif +#ifdef _WIN32 + #define lfs_mkdir _mkdir +#else + #define lfs_mkdir(path) (mkdir((path), \ + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH)) +#endif + /* ** Utility functions */ @@ -136,12 +144,13 @@ static int pusherror(lua_State *L, const char *info) return 3; } -static int pushresult(lua_State *L, int i, const char *info) -{ - if (i==-1) - return pusherror(L, info); - lua_pushinteger(L, i); - return 1; +static int pushresult(lua_State *L, int res, const char *info) { + if (res == -1) { + return pusherror(L, info); + } else { + lua_pushboolean(L, 1); + return 1; + } } @@ -167,33 +176,61 @@ static int change_dir (lua_State *L) { ** 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[LFS_MAXPATHLEN]; - if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) { +#ifdef NO_GETCWD lua_pushnil(L); - lua_pushstring(L, getcwd_error); + lua_pushstring(L, "Function 'getcwd' not provided by system"); return 2; - } - else { - lua_pushstring(L, path); - return 1; - } +#else + char *path = NULL; + /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ + size_t size = LFS_MAXPATHLEN; /* initial buffer size */ + int result; + while (1) { + char* path2 = realloc(path, size); + if (!path2) /* failed to allocate */ { + result = pusherror(L, "get_dir realloc() failed"); + break; + } + path = path2; + if (getcwd(path, size) != NULL) { + /* success, push the path to the Lua stack */ + lua_pushstring(L, path); + result = 1; + break; + } + if (errno != ERANGE) { /* unexpected error */ + result = pusherror(L, "get_dir getcwd() failed"); + break; + } + /* ERANGE = insufficient buffer capacity, double size and retry */ + size *= 2; + } + free(path); + return result; +#endif } /* ** 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) { +#if LUA_VERSION_NUM == 501 FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); - if (fh == NULL) { - luaL_error (L, "%s: not a file", funcname); - return 0; - } else if (*fh == NULL) { + if (*fh == NULL) { luaL_error (L, "%s: closed file", funcname); return 0; } else return *fh; +#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503 + luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*"); + if (fh->closef == 0 || fh->f == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return fh->f; +#else +#error unsupported Lua version +#endif } @@ -280,8 +317,11 @@ static int lfs_lock_dir(lua_State *L) { return 1; } static int lfs_unlock_dir(lua_State *L) { - lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE); - CloseHandle(lock->fd); + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->fd != INVALID_HANDLE_VALUE) { + CloseHandle(lock->fd); + lock->fd=INVALID_HANDLE_VALUE; + } return 0; } #else @@ -310,7 +350,7 @@ static int lfs_lock_dir(lua_State *L) { return 1; } static int lfs_unlock_dir(lua_State *L) { - lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE); + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); if(lock->ln) { unlink(lock->ln); free(lock->ln); @@ -324,25 +364,20 @@ static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { static const int mode[] = {_O_BINARY, _O_TEXT}; static const char *const modenames[] = {"binary", "text", NULL}; int op = luaL_checkoption(L, arg, NULL, modenames); - int res = lfs_setmode(L, f, mode[op]); + int res = lfs_setmode(f, mode[op]); if (res != -1) { int i; lua_pushboolean(L, 1); for (i = 0; modenames[i] != NULL; i++) { if (mode[i] == res) { lua_pushstring(L, modenames[i]); - goto exit; + return 2; } } lua_pushnil(L); - exit: return 2; } else { - int en = errno; - lua_pushnil(L); - lua_pushfstring(L, "%s", strerror(en)); - lua_pushinteger(L, en); - return 3; + return pusherror(L, NULL); } } @@ -360,8 +395,8 @@ static int lfs_f_setmode(lua_State *L) { static int file_lock (lua_State *L) { FILE *fh = check_file (L, 1, "lock"); const char *mode = luaL_checkstring (L, 2); - const long start = luaL_optlong (L, 3, 0); - long len = luaL_optlong (L, 4, 0); + const long start = (long) luaL_optinteger (L, 3, 0); + long len = (long) luaL_optinteger (L, 4, 0); if (_file_lock (L, fh, mode, start, len, "lock")) { lua_pushboolean (L, 1); return 1; @@ -381,8 +416,8 @@ static int file_lock (lua_State *L) { */ static int file_unlock (lua_State *L) { FILE *fh = check_file (L, 1, "unlock"); - const long start = luaL_optlong (L, 2, 0); - long len = luaL_optlong (L, 3, 0); + const long start = (long) luaL_optinteger (L, 2, 0); + long len = (long) luaL_optinteger (L, 3, 0); if (_file_lock (L, fh, "u", start, len, "unlock")) { lua_pushboolean (L, 1); return 1; @@ -400,15 +435,20 @@ static int file_unlock (lua_State *L) { ** @param #2 Name of link. ** @param #3 True if link is symbolic (optional). */ -static int make_link(lua_State *L) -{ +static int make_link (lua_State *L) { #ifndef _WIN32 - const char *oldpath = luaL_checkstring(L, 1); - const char *newpath = luaL_checkstring(L, 2); - return pushresult(L, - (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL); + const char *oldpath = luaL_checkstring(L, 1); + const char *newpath = luaL_checkstring(L, 2); + int res = (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath); + if (res == -1) { + return pusherror(L, NULL); + } else { + lua_pushinteger(L, 0); + return 1; + } #else - pusherror(L, "make_link is not supported on Windows"); + errno = ENOSYS; /* = "Function not implemented" */ + return pushresult(L, -1, "make_link is not supported on Windows"); #endif } @@ -418,42 +458,21 @@ static int make_link(lua_State *L) ** @param #1 Directory path. */ static int make_dir (lua_State *L) { - const char *path = luaL_checkstring (L, 1); - int fail; -#ifdef _WIN32 - fail = _mkdir (path); -#else - fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | - S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH ); -#endif - if (fail) { - lua_pushnil (L); - lua_pushfstring (L, "%s", strerror(errno)); - return 2; - } - lua_pushboolean (L, 1); - return 1; + const char *path = luaL_checkstring(L, 1); + return pushresult(L, lfs_mkdir(path), NULL); } + /* ** Removes a directory. ** @param #1 Directory path. */ static int remove_dir (lua_State *L) { - const char *path = luaL_checkstring (L, 1); - int fail; - - fail = rmdir (path); - - if (fail) { - lua_pushnil (L); - lua_pushfstring (L, "%s", strerror(errno)); - return 2; - } - lua_pushboolean (L, 1); - return 1; + const char *path = luaL_checkstring(L, 1); + return pushresult(L, rmdir(path), NULL); } + /* ** Directory iterator */ @@ -566,6 +585,7 @@ static int dir_create_meta (lua_State *L) { return 1; } + /* ** Creates lock metatable. */ @@ -636,26 +656,24 @@ static const char *mode2string (mode_t mode) { /* -** Set access time and modification values for file +** Set access time and modification values for a file. +** @param #1 File path. +** @param #2 Access time in seconds, current time is used if missing. +** @param #3 Modification time in seconds, access time is used if missing. */ static int file_utime (lua_State *L) { - const char *file = luaL_checkstring (L, 1); - struct utimbuf utb, *buf; + const char *file = luaL_checkstring(L, 1); + struct utimbuf utb, *buf; - if (lua_gettop (L) == 1) /* set to current date/time */ - buf = NULL; - else { - utb.actime = (time_t)luaL_optnumber (L, 2, 0); - utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime); - buf = &utb; - } - if (utime (file, buf)) { - lua_pushnil (L); - lua_pushfstring (L, "%s", strerror (errno)); - return 2; - } - lua_pushboolean (L, 1); - return 1; + if (lua_gettop (L) == 1) /* set to current date/time */ + buf = NULL; + else { + utb.actime = (time_t) luaL_optnumber(L, 2, 0); + utb.modtime = (time_t) luaL_optinteger(L, 3, utb.actime); + buf = &utb; + } + + return pushresult(L, utime(file, buf), NULL); } @@ -665,60 +683,54 @@ static void push_st_mode (lua_State *L, STAT_STRUCT *info) { } /* device inode resides on */ static void push_st_dev (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_dev); + lua_pushinteger (L, (lua_Integer) info->st_dev); } /* inode's number */ static void push_st_ino (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_ino); + lua_pushinteger (L, (lua_Integer) info->st_ino); } /* number of hard links to the file */ static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_nlink); + lua_pushinteger (L, (lua_Integer)info->st_nlink); } /* user-id of owner */ static void push_st_uid (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_uid); + lua_pushinteger (L, (lua_Integer)info->st_uid); } /* group-id of owner */ static void push_st_gid (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_gid); + lua_pushinteger (L, (lua_Integer)info->st_gid); } /* device type, for special file inode */ static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_rdev); + lua_pushinteger (L, (lua_Integer) info->st_rdev); } /* time of last access */ static void push_st_atime (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, info->st_atime); + lua_pushinteger (L, (lua_Integer) info->st_atime); } /* time of last data modification */ static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, info->st_mtime); + lua_pushinteger (L, (lua_Integer) info->st_mtime); } /* time of last file status change */ static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, info->st_ctime); + lua_pushinteger (L, (lua_Integer) info->st_ctime); } /* file size, in bytes */ static void push_st_size (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_size); + lua_pushinteger (L, (lua_Integer)info->st_size); } #ifndef _WIN32 /* blocks allocated for file */ static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_blocks); + lua_pushinteger (L, (lua_Integer)info->st_blocks); } /* optimal file system I/O blocksize */ static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { - lua_pushnumber (L, (lua_Number)info->st_blksize); + lua_pushinteger (L, (lua_Integer)info->st_blksize); } #endif -static void push_invalid (lua_State *L, STAT_STRUCT *info) { - luaL_error(L, "invalid attribute name"); -#ifndef _WIN32 - info->st_blksize = 0; /* never reached */ -#endif -} /* ** Convert the inode protection mode to a permission list. @@ -726,7 +738,7 @@ static void push_invalid (lua_State *L, STAT_STRUCT *info) { #ifdef _WIN32 static const char *perm2string (unsigned short mode) { - static char perms[10] = "---------\0"; + static char perms[10] = "---------"; int i; for (i=0;i<9;i++) perms[i]='-'; if (mode & _S_IREAD) @@ -739,7 +751,7 @@ static const char *perm2string (unsigned short mode) { } #else static const char *perm2string (mode_t mode) { - static char perms[10] = "---------\0"; + static char perms[10] = "---------"; int i; for (i=0;i<9;i++) perms[i]='-'; if (mode & S_IRUSR) perms[0] = 'r'; @@ -784,40 +796,40 @@ struct _stat_members members[] = { { "blocks", push_st_blocks }, { "blksize", push_st_blksize }, #endif - { NULL, push_invalid } + { NULL, NULL } }; /* ** 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); + int i; if (st(file, &info)) { - lua_pushnil (L); - lua_pushfstring (L, "cannot obtain information from file `%s'", file); - return 2; + lua_pushnil(L); + lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno)); + lua_pushinteger(L, errno); + return 3; } 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 */ + for (i = 0; members[i].name; i++) { + if (strcmp(members[i].name, member) == 0) { + /* push member value and return */ + members[i].push (L, &info); + return 1; + } + } + /* member not found */ + return luaL_error(L, "invalid attribute name '%s'", member); + } + /* creates a table if none is given, removes extra arguments */ + lua_settop(L, 2); + if (!lua_istable (L, 2)) { 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); @@ -836,11 +848,61 @@ static int file_info (lua_State *L) { } +/* +** Push the symlink target to the top of the stack. +** Assumes the file name is at position 1 of the stack. +** Returns 1 if successful (with the target on top of the stack), +** 0 on failure (with stack unchanged, and errno set). +*/ +static int push_link_target(lua_State *L) { +#ifdef _WIN32 + errno = ENOSYS; + return 0; +#else + const char *file = luaL_checkstring(L, 1); + char *target = NULL; + int tsize, size = 256; /* size = initial buffer capacity */ + while (1) { + char* target2 = realloc(target, size); + if (!target2) { /* failed to allocate */ + free(target); + return 0; + } + target = target2; + tsize = readlink(file, target, size); + if (tsize < 0) { /* a readlink() error occurred */ + free(target); + return 0; + } + if (tsize < size) + break; + /* possibly truncated readlink() result, double size and retry */ + size *= 2; + } + target[tsize] = '\0'; + lua_pushlstring(L, target, tsize); + free(target); + return 1; +#endif +} + /* ** Get symbolic link information using lstat. */ static int link_info (lua_State *L) { - return _file_info_ (L, LSTAT_FUNC); + int ret; + if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { + int ok = push_link_target(L); + return ok ? 1 : pusherror(L, "could not obtain link target"); + } + ret = _file_info_ (L, LSTAT_FUNC); + if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { + int ok = push_link_target(L); + if (ok) { + lua_setfield(L, -2, "target"); + } + } + return ret; } @@ -848,15 +910,12 @@ static int link_info (lua_State *L) { ** Assumes the table is on top of the stack. */ static void set_info (lua_State *L) { - lua_pushliteral (L, "_COPYRIGHT"); - lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project"); - lua_settable (L, -3); - 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_settable (L, -3); - lua_pushliteral (L, "_VERSION"); - lua_pushliteral (L, "LuaFileSystem "LFS_VERSION); - lua_settable (L, -3); + lua_pushliteral(L, "Copyright (C) 2003-2017 Kepler Project"); + lua_setfield(L, -2, "_COPYRIGHT"); + 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_setfield(L, -2, "_DESCRIPTION"); + lua_pushliteral(L, "LuaFileSystem " LFS_VERSION); + lua_setfield(L, -2, "_VERSION"); } @@ -877,10 +936,10 @@ static const struct luaL_Reg fslib[] = { {NULL, NULL}, }; -int luaopen_lfs (lua_State *L) { +LFS_EXPORT int luaopen_lfs (lua_State *L) { dir_create_meta (L); lock_create_meta (L); - luaL_newlib (L, fslib); + new_lib (L, fslib); lua_pushvalue(L, -1); lua_setglobal(L, LFS_LIBNAME); set_info (L); diff --git a/csrc/lfs/lfs.h b/csrc/lfs/lfs.h index 4b52780..4587564 100644 --- a/csrc/lfs/lfs.h +++ b/csrc/lfs/lfs.h @@ -1,17 +1,34 @@ /* ** LuaFileSystem -** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem) -** -** $Id: lfs.h,v 1.5 2008/02/19 20:08:23 mascarenhas Exp $ +** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem) */ /* Define 'chdir' for systems that do not implement it */ #ifdef NO_CHDIR -#define chdir(p) (-1) -#define chdir_error "Function 'chdir' not provided by system" + #define chdir(p) (-1) + #define chdir_error "Function 'chdir' not provided by system" #else -#define chdir_error strerror(errno) + #define chdir_error strerror(errno) #endif +#ifdef _WIN32 + #define chdir(p) (_chdir(p)) + #define getcwd(d, s) (_getcwd(d, s)) + #define rmdir(p) (_rmdir(p)) + #define LFS_EXPORT __declspec (dllexport) + #ifndef fileno + #define fileno(f) (_fileno(f)) + #endif +#else + #define LFS_EXPORT +#endif -int luaopen_lfs (lua_State *L); +#ifdef __cplusplus +extern "C" { +#endif + +LFS_EXPORT int luaopen_lfs (lua_State *L); + +#ifdef __cplusplus +} +#endif diff --git a/lfs_test.lua b/lfs_test.lua index 6f7482b..591ee25 100644 --- a/lfs_test.lua +++ b/lfs_test.lua @@ -1,171 +1,194 @@ -#!/usr/bin/env lua5.1 - -local tmp = "/tmp" -local sep = string.match (package.config, "[^\n]+") -local upper = ".." - -local lfs = require"lfs" -print (lfs._VERSION) - -io.write(".") -io.flush() - -function attrdir (path) - for file in lfs.dir(path) do - if file ~= "." and file ~= ".." then - local f = path..sep..file - print ("\t=> "..f.." <=") - local attr = lfs.attributes (f) - assert (type(attr) == "table") - if attr.mode == "directory" then - attrdir (f) - else - for name, value in pairs(attr) do - print (name, value) - end - end - end - end -end - --- Checking changing directories -local current = assert (lfs.currentdir()) -local reldir = string.gsub (current, "^.*%"..sep.."([^"..sep.."])$", "%1") -assert (lfs.chdir (upper), "could not change to upper directory") -assert (lfs.chdir (reldir), "could not change back to current directory") -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") - -io.write(".") -io.flush() - --- Changing creating and removing directories -local tmpdir = current..sep.."lfs_tmp_dir" -local tmpfile = tmpdir..sep.."tmp_file" --- Test for existence of a previous lfs_tmp_dir --- that may have resulted from an interrupted test execution and remove it -if lfs.chdir (tmpdir) then - assert (lfs.chdir (upper), "could not change to upper directory") - assert (os.remove (tmpfile), "could not remove file from previous test") - assert (lfs.rmdir (tmpdir), "could not remove directory from previous test") -end - -io.write(".") -io.flush() - --- tries to create a directory -assert (lfs.mkdir (tmpdir), "could not make a new directory") -local attrib, errmsg = lfs.attributes (tmpdir) -if not attrib then - error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg) -end -local f = io.open(tmpfile, "w") -f:close() - -io.write(".") -io.flush() - --- Change access time -local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0}) -assert (lfs.touch (tmpfile, testdate)) -local new_att = assert (lfs.attributes (tmpfile)) -assert (new_att.access == testdate, "could not set access time") -assert (new_att.modification == testdate, "could not set modification time") - -io.write(".") -io.flush() - --- Change access and modification time -local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0}) -local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0}) - -assert (lfs.touch (tmpfile, testdate2, testdate1)) -local new_att = assert (lfs.attributes (tmpfile)) -assert (new_att.access == testdate2, "could not set access time") -assert (new_att.modification == testdate1, "could not set modification time") - -io.write(".") -io.flush() - --- Checking link (does not work on Windows) -local ffi = require'ffi' -if ffi.os ~= 'Windows' then - if lfs.link (tmpfile, "_a_link_for_test_", true) then - assert (lfs.attributes"_a_link_for_test_".mode == "file") - assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link") - assert (lfs.link (tmpfile, "_a_hard_link_for_test_")) - assert (lfs.attributes (tmpfile, "nlink") == 2) - assert (os.remove"_a_link_for_test_") - assert (os.remove"_a_hard_link_for_test_") - end - - io.write(".") - io.flush() -end - --- Checking text/binary modes (only has an effect in Windows) -local f = io.open(tmpfile, "w") -local result, mode = lfs.setmode(f, "binary") -assert(result) -- on non-Windows platforms, mode is always returned as "binary" -result, mode = lfs.setmode(f, "text") -assert(result and mode == "binary") -f:close() - -io.write(".") -io.flush() - --- Restore access time to current value -assert (lfs.touch (tmpfile, attrib.access, attrib.modification)) -new_att = assert (lfs.attributes (tmpfile)) -assert (new_att.access == attrib.access) -assert (new_att.modification == attrib.modification) - -io.write(".") -io.flush() - --- Remove new file and directory -assert (os.remove (tmpfile), "could not remove new file") -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") - -io.write(".") -io.flush() - --- 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 (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") - -io.write(".") -io.flush() - --- Stressing directory iterator -count = 0 -for i = 1, 4000 do - for file in lfs.dir (tmp) do - count = count + 1 - end -end - -io.write(".") -io.flush() - --- Stressing directory iterator, explicit version -count = 0 -for i = 1, 4000 do - local iter, dir = lfs.dir(tmp) - local file = dir:next() - while file do - count = count + 1 - file = dir:next() - end - assert(not pcall(dir.next, dir)) -end - -io.write(".") -io.flush() - --- directory explicit close -local iter, dir = lfs.dir(tmp) -dir:close() -assert(not pcall(dir.next, dir)) -print"Ok!" +#!/usr/bin/env lua5.1 + +local tmp = "/tmp" +local sep = string.match (package.config, "[^\n]+") +local upper = ".." + +local lfs = require"lfs" +print (lfs._VERSION) + +io.write(".") +io.flush() + +function attrdir (path) + for file in lfs.dir(path) do + if file ~= "." and file ~= ".." then + local f = path..sep..file + print ("\t=> "..f.." <=") + local attr = lfs.attributes (f) + assert (type(attr) == "table") + if attr.mode == "directory" then + attrdir (f) + else + for name, value in pairs(attr) do + print (name, value) + end + end + end + end +end + +-- Checking changing directories +local current = assert (lfs.currentdir()) +local reldir = string.gsub (current, "^.*%"..sep.."([^"..sep.."])$", "%1") +assert (lfs.chdir (upper), "could not change to upper directory") +assert (lfs.chdir (reldir), "could not change back to current directory") +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") + +io.write(".") +io.flush() + +-- Changing creating and removing directories +local tmpdir = current..sep.."lfs_tmp_dir" +local tmpfile = tmpdir..sep.."tmp_file" +-- Test for existence of a previous lfs_tmp_dir +-- that may have resulted from an interrupted test execution and remove it +if lfs.chdir (tmpdir) then + assert (lfs.chdir (upper), "could not change to upper directory") + assert (os.remove (tmpfile), "could not remove file from previous test") + assert (lfs.rmdir (tmpdir), "could not remove directory from previous test") +end + +io.write(".") +io.flush() + +-- tries to create a directory +assert (lfs.mkdir (tmpdir), "could not make a new directory") +local attrib, errmsg = lfs.attributes (tmpdir) +if not attrib then + error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg) +end +local f = io.open(tmpfile, "w") +f:close() + +io.write(".") +io.flush() + +-- Change access time +local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0}) +assert (lfs.touch (tmpfile, testdate)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate, "could not set access time") +assert (new_att.modification == testdate, "could not set modification time") + +io.write(".") +io.flush() + +-- Change access and modification time +local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0}) +local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0}) + +assert (lfs.touch (tmpfile, testdate2, testdate1)) +local new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == testdate2, "could not set access time") +assert (new_att.modification == testdate1, "could not set modification time") + +io.write(".") +io.flush() + +-- Checking link (does not work on Windows) +if lfs.link (tmpfile, "_a_link_for_test_", true) then + assert (lfs.attributes"_a_link_for_test_".mode == "file") + assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link") + assert (lfs.symlinkattributes"_a_link_for_test_".target == tmpfile) + assert (lfs.symlinkattributes("_a_link_for_test_", "target") == tmpfile) + assert (lfs.link (tmpfile, "_a_hard_link_for_test_")) + assert (lfs.attributes (tmpfile, "nlink") == 2) + assert (os.remove"_a_link_for_test_") + assert (os.remove"_a_hard_link_for_test_") +end + +io.write(".") +io.flush() + +-- Checking text/binary modes (only has an effect in Windows) +local f = io.open(tmpfile, "w") +local result, mode = lfs.setmode(f, "binary") +assert(result) -- on non-Windows platforms, mode is always returned as "binary" +result, mode = lfs.setmode(f, "text") +assert(result and mode == "binary") +f:close() +local ok, err = pcall(lfs.setmode, f, "binary") +assert(not ok, "could setmode on closed file") +assert(err:find("closed file"), "bad error message for setmode on closed file") + +io.write(".") +io.flush() + +-- Restore access time to current value +assert (lfs.touch (tmpfile, attrib.access, attrib.modification)) +new_att = assert (lfs.attributes (tmpfile)) +assert (new_att.access == attrib.access) +assert (new_att.modification == attrib.modification) + +io.write(".") +io.flush() + +-- Check consistency of lfs.attributes values +local attr = lfs.attributes (tmpfile) +for key, value in pairs(attr) do + assert (value == lfs.attributes (tmpfile, key), + "lfs.attributes values not consistent") +end + +-- Check that lfs.attributes accepts a table as second argument +local attr2 = {} +lfs.attributes(tmpfile, attr2) +for key, value in pairs(attr2) do + assert (value == lfs.attributes (tmpfile, key), + "lfs.attributes values with table argument not consistent") +end + +-- Check that extra arguments are ignored +lfs.attributes(tmpfile, attr2, nil) + +-- Remove new file and directory +assert (os.remove (tmpfile), "could not remove new file") +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") + +io.write(".") +io.flush() + +-- Trying to get attributes of a non-existent file +local attr_ok, err, errno = lfs.attributes("this couldn't be an actual file") +assert(attr_ok == nil, "could get attributes of a non-existent file") +assert(type(err) == "string", "failed lfs.attributes did not return an error message") +assert(type(errno) == "number", "failed lfs.attributes did not return error code") +assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory") + +io.write(".") +io.flush() + +-- Stressing directory iterator +count = 0 +for i = 1, 4000 do + for file in lfs.dir (tmp) do + count = count + 1 + end +end + +io.write(".") +io.flush() + +-- Stressing directory iterator, explicit version +count = 0 +for i = 1, 4000 do + local iter, dir = lfs.dir(tmp) + local file = dir:next() + while file do + count = count + 1 + file = dir:next() + end + assert(not pcall(dir.next, dir)) +end + +io.write(".") +io.flush() + +-- directory explicit close +local iter, dir = lfs.dir(tmp) +dir:close() +assert(not pcall(dir.next, dir)) +print"Ok!"