]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/liblua/lfs.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / stand / liblua / lfs.c
1 /*-
2  * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * Portions derived from https://github.com/keplerproject/luafilesystem under
27  * the terms of the MIT license:
28  *
29  * Copyright (c) 2003-2014 Kepler Project.
30  *
31  * Permission is hereby granted, free of charge, to any person
32  * obtaining a copy of this software and associated documentation
33  * files (the "Software"), to deal in the Software without
34  * restriction, including without limitation the rights to use, copy,
35  * modify, merge, publish, distribute, sublicense, and/or sell copies
36  * of the Software, and to permit persons to whom the Software is
37  * furnished to do so, subject to the following conditions:
38  *
39  * The above copyright notice and this permission notice shall be
40  * included in all copies or substantial portions of the Software.
41  *
42  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
45  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
46  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
47  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
48  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
49  * SOFTWARE.
50  */
51
52 #include <sys/cdefs.h>
53 __FBSDID("$FreeBSD$");
54
55 #include <lua.h>
56 #include "lauxlib.h"
57 #include "lfs.h"
58 #include "lstd.h"
59 #include "lutils.h"
60 #include "bootstrap.h"
61
62 #ifndef nitems
63 #define nitems(x)       (sizeof((x)) / sizeof((x)[0]))
64 #endif
65
66 /*
67  * The goal is to emulate a subset of the upstream Lua FileSystem library, as
68  * faithfully as possible in the boot environment.  Only APIs that seem useful
69  * need to emulated.
70  *
71  * Example usage:
72  *
73  *     for file in lfs.dir("/boot") do
74  *         print("\t"..file)
75  *     end
76  *
77  * Prints:
78  *     .
79  *     ..
80  * (etc.)
81  *
82  * The other available API is lfs.attributes(), which functions somewhat like
83  * stat(2) and returns a table of values.  Example code:
84  *
85  *     attrs, errormsg, errorcode = lfs.attributes("/boot")
86  *     if attrs == nil then
87  *         print(errormsg)
88  *         return errorcode
89  *     end
90  *
91  *     for k, v in pairs(attrs) do
92  *         print(k .. ":\t" .. v)
93  *     end
94  *     return 0
95  *
96  * Prints (on success):
97  *     gid:    0
98  *     change: 140737488342640
99  *     mode:   directory
100  *     rdev:   0
101  *     ino:    4199275
102  *     dev:    140737488342544
103  *     modification:   140737488342576
104  *     size:   512
105  *     access: 140737488342560
106  *     permissions:    755
107  *     nlink:  58283552
108  *     uid:    1001
109  */
110
111 #define DIR_METATABLE "directory iterator metatable"
112
113 static int
114 lua_dir_iter_next(lua_State *L)
115 {
116         struct dirent *entry;
117         DIR *dp, **dpp;
118
119         dpp = (DIR **)luaL_checkudata(L, 1, DIR_METATABLE);
120         dp = *dpp;
121         luaL_argcheck(L, dp != NULL, 1, "closed directory");
122
123         entry = readdirfd(dp->fd);
124         if (entry == NULL) {
125                 closedir(dp);
126                 *dpp = NULL;
127                 return 0;
128         }
129
130         lua_pushstring(L, entry->d_name);
131         return 1;
132 }
133
134 static int
135 lua_dir_iter_close(lua_State *L)
136 {
137         DIR *dp, **dpp;
138
139         dpp = (DIR **)lua_touserdata(L, 1);
140         dp = *dpp;
141         if (dp == NULL)
142                 return 0;
143
144         closedir(dp);
145         *dpp = NULL;
146         return 0;
147 }
148
149 static int
150 lua_dir(lua_State *L)
151 {
152         const char *path;
153         DIR *dp;
154
155         if (lua_gettop(L) != 1) {
156                 lua_pushnil(L);
157                 return 1;
158         }
159
160         path = luaL_checkstring(L, 1);
161         dp = opendir(path);
162         if (dp == NULL) {
163                 lua_pushnil(L);
164                 return 1;
165         }
166
167         lua_pushcfunction(L, lua_dir_iter_next);
168         *(DIR **)lua_newuserdata(L, sizeof(DIR **)) = dp;
169         luaL_getmetatable(L, DIR_METATABLE);
170         lua_setmetatable(L, -2);
171         return 2;
172 }
173
174 static void
175 register_metatable(lua_State *L)
176 {
177         /*
178          * Create so-called metatable for iterator object returned by
179          * lfs.dir().
180          */
181         luaL_newmetatable(L, DIR_METATABLE);
182
183         lua_newtable(L);
184         lua_pushcfunction(L, lua_dir_iter_next);
185         lua_setfield(L, -2, "next");
186         lua_pushcfunction(L, lua_dir_iter_close);
187         lua_setfield(L, -2, "close");
188
189         /* Magically associate anonymous method table with metatable. */
190         lua_setfield(L, -2, "__index");
191         /* Implement magic destructor method */
192         lua_pushcfunction(L, lua_dir_iter_close);
193         lua_setfield(L, -2, "__gc");
194
195         lua_pop(L, 1);
196 }
197
198 #define PUSH_INTEGER(lname, stname)                             \
199 static void                                                     \
200 push_st_ ## lname (lua_State *L, struct stat *sb)               \
201 {                                                               \
202         lua_pushinteger(L, (lua_Integer)sb->st_ ## stname);     \
203 }
204 PUSH_INTEGER(dev, dev)
205 PUSH_INTEGER(ino, ino)
206 PUSH_INTEGER(nlink, nlink)
207 PUSH_INTEGER(uid, uid)
208 PUSH_INTEGER(gid, gid)
209 PUSH_INTEGER(rdev, rdev)
210 PUSH_INTEGER(access, atime)
211 PUSH_INTEGER(modification, mtime)
212 PUSH_INTEGER(change, ctime)
213 PUSH_INTEGER(size, size)
214 #undef PUSH_INTEGER
215
216 static void
217 push_st_mode(lua_State *L, struct stat *sb)
218 {
219         const char *mode_s;
220         mode_t mode;
221
222         mode = (sb->st_mode & S_IFMT);
223         if (S_ISREG(mode))
224                 mode_s = "file";
225         else if (S_ISDIR(mode))
226                 mode_s = "directory";
227         else if (S_ISLNK(mode))
228                 mode_s = "link";
229         else if (S_ISSOCK(mode))
230                 mode_s = "socket";
231         else if (S_ISFIFO(mode))
232                 mode_s = "fifo";
233         else if (S_ISCHR(mode))
234                 mode_s = "char device";
235         else if (S_ISBLK(mode))
236                 mode_s = "block device";
237         else
238                 mode_s = "other";
239
240         lua_pushstring(L, mode_s);
241 }
242
243 static void
244 push_st_permissions(lua_State *L, struct stat *sb)
245 {
246         char buf[20];
247
248         /*
249          * XXX
250          * Could actually format as "-rwxrwxrwx" -- do we care?
251          */
252         snprintf(buf, sizeof(buf), "%o", sb->st_mode & ~S_IFMT);
253         lua_pushstring(L, buf);
254 }
255
256 #define PUSH_ENTRY(n)   { #n, push_st_ ## n }
257 struct stat_members {
258         const char *name;
259         void (*push)(lua_State *, struct stat *);
260 } members[] = {
261         PUSH_ENTRY(mode),
262         PUSH_ENTRY(dev),
263         PUSH_ENTRY(ino),
264         PUSH_ENTRY(nlink),
265         PUSH_ENTRY(uid),
266         PUSH_ENTRY(gid),
267         PUSH_ENTRY(rdev),
268         PUSH_ENTRY(access),
269         PUSH_ENTRY(modification),
270         PUSH_ENTRY(change),
271         PUSH_ENTRY(size),
272         PUSH_ENTRY(permissions),
273 };
274 #undef PUSH_ENTRY
275
276 static int
277 lua_attributes(lua_State *L)
278 {
279         struct stat sb;
280         const char *path, *member;
281         size_t i;
282         int rc;
283
284         path = luaL_checkstring(L, 1);
285         if (path == NULL) {
286                 lua_pushnil(L);
287                 lua_pushfstring(L, "cannot convert first argument to string");
288                 lua_pushinteger(L, EINVAL);
289                 return 3;
290         }
291
292         rc = stat(path, &sb);
293         if (rc != 0) {
294                 lua_pushnil(L);
295                 lua_pushfstring(L,
296                     "cannot obtain information from file '%s': %s", path,
297                     strerror(errno));
298                 lua_pushinteger(L, errno);
299                 return 3;
300         }
301
302         if (lua_isstring(L, 2)) {
303                 member = lua_tostring(L, 2);
304                 for (i = 0; i < nitems(members); i++) {
305                         if (strcmp(members[i].name, member) != 0)
306                                 continue;
307
308                         members[i].push(L, &sb);
309                         return 1;
310                 }
311                 return luaL_error(L, "invalid attribute name '%s'", member);
312         }
313
314         /* Create or reuse existing table */
315         lua_settop(L, 2);
316         if (!lua_istable(L, 2))
317                 lua_newtable(L);
318
319         /* Export all stat data to caller */
320         for (i = 0; i < nitems(members); i++) {
321                 lua_pushstring(L, members[i].name);
322                 members[i].push(L, &sb);
323                 lua_rawset(L, -3);
324         }
325         return 1;
326 }
327
328 #define REG_SIMPLE(n)   { #n, lua_ ## n }
329 static const struct luaL_Reg fslib[] = {
330         REG_SIMPLE(attributes),
331         REG_SIMPLE(dir),
332         { NULL, NULL },
333 };
334 #undef REG_SIMPLE
335
336 int
337 luaopen_lfs(lua_State *L)
338 {
339         register_metatable(L);
340         luaL_newlib(L, fslib);
341         return 1;
342 }