/* ** File system manipulation library. ** This library offers these functions: ** luafilesystem.attributes (filepath [, attributename]) ** luafilesystem.chdir (path) ** luafilesystem.currentdir () ** luafilesystem.dir (path) ** luafilesystem.mkdir (path) ** luafilesystem.lock (fh, mode) ** luafilesystem.unlock (fh) ** ** $Id: luafilesystem.c,v 1.1.1.1 2004/07/27 14:15:24 tomas Exp $ */ #include #include #include #include #ifdef WIN32 #include #include #include #else #include #include #include #include #include #endif #include #include #include #include "luafilesystem.h" /* Define 'strerror' for systems that do not implement it */ #ifdef NO_STRERROR #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) #endif #define DIR_METATABLE "directory metatable" #define MAX_DIR_LENGTH 1023 #ifdef _WIN32 typedef struct dir_data { long hFile; char pattern[MAX_DIR_LENGTH+1]; } dir_data; #endif /* ** This function changes the working (current) directory */ static int change_dir (lua_State *L) { const char *path = luaL_checkstring(L, 1); if (chdir(path)) luaL_error(L,"Unable to change working directory to '%s'\n%s\n", path, chdir_error); return 0; } /* ** 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[255+2]; if (getcwd(path, 255) == NULL) { lua_pushnil(L); lua_pushstring(L, getcwd_error); return 2; } else { lua_pushstring(L, path); return 1; } } /* ** 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) { 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) { luaL_error (L, "%s: closed file", funcname); return 0; } else return *fh; } /* ** */ static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { int code; #ifdef WIN32 /* 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_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. _LK_NBRLCK Same as _LK_NBLCK. _LK_RLCK Same as _LK_LOCK. _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. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp */ int lkmode; switch (*mode) { case 'r': lkmode = _LK_NBLCK; break; case 'w': lkmode = _LK_NBLCK; break; case 'u': lkmode = _LK_UNLCK; break; default : luaL_error (L, "%s: invalid mode", funcname); } if (!len) { fseek (fh, 0L, SEEK_END); len = ftell (fh); } fseek (fh, start, SEEK_SET); code = _locking (fileno(fh), lkmode, len); #else struct flock f; switch (*mode) { case 'w': f.l_type = F_WRLCK; break; case 'r': f.l_type = F_RDLCK; break; case 'u': f.l_type = F_UNLCK; break; default : luaL_error (L, "%s: invalid mode", funcname); } f.l_whence = SEEK_SET; f.l_start = (off_t)start; f.l_len = (off_t)len; code = fcntl (fileno(fh), F_SETLK, &f); #endif return (code != -1); } /* ** Locks a file. ** @param #1 File handle. ** @param #2 String with lock mode ('w'rite, 'r'ead). ** @param #3 Number with start position (optional). ** @param #4 Number with length (optional). */ 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); if (_file_lock (L, fh, mode, start, len, "lock")) { lua_pushboolean (L, 1); return 1; } else { lua_pushboolean (L, 0); lua_pushfstring (L, "%s", strerror(errno)); return 2; } } /* ** Unlocks a file. ** @param #1 File handle. ** @param #2 Number with start position (optional). ** @param #3 Number with length (optional). */ 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); if (_file_lock (L, fh, "u", start, len, "unlock")) { lua_pushboolean (L, 1); return 1; } else { lua_pushboolean (L, 0); lua_pushfstring (L, "%s", strerror(errno)); return 2; } } /* static void cgilua_sleep( lua_State *L ) { unsigned int usec = (unsigned int)luaL_check_number( L, 1 ); #ifndef WIN32 sleep( (unsigned int)ceil( usec/1000.0 )); #else Sleep( (DWORD)usec ); #endif } static void cgilua_filesize( lua_State *L ) { struct stat info; char *file = luaL_check_string( L, 1 ); if (stat(file, &info)) { lua_pushnil( L ); lua_pushstring( L, "Cannot retrieve stat info from file" ); return; } lua_pushnumber(L, info.st_size); } */ static int make_dir (lua_State *L) { const char *path = luaL_checkstring (L, 1); int fail; #ifdef WIN32 int oldmask = umask (0); fail = _mkdir (path); #else mode_t oldmask = umask( (mode_t)0 ); fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH ); #endif lua_pushboolean (L, fail); umask (oldmask); return 1; } /* ** Directory iterator */ static int dir_iter (lua_State *L) { #ifdef _WIN32 dir_data *d = (dir_data *)lua_touserdata (L, lua_upvalueindex (1)); struct _finddata_t c_file; if (d->hFile == 0L) { /* first entry */ if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { lua_pushnil (L); lua_pushstring (L, strerror (errno)); return 2; } else { lua_pushstring (L, c_file.name); return 1; } } else { /* next entry */ if (_findnext (d->hFile, &c_file) == -1L) return 0; else { lua_pushstring (L, c_file.name); return 1; } } #else DIR *d = *(DIR **) lua_touserdata (L, lua_upvalueindex (1)); struct dirent *entry; if ((entry = readdir (d)) != NULL) { lua_pushstring (L, entry->d_name); return 1; } else return 0; #endif } /* ** Closes directory iterators */ static int dir_close (lua_State *L) { #ifdef _WIN32 dir_data *d = (dir_data *)lua_touserdata (L, 1); if (d->hFile) { _findclose (d->hFile); } #else DIR *d = *(DIR **)lua_touserdata (L, 1); if (d) closedir (d); #endif return 0; } /* ** Factory of directory iterators */ static int dir_iter_factory (lua_State *L) { const char *path = luaL_checkstring (L, 1); #ifdef _WIN32 dir_data *dir = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); dir->hFile = 0L; if (strlen(path) > MAX_DIR_LENGTH) luaL_error (L, "path too long: %s", path); else sprintf (dir->pattern, "%s/*", path); luaL_getmetatable (L, DIR_METATABLE); lua_setmetatable (L, -2); #else DIR **d = (DIR **) lua_newuserdata (L, sizeof(DIR *)); luaL_getmetatable (L, DIR_METATABLE); lua_setmetatable (L, -2); *d = opendir (path); if (*d == NULL) luaL_error (L, "cannot open %s: %s", path, strerror (errno)); #endif lua_pushcclosure (L, dir_iter, 1); return 1; } /* ** Creates directory metatable. */ static int dir_create_meta (lua_State *L) { luaL_newmetatable (L, DIR_METATABLE); /* set its __gc field */ lua_pushstring (L, "__gc"); lua_pushcfunction (L, dir_close); lua_settable (L, -3); return 1; } static const struct luaL_reg fslib[] = { {"chdir", change_dir}, {"currentdir", get_dir}, {"dir", dir_iter_factory}, {"lock", file_lock}, {"mkdir", make_dir}, {"unlock", file_unlock}, {NULL, NULL}, }; int luaopen_luafilesystem (lua_State *L) { dir_create_meta (L); luaL_openlib (L, "luafilesystem", fslib, 0); return 1; }