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