mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

lfs.cc (26661B)


      1 /*
      2 ** LuaFileSystem
      3 ** Copyright Kepler Project 2003 - 2017 (http://keplerproject.github.io/luafilesystem)
      4 **
      5 ** File system manipulation library.
      6 ** This library offers these functions:
      7 **   lfs.attributes (filepath [, attributename | attributetable])
      8 **   lfs.chdir (path)
      9 **   lfs.currentdir ()
     10 **   lfs.dir (path)
     11 **   lfs.link (old, new[, symlink])
     12 **   lfs.lock (fh, mode)
     13 **   lfs.lock_dir (path)
     14 **   lfs.mkdir (path)
     15 **   lfs.rmdir (path)
     16 **   lfs.setmode (filepath, mode)
     17 **   lfs.symlinkattributes (filepath [, attributename])
     18 **   lfs.touch (filepath [, atime [, mtime]])
     19 **   lfs.unlock (fh)
     20 */
     21 
     22 #ifndef LFS_DO_NOT_USE_LARGE_FILE
     23 #ifndef _WIN32
     24 #ifndef _AIX
     25 #define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */
     26 #else
     27 #define _LARGE_FILES 1 /* AIX */
     28 #endif
     29 #endif
     30 #endif
     31 
     32 #ifndef LFS_DO_NOT_USE_LARGE_FILE
     33 #define _LARGEFILE64_SOURCE
     34 #endif
     35 
     36 #include <errno.h>
     37 #include <stdio.h>
     38 #include <string.h>
     39 #include <stdlib.h>
     40 #include <time.h>
     41 #include <sys/stat.h>
     42 
     43 #ifdef _WIN32
     44   #include <direct.h>
     45   #include <windows.h>
     46   #include <io.h>
     47   #include <sys/locking.h>
     48   #ifdef __BORLANDC__
     49     #include <utime.h>
     50   #else
     51     #include <sys/utime.h>
     52   #endif
     53   #include <fcntl.h>
     54   /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */
     55   #define LFS_MAXPATHLEN MAX_PATH
     56 #else
     57   #include <unistd.h>
     58   #include <dirent.h>
     59   #include <fcntl.h>
     60   #include <sys/types.h>
     61   #include <utime.h>
     62   #include <sys/param.h> /* for MAXPATHLEN */
     63   #define LFS_MAXPATHLEN MAXPATHLEN
     64 #endif
     65 
     66 #include <lua.h>
     67 #include <lauxlib.h>
     68 #include <lualib.h>
     69 
     70 #include "lfs.h"
     71 
     72 #define LFS_VERSION "1.7.0"
     73 #define LFS_LIBNAME "lfs"
     74 
     75 #if LUA_VERSION_NUM >= 503 /* Lua 5.3 */
     76 
     77 #ifndef luaL_optlong
     78 #define luaL_optlong luaL_optinteger
     79 #endif
     80 
     81 #endif
     82 
     83 #if LUA_VERSION_NUM >= 502
     84 #  define new_lib(L, l) (luaL_newlib(L, l))
     85 #else
     86 #  define new_lib(L, l) (lua_newtable(L), luaL_register(L, NULL, l))
     87 #endif
     88 
     89 /* Define 'strerror' for systems that do not implement it */
     90 #ifdef NO_STRERROR
     91 #define strerror(_)     "System unable to describe the error"
     92 #endif
     93 
     94 #define DIR_METATABLE "directory metatable"
     95 typedef struct dir_data {
     96         int  closed;
     97 #ifdef _WIN32
     98         intptr_t hFile;
     99         char pattern[MAX_PATH+1];
    100 #else
    101         DIR *dir;
    102 #endif
    103 } dir_data;
    104 
    105 #define LOCK_METATABLE "lock metatable"
    106 
    107 #ifdef _WIN32
    108  #ifdef __BORLANDC__
    109   #define lfs_setmode(file, m)   (setmode(_fileno(file), m))
    110   #define STAT_STRUCT struct stati64
    111  #else
    112   #define lfs_setmode(file, m)   (_setmode(_fileno(file), m))
    113   #define STAT_STRUCT struct _stati64
    114  #endif
    115 #define STAT_FUNC _stati64
    116 #define LSTAT_FUNC STAT_FUNC
    117 #else
    118 #define _O_TEXT               0
    119 #define _O_BINARY             0
    120 #define lfs_setmode(file, m)   ((void)file, (void)m, 0)
    121 #define STAT_STRUCT struct stat
    122 #define STAT_FUNC stat
    123 #define LSTAT_FUNC lstat
    124 #endif
    125 
    126 #ifdef _WIN32
    127   #define lfs_mkdir _mkdir
    128 #else
    129   #define lfs_mkdir(path) (mkdir((path), \
    130     S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH))
    131 #endif
    132 
    133 /*
    134 ** Utility functions
    135 */
    136 static int pusherror(lua_State *L, const char *info)
    137 {
    138         lua_pushnil(L);
    139         if (info==NULL)
    140                 lua_pushstring(L, strerror(errno));
    141         else
    142                 lua_pushfstring(L, "%s: %s", info, strerror(errno));
    143         lua_pushinteger(L, errno);
    144         return 3;
    145 }
    146 
    147 static int pushresult(lua_State *L, int res, const char *info) {
    148   if (res == -1) {
    149     return pusherror(L, info);
    150   } else {
    151     lua_pushboolean(L, 1);
    152     return 1;
    153   }
    154 }
    155 
    156 
    157 /*
    158 ** This function changes the working (current) directory
    159 */
    160 static int change_dir (lua_State *L) {
    161         const char *path = luaL_checkstring(L, 1);
    162         if (chdir(path)) {
    163                 lua_pushnil (L);
    164                 lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n",
    165                                 path, chdir_error);
    166                 return 2;
    167         } else {
    168                 lua_pushboolean (L, 1);
    169                 return 1;
    170         }
    171 }
    172 
    173 /*
    174 ** This function returns the current directory
    175 ** If unable to get the current directory, it returns nil
    176 **  and a string describing the error
    177 */
    178 static int get_dir (lua_State *L) {
    179 #ifdef NO_GETCWD
    180     lua_pushnil(L);
    181     lua_pushstring(L, "Function 'getcwd' not provided by system");
    182     return 2;
    183 #else
    184     char *path = NULL;
    185     /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */
    186     size_t size = LFS_MAXPATHLEN; /* initial buffer size */
    187     int result;
    188     while (1) {
    189         path = realloc(path, size);
    190         if (!path) /* failed to allocate */
    191             return pusherror(L, "get_dir realloc() failed");
    192         if (getcwd(path, size) != NULL) {
    193             /* success, push the path to the Lua stack */
    194             lua_pushstring(L, path);
    195             result = 1;
    196             break;
    197         }
    198         if (errno != ERANGE) { /* unexpected error */
    199             result = pusherror(L, "get_dir getcwd() failed");
    200             break;
    201         }
    202         /* ERANGE = insufficient buffer capacity, double size and retry */
    203         size *= 2;
    204     }
    205     free(path);
    206     return result;
    207 #endif
    208 }
    209 
    210 /*
    211 ** Check if the given element on the stack is a file and returns it.
    212 */
    213 static FILE *check_file (lua_State *L, int idx, const char *funcname) {
    214 #if LUA_VERSION_NUM == 501
    215         FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*");
    216         if (*fh == NULL) {
    217                 luaL_error (L, "%s: closed file", funcname);
    218                 return 0;
    219         } else
    220                 return *fh;
    221 #elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503
    222         luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*");
    223         if (fh->closef == 0 || fh->f == NULL) {
    224                 luaL_error (L, "%s: closed file", funcname);
    225                 return 0;
    226         } else
    227                 return fh->f;
    228 #else
    229 #error unsupported Lua version
    230 #endif
    231 }
    232 
    233 
    234 /*
    235 **
    236 */
    237 static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) {
    238         int code;
    239 #ifdef _WIN32
    240         /* lkmode valid values are:
    241            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.
    242            LK_NBLCK   Locks the specified bytes. If the bytes cannot be locked, the constant returns an error.
    243            LK_NBRLCK  Same as _LK_NBLCK.
    244            LK_RLCK    Same as _LK_LOCK.
    245            LK_UNLCK   Unlocks the specified bytes, which must have been previously locked.
    246 
    247            Regions should be locked only briefly and should be unlocked before closing a file or exiting the program.
    248 
    249            http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp
    250         */
    251         int lkmode;
    252         switch (*mode) {
    253                 case 'r': lkmode = LK_NBLCK; break;
    254                 case 'w': lkmode = LK_NBLCK; break;
    255                 case 'u': lkmode = LK_UNLCK; break;
    256                 default : return luaL_error (L, "%s: invalid mode", funcname);
    257         }
    258         if (!len) {
    259                 fseek (fh, 0L, SEEK_END);
    260                 len = ftell (fh);
    261         }
    262         fseek (fh, start, SEEK_SET);
    263 #ifdef __BORLANDC__
    264         code = locking (fileno(fh), lkmode, len);
    265 #else
    266         code = _locking (fileno(fh), lkmode, len);
    267 #endif
    268 #else
    269         struct flock f;
    270         switch (*mode) {
    271                 case 'w': f.l_type = F_WRLCK; break;
    272                 case 'r': f.l_type = F_RDLCK; break;
    273                 case 'u': f.l_type = F_UNLCK; break;
    274                 default : return luaL_error (L, "%s: invalid mode", funcname);
    275         }
    276         f.l_whence = SEEK_SET;
    277         f.l_start = (off_t)start;
    278         f.l_len = (off_t)len;
    279         code = fcntl (fileno(fh), F_SETLK, &f);
    280 #endif
    281         return (code != -1);
    282 }
    283 
    284 #ifdef _WIN32
    285 typedef struct lfs_Lock {
    286   HANDLE fd;
    287 } lfs_Lock;
    288 static int lfs_lock_dir(lua_State *L) {
    289   size_t pathl; HANDLE fd;
    290   lfs_Lock *lock;
    291   char *ln;
    292   const char *lockfile = "/lockfile.lfs";
    293   const char *path = luaL_checklstring(L, 1, &pathl);
    294   ln = (char*)malloc(pathl + strlen(lockfile) + 1);
    295   if(!ln) {
    296     lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;
    297   }
    298   strcpy(ln, path); strcat(ln, lockfile);
    299   if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW,
    300                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) {
    301         int en = GetLastError();
    302         free(ln); lua_pushnil(L);
    303         if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION)
    304                 lua_pushstring(L, "File exists");
    305         else
    306                 lua_pushstring(L, strerror(en));
    307         return 2;
    308   }
    309   free(ln);
    310   lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));
    311   lock->fd = fd;
    312   luaL_getmetatable (L, LOCK_METATABLE);
    313   lua_setmetatable (L, -2);
    314   return 1;
    315 }
    316 static int lfs_unlock_dir(lua_State *L) {
    317   lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE);
    318   if(lock->fd != INVALID_HANDLE_VALUE) {    
    319     CloseHandle(lock->fd);
    320     lock->fd=INVALID_HANDLE_VALUE;
    321   }
    322   return 0;
    323 }
    324 #else
    325 typedef struct lfs_Lock {
    326   char *ln;
    327 } lfs_Lock;
    328 static int lfs_lock_dir(lua_State *L) {
    329   lfs_Lock *lock;
    330   size_t pathl;
    331   char *ln;
    332   const char *lockfile = "/lockfile.lfs";
    333   const char *path = luaL_checklstring(L, 1, &pathl);
    334   lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));
    335   ln = (char*)malloc(pathl + strlen(lockfile) + 1);
    336   if(!ln) {
    337     lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;
    338   }
    339   strcpy(ln, path); strcat(ln, lockfile);
    340   if(symlink("lock", ln) == -1) {
    341     free(ln); lua_pushnil(L);
    342     lua_pushstring(L, strerror(errno)); return 2;
    343   }
    344   lock->ln = ln;
    345   luaL_getmetatable (L, LOCK_METATABLE);
    346   lua_setmetatable (L, -2);
    347   return 1;
    348 }
    349 static int lfs_unlock_dir(lua_State *L) {
    350   lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE);
    351   if(lock->ln) {
    352     unlink(lock->ln);
    353     free(lock->ln);
    354     lock->ln = NULL;
    355   }
    356   return 0;
    357 }
    358 #endif
    359 
    360 static int lfs_g_setmode (lua_State *L, FILE *f, int arg) {
    361   static const int mode[] = {_O_BINARY, _O_TEXT};
    362   static const char *const modenames[] = {"binary", "text", NULL};
    363   int op = luaL_checkoption(L, arg, NULL, modenames);
    364   int res = lfs_setmode(f, mode[op]);
    365   if (res != -1) {
    366     int i;
    367     lua_pushboolean(L, 1);
    368     for (i = 0; modenames[i] != NULL; i++) {
    369       if (mode[i] == res) {
    370         lua_pushstring(L, modenames[i]);
    371         return 2;
    372       }
    373     }
    374     lua_pushnil(L);
    375     return 2;
    376   } else {
    377     return pusherror(L, NULL);
    378   }
    379 }
    380 
    381 static int lfs_f_setmode(lua_State *L) {
    382   return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2);
    383 }
    384 
    385 /*
    386 ** Locks a file.
    387 ** @param #1 File handle.
    388 ** @param #2 String with lock mode ('w'rite, 'r'ead).
    389 ** @param #3 Number with start position (optional).
    390 ** @param #4 Number with length (optional).
    391 */
    392 static int file_lock (lua_State *L) {
    393         FILE *fh = check_file (L, 1, "lock");
    394         const char *mode = luaL_checkstring (L, 2);
    395         const long start = (long) luaL_optinteger (L, 3, 0);
    396         long len = (long) luaL_optinteger (L, 4, 0);
    397         if (_file_lock (L, fh, mode, start, len, "lock")) {
    398                 lua_pushboolean (L, 1);
    399                 return 1;
    400         } else {
    401                 lua_pushnil (L);
    402                 lua_pushfstring (L, "%s", strerror(errno));
    403                 return 2;
    404         }
    405 }
    406 
    407 
    408 /*
    409 ** Unlocks a file.
    410 ** @param #1 File handle.
    411 ** @param #2 Number with start position (optional).
    412 ** @param #3 Number with length (optional).
    413 */
    414 static int file_unlock (lua_State *L) {
    415         FILE *fh = check_file (L, 1, "unlock");
    416         const long start = (long) luaL_optinteger (L, 2, 0);
    417         long len = (long) luaL_optinteger (L, 3, 0);
    418         if (_file_lock (L, fh, "u", start, len, "unlock")) {
    419                 lua_pushboolean (L, 1);
    420                 return 1;
    421         } else {
    422                 lua_pushnil (L);
    423                 lua_pushfstring (L, "%s", strerror(errno));
    424                 return 2;
    425         }
    426 }
    427 
    428 
    429 /*
    430 ** Creates a link.
    431 ** @param #1 Object to link to.
    432 ** @param #2 Name of link.
    433 ** @param #3 True if link is symbolic (optional).
    434 */
    435 static int make_link (lua_State *L) {
    436 #ifndef _WIN32
    437   const char *oldpath = luaL_checkstring(L, 1);
    438   const char *newpath = luaL_checkstring(L, 2);
    439   int res = (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath);
    440   if (res == -1) {
    441     return pusherror(L, NULL);
    442   } else {
    443     lua_pushinteger(L, 0);
    444     return 1;
    445   }
    446 #else
    447   errno = ENOSYS; /* = "Function not implemented" */
    448   return pushresult(L, -1, "make_link is not supported on Windows");
    449 #endif
    450 }
    451 
    452 
    453 /*
    454 ** Creates a directory.
    455 ** @param #1 Directory path.
    456 */
    457 static int make_dir (lua_State *L) {
    458   const char *path = luaL_checkstring(L, 1);
    459   return pushresult(L, lfs_mkdir(path), NULL);
    460 }
    461 
    462 
    463 /*
    464 ** Removes a directory.
    465 ** @param #1 Directory path.
    466 */
    467 static int remove_dir (lua_State *L) {
    468   const char *path = luaL_checkstring(L, 1);
    469   return pushresult(L, rmdir(path), NULL);
    470 }
    471 
    472 
    473 /*
    474 ** Directory iterator
    475 */
    476 static int dir_iter (lua_State *L) {
    477 #ifdef _WIN32
    478         struct _finddata_t c_file;
    479 #else
    480         struct dirent *entry;
    481 #endif
    482         dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE);
    483         luaL_argcheck (L, d->closed == 0, 1, "closed directory");
    484 #ifdef _WIN32
    485         if (d->hFile == 0L) { /* first entry */
    486                 if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) {
    487                         lua_pushnil (L);
    488                         lua_pushstring (L, strerror (errno));
    489                         d->closed = 1;
    490                         return 2;
    491                 } else {
    492                         lua_pushstring (L, c_file.name);
    493                         return 1;
    494                 }
    495         } else { /* next entry */
    496                 if (_findnext (d->hFile, &c_file) == -1L) {
    497                         /* no more entries => close directory */
    498                         _findclose (d->hFile);
    499                         d->closed = 1;
    500                         return 0;
    501                 } else {
    502                         lua_pushstring (L, c_file.name);
    503                         return 1;
    504                 }
    505         }
    506 #else
    507         if ((entry = readdir (d->dir)) != NULL) {
    508                 lua_pushstring (L, entry->d_name);
    509                 return 1;
    510         } else {
    511                 /* no more entries => close directory */
    512                 closedir (d->dir);
    513                 d->closed = 1;
    514                 return 0;
    515         }
    516 #endif
    517 }
    518 
    519 
    520 /*
    521 ** Closes directory iterators
    522 */
    523 static int dir_close (lua_State *L) {
    524         dir_data *d = (dir_data *)lua_touserdata (L, 1);
    525 #ifdef _WIN32
    526         if (!d->closed && d->hFile) {
    527                 _findclose (d->hFile);
    528         }
    529 #else
    530         if (!d->closed && d->dir) {
    531                 closedir (d->dir);
    532         }
    533 #endif
    534         d->closed = 1;
    535         return 0;
    536 }
    537 
    538 
    539 /*
    540 ** Factory of directory iterators
    541 */
    542 static int dir_iter_factory (lua_State *L) {
    543         const char *path = luaL_checkstring (L, 1);
    544         dir_data *d;
    545         lua_pushcfunction (L, dir_iter);
    546         d = (dir_data *) lua_newuserdata (L, sizeof(dir_data));
    547         luaL_getmetatable (L, DIR_METATABLE);
    548         lua_setmetatable (L, -2);
    549         d->closed = 0;
    550 #ifdef _WIN32
    551         d->hFile = 0L;
    552         if (strlen(path) > MAX_PATH-2)
    553           luaL_error (L, "path too long: %s", path);
    554         else
    555           sprintf (d->pattern, "%s/*", path);
    556 #else
    557         d->dir = opendir (path);
    558         if (d->dir == NULL)
    559           luaL_error (L, "cannot open %s: %s", path, strerror (errno));
    560 #endif
    561         return 2;
    562 }
    563 
    564 
    565 /*
    566 ** Creates directory metatable.
    567 */
    568 static int dir_create_meta (lua_State *L) {
    569         luaL_newmetatable (L, DIR_METATABLE);
    570 
    571         /* Method table */
    572         lua_newtable(L);
    573         lua_pushcfunction (L, dir_iter);
    574         lua_setfield(L, -2, "next");
    575         lua_pushcfunction (L, dir_close);
    576         lua_setfield(L, -2, "close");
    577 
    578         /* Metamethods */
    579         lua_setfield(L, -2, "__index");
    580         lua_pushcfunction (L, dir_close);
    581         lua_setfield (L, -2, "__gc");
    582         return 1;
    583 }
    584 
    585 
    586 /*
    587 ** Creates lock metatable.
    588 */
    589 static int lock_create_meta (lua_State *L) {
    590         luaL_newmetatable (L, LOCK_METATABLE);
    591 
    592         /* Method table */
    593         lua_newtable(L);
    594         lua_pushcfunction(L, lfs_unlock_dir);
    595         lua_setfield(L, -2, "free");
    596 
    597         /* Metamethods */
    598         lua_setfield(L, -2, "__index");
    599         lua_pushcfunction(L, lfs_unlock_dir);
    600         lua_setfield(L, -2, "__gc");
    601         return 1;
    602 }
    603 
    604 
    605 #ifdef _WIN32
    606  #ifndef S_ISDIR
    607    #define S_ISDIR(mode)  (mode&_S_IFDIR)
    608  #endif
    609  #ifndef S_ISREG
    610    #define S_ISREG(mode)  (mode&_S_IFREG)
    611  #endif
    612  #ifndef S_ISLNK
    613    #define S_ISLNK(mode)  (0)
    614  #endif
    615  #ifndef S_ISSOCK
    616    #define S_ISSOCK(mode)  (0)
    617  #endif
    618  #ifndef S_ISFIFO
    619    #define S_ISFIFO(mode)  (0)
    620  #endif
    621  #ifndef S_ISCHR
    622    #define S_ISCHR(mode)  (mode&_S_IFCHR)
    623  #endif
    624  #ifndef S_ISBLK
    625    #define S_ISBLK(mode)  (0)
    626  #endif
    627 #endif
    628 /*
    629 ** Convert the inode protection mode to a string.
    630 */
    631 #ifdef _WIN32
    632 static const char *mode2string (unsigned short mode) {
    633 #else
    634 static const char *mode2string (mode_t mode) {
    635 #endif
    636   if ( S_ISREG(mode) )
    637     return "file";
    638   else if ( S_ISDIR(mode) )
    639     return "directory";
    640   else if ( S_ISLNK(mode) )
    641         return "link";
    642   else if ( S_ISSOCK(mode) )
    643     return "socket";
    644   else if ( S_ISFIFO(mode) )
    645         return "named pipe";
    646   else if ( S_ISCHR(mode) )
    647         return "char device";
    648   else if ( S_ISBLK(mode) )
    649         return "block device";
    650   else
    651         return "other";
    652 }
    653 
    654 
    655 /*
    656 ** Set access time and modification values for a file.
    657 ** @param #1 File path.
    658 ** @param #2 Access time in seconds, current time is used if missing.
    659 ** @param #3 Modification time in seconds, access time is used if missing.
    660 */
    661 static int file_utime (lua_State *L) {
    662   const char *file = luaL_checkstring(L, 1);
    663   struct utimbuf utb, *buf;
    664 
    665   if (lua_gettop (L) == 1) /* set to current date/time */
    666     buf = NULL;
    667   else {
    668     utb.actime = (time_t) luaL_optnumber(L, 2, 0);
    669     utb.modtime = (time_t) luaL_optinteger(L, 3, utb.actime);
    670     buf = &utb;
    671   }
    672 
    673   return pushresult(L, utime(file, buf), NULL);
    674 }
    675 
    676 
    677 /* inode protection mode */
    678 static void push_st_mode (lua_State *L, STAT_STRUCT *info) {
    679         lua_pushstring (L, mode2string (info->st_mode));
    680 }
    681 /* device inode resides on */
    682 static void push_st_dev (lua_State *L, STAT_STRUCT *info) {
    683         lua_pushinteger (L, (lua_Integer) info->st_dev);
    684 }
    685 /* inode's number */
    686 static void push_st_ino (lua_State *L, STAT_STRUCT *info) {
    687         lua_pushinteger (L, (lua_Integer) info->st_ino);
    688 }
    689 /* number of hard links to the file */
    690 static void push_st_nlink (lua_State *L, STAT_STRUCT *info) {
    691         lua_pushinteger (L, (lua_Integer)info->st_nlink);
    692 }
    693 /* user-id of owner */
    694 static void push_st_uid (lua_State *L, STAT_STRUCT *info) {
    695         lua_pushinteger (L, (lua_Integer)info->st_uid);
    696 }
    697 /* group-id of owner */
    698 static void push_st_gid (lua_State *L, STAT_STRUCT *info) {
    699         lua_pushinteger (L, (lua_Integer)info->st_gid);
    700 }
    701 /* device type, for special file inode */
    702 static void push_st_rdev (lua_State *L, STAT_STRUCT *info) {
    703         lua_pushinteger (L, (lua_Integer) info->st_rdev);
    704 }
    705 /* time of last access */
    706 static void push_st_atime (lua_State *L, STAT_STRUCT *info) {
    707         lua_pushinteger (L, (lua_Integer) info->st_atime);
    708 }
    709 /* time of last data modification */
    710 static void push_st_mtime (lua_State *L, STAT_STRUCT *info) {
    711         lua_pushinteger (L, (lua_Integer) info->st_mtime);
    712 }
    713 /* time of last file status change */
    714 static void push_st_ctime (lua_State *L, STAT_STRUCT *info) {
    715         lua_pushinteger (L, (lua_Integer) info->st_ctime);
    716 }
    717 /* file size, in bytes */
    718 static void push_st_size (lua_State *L, STAT_STRUCT *info) {
    719         lua_pushinteger (L, (lua_Integer)info->st_size);
    720 }
    721 #ifndef _WIN32
    722 /* blocks allocated for file */
    723 static void push_st_blocks (lua_State *L, STAT_STRUCT *info) {
    724         lua_pushinteger (L, (lua_Integer)info->st_blocks);
    725 }
    726 /* optimal file system I/O blocksize */
    727 static void push_st_blksize (lua_State *L, STAT_STRUCT *info) {
    728         lua_pushinteger (L, (lua_Integer)info->st_blksize);
    729 }
    730 #endif
    731 
    732  /*
    733 ** Convert the inode protection mode to a permission list.
    734 */
    735 
    736 #ifdef _WIN32
    737 static const char *perm2string (unsigned short mode) {
    738   static char perms[10] = "---------";
    739   int i;
    740   for (i=0;i<9;i++) perms[i]='-';
    741   if (mode  & _S_IREAD)
    742    { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; }
    743   if (mode  & _S_IWRITE)
    744    { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; }
    745   if (mode  & _S_IEXEC)
    746    { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; }
    747   return perms;
    748 }
    749 #else
    750 static const char *perm2string (mode_t mode) {
    751   static char perms[10] = "---------";
    752   int i;
    753   for (i=0;i<9;i++) perms[i]='-';
    754   if (mode & S_IRUSR) perms[0] = 'r';
    755   if (mode & S_IWUSR) perms[1] = 'w';
    756   if (mode & S_IXUSR) perms[2] = 'x';
    757   if (mode & S_IRGRP) perms[3] = 'r';
    758   if (mode & S_IWGRP) perms[4] = 'w';
    759   if (mode & S_IXGRP) perms[5] = 'x';
    760   if (mode & S_IROTH) perms[6] = 'r';
    761   if (mode & S_IWOTH) perms[7] = 'w';
    762   if (mode & S_IXOTH) perms[8] = 'x';
    763   return perms;
    764 }
    765 #endif
    766 
    767 /* permssions string */
    768 static void push_st_perm (lua_State *L, STAT_STRUCT *info) {
    769     lua_pushstring (L, perm2string (info->st_mode));
    770 }
    771 
    772 typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info);
    773 
    774 struct _stat_members {
    775         const char *name;
    776         _push_function push;
    777 };
    778 
    779 struct _stat_members members[] = {
    780         { "mode",         push_st_mode },
    781         { "dev",          push_st_dev },
    782         { "ino",          push_st_ino },
    783         { "nlink",        push_st_nlink },
    784         { "uid",          push_st_uid },
    785         { "gid",          push_st_gid },
    786         { "rdev",         push_st_rdev },
    787         { "access",       push_st_atime },
    788         { "modification", push_st_mtime },
    789         { "change",       push_st_ctime },
    790         { "size",         push_st_size },
    791         { "permissions",  push_st_perm },
    792 #ifndef _WIN32
    793         { "blocks",       push_st_blocks },
    794         { "blksize",      push_st_blksize },
    795 #endif
    796         { NULL, NULL }
    797 };
    798 
    799 /*
    800 ** Get file or symbolic link information
    801 */
    802 static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {
    803         STAT_STRUCT info;
    804         const char *file = luaL_checkstring (L, 1);
    805         int i;
    806 
    807         if (st(file, &info)) {
    808                 lua_pushnil(L);
    809                 lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno));
    810                 lua_pushinteger(L, errno);
    811                 return 3;
    812         }
    813         if (lua_isstring (L, 2)) {
    814                 const char *member = lua_tostring (L, 2);
    815                 for (i = 0; members[i].name; i++) {
    816                         if (strcmp(members[i].name, member) == 0) {
    817                                 /* push member value and return */
    818                                 members[i].push (L, &info);
    819                                 return 1;
    820                         }
    821                 }
    822                 /* member not found */
    823                 return luaL_error(L, "invalid attribute name '%s'", member);
    824         }
    825         /* creates a table if none is given, removes extra arguments */
    826         lua_settop(L, 2);
    827         if (!lua_istable (L, 2)) {
    828                 lua_newtable (L);
    829         }
    830         /* stores all members in table on top of the stack */
    831         for (i = 0; members[i].name; i++) {
    832                 lua_pushstring (L, members[i].name);
    833                 members[i].push (L, &info);
    834                 lua_rawset (L, -3);
    835         }
    836         return 1;
    837 }
    838 
    839 
    840 /*
    841 ** Get file information using stat.
    842 */
    843 static int file_info (lua_State *L) {
    844         return _file_info_ (L, STAT_FUNC);
    845 }
    846 
    847 
    848 /*
    849 ** Push the symlink target to the top of the stack.
    850 ** Assumes the file name is at position 1 of the stack.
    851 ** Returns 1 if successful (with the target on top of the stack),
    852 ** 0 on failure (with stack unchanged, and errno set).
    853 */
    854 static int push_link_target(lua_State *L) {
    855 #ifdef _WIN32
    856         errno = ENOSYS;
    857         return 0;
    858 #else
    859         const char *file = luaL_checkstring(L, 1);
    860         char *target = NULL;
    861         int tsize, size = 256; /* size = initial buffer capacity */
    862         while (1) {
    863             target = realloc(target, size);
    864             if (!target) /* failed to allocate */
    865                 return 0;
    866             tsize = readlink(file, target, size);
    867             if (tsize < 0) { /* a readlink() error occurred */
    868                 free(target);
    869                 return 0;
    870             }
    871             if (tsize < size)
    872                 break;
    873             /* possibly truncated readlink() result, double size and retry */
    874             size *= 2;
    875         }
    876         target[tsize] = '\0';
    877         lua_pushlstring(L, target, tsize);
    878         free(target);
    879         return 1;
    880 #endif
    881 }
    882 
    883 /*
    884 ** Get symbolic link information using lstat.
    885 */
    886 static int link_info (lua_State *L) {
    887         int ret;
    888         if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) {
    889                 int ok = push_link_target(L);
    890                 return ok ? 1 : pusherror(L, "could not obtain link target");
    891         }
    892         ret = _file_info_ (L, LSTAT_FUNC);
    893         if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) {
    894                 int ok = push_link_target(L);
    895                 if (ok) {
    896                         lua_setfield(L, -2, "target");
    897                 }
    898         }
    899         return ret;
    900 }
    901 
    902 
    903 /*
    904 ** Assumes the table is on top of the stack.
    905 */
    906 static void set_info (lua_State *L) {
    907   lua_pushliteral(L, "Copyright (C) 2003-2017 Kepler Project");
    908   lua_setfield(L, -2, "_COPYRIGHT");
    909   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");
    910   lua_setfield(L, -2, "_DESCRIPTION");
    911   lua_pushliteral(L, "LuaFileSystem " LFS_VERSION);
    912   lua_setfield(L, -2, "_VERSION");
    913 }
    914 
    915 
    916 static const struct luaL_Reg fslib[] = {
    917         {"attributes", file_info},
    918         {"chdir", change_dir},
    919         {"currentdir", get_dir},
    920         {"dir", dir_iter_factory},
    921         {"link", make_link},
    922         {"lock", file_lock},
    923         {"mkdir", make_dir},
    924         {"rmdir", remove_dir},
    925         {"symlinkattributes", link_info},
    926         {"setmode", lfs_f_setmode},
    927         {"touch", file_utime},
    928         {"unlock", file_unlock},
    929         {"lock_dir", lfs_lock_dir},
    930         {NULL, NULL},
    931 };
    932 
    933 LFS_EXPORT int luaopen_lfs (lua_State *L) {
    934         dir_create_meta (L);
    935         lock_create_meta (L);
    936         new_lib (L, fslib);
    937         lua_pushvalue(L, -1);
    938         lua_setglobal(L, LFS_LIBNAME);
    939         set_info (L);
    940         return 1;
    941 }