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