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