nixio:
[oweals/luci.git] / libs / nixio / src / file.c
1 /*
2  * nixio - Linux I/O library for lua
3  *
4  *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "nixio.h"
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/param.h>
29
30
31 static int nixio_open(lua_State *L) {
32         const char *filename = luaL_checklstring(L, 1, NULL);
33         int flags;
34
35         if (lua_isnoneornil(L, 2)) {
36                 flags = O_RDONLY;
37         } else if (lua_isnumber(L, 2)) {
38                 flags = lua_tointeger(L, 2);
39         } else if (lua_isstring(L, 2)) {
40                 const char *str = lua_tostring(L, 2);
41                 if (!strcmp(str, "r")) {
42                         flags = O_RDONLY;
43                 } else if (!strcmp(str, "r+")) {
44                         flags = O_RDWR;
45                 } else if (!strcmp(str, "w")) {
46                         flags = O_WRONLY | O_CREAT | O_TRUNC;
47                 } else if (!strcmp(str, "w+")) {
48                         flags = O_RDWR | O_CREAT | O_TRUNC;
49                 } else if (!strcmp(str, "a")) {
50                         flags = O_WRONLY | O_CREAT | O_APPEND;
51                 } else if (!strcmp(str, "a+")) {
52                         flags = O_RDWR | O_CREAT | O_APPEND;
53                 } else {
54                         return luaL_argerror(L, 2, "supported values: r, r+, w, w+, a, a+");
55                 }
56         } else {
57                 return luaL_argerror(L, 2, "open flags or string expected");
58         }
59
60         int fd;
61
62         do {
63                 fd = open(filename, flags, nixio__check_mode(L, 3, 0666));
64         } while (fd == -1 && errno == EINTR);
65         if (fd == -1) {
66                 return nixio__perror(L);
67         }
68
69         int *udata = lua_newuserdata(L, sizeof(int));
70         if (!udata) {
71                 return luaL_error(L, "out of memory");
72         }
73
74         *udata = fd;
75
76         luaL_getmetatable(L, NIXIO_FILE_META);
77         lua_setmetatable(L, -2);
78
79         return 1;
80 }
81
82 static int nixio_open_flags(lua_State *L) {
83         int mode = 0;
84         const int j = lua_gettop(L);
85         for (int i=1; i<=j; i++) {
86                 const char *flag = luaL_checkstring(L, i);
87                 if (!strcmp(flag, "append")) {
88                         mode |= O_APPEND;
89                 } else if (!strcmp(flag, "creat")) {
90                         mode |= O_CREAT;
91                 } else if (!strcmp(flag, "excl")) {
92                         mode |= O_EXCL;
93                 } else if (!strcmp(flag, "nonblock") || !strcmp(flag, "ndelay")) {
94 #ifndef __WINNT__
95                         mode |= O_NONBLOCK;
96 #endif
97                 } else if (!strcmp(flag, "sync")) {
98 #ifndef __WINNT__
99                         mode |= O_SYNC;
100 #endif
101                 } else if (!strcmp(flag, "trunc")) {
102                         mode |= O_TRUNC;
103                 } else if (!strcmp(flag, "rdonly")) {
104                         mode |= O_RDONLY;
105                 } else if (!strcmp(flag, "wronly")) {
106                         mode |= O_WRONLY;
107                 } else if (!strcmp(flag, "rdwr")) {
108                         mode |= O_RDWR;
109                 } else {
110                         return luaL_argerror(L, i, "supported values: append, creat, "
111                                         "excl, nonblock, ndelay, sync, trunc");
112                 }
113         }
114         lua_pushinteger(L, mode);
115         return 1;
116 }
117
118 static int nixio_dup(lua_State *L) {
119         int oldfd = nixio__checkfd(L, 1);
120         int newfd = (lua_gettop(L) > 1) ? nixio__checkfd(L, 2) : -1;
121         int stat  = (newfd == -1) ? dup(oldfd) : dup2(oldfd, newfd);
122
123         if (stat == -1) {
124                 return nixio__perror(L);
125         } else {
126                 int *udata = lua_newuserdata(L, sizeof(int));
127                 if (!udata) {
128                         return luaL_error(L, "out of memory");
129                 }
130
131                 *udata = stat;
132                 luaL_getmetatable(L, NIXIO_FILE_META);
133                 lua_setmetatable(L, -2);
134                 return 1;
135         }
136 }
137
138 static int nixio_pipe(lua_State *L) {
139         int pipefd[2], *udata;
140         if (pipe(pipefd)) {
141                 return nixio__perror(L);
142         }
143
144         luaL_getmetatable(L, NIXIO_FILE_META);
145         udata = lua_newuserdata(L, sizeof(int));
146         if (!udata) {
147                 return luaL_error(L, "out of memory");
148         }
149
150         *udata = pipefd[0];
151         lua_pushvalue(L, -2);
152         lua_setmetatable(L, -2);
153
154
155         udata = lua_newuserdata(L, sizeof(int));
156         if (!udata) {
157                 return luaL_error(L, "out of memory");
158         }
159
160         *udata = pipefd[1];
161         lua_pushvalue(L, -3);
162         lua_setmetatable(L, -2);
163
164         return 2;
165 }
166
167 static int nixio_file_write(lua_State *L) {
168         int fd = nixio__checkfd(L, 1);
169         size_t len;
170         ssize_t sent;
171         const char *data = luaL_checklstring(L, 2, &len);
172
173         if (lua_gettop(L) > 2) {
174                 int offset = luaL_optint(L, 3, 0);
175                 if (offset) {
176                         if (offset < len) {
177                                 data += offset;
178                                 len -= offset;
179                         } else {
180                                 len = 0;
181                         }
182                 }
183
184                 unsigned int wlen = luaL_optint(L, 4, len);
185                 if (wlen < len) {
186                         len = wlen;
187                 }
188         }
189
190         do {
191                 sent = write(fd, data, len);
192         } while(sent == -1 && errno == EINTR);
193         if (sent >= 0) {
194                 lua_pushinteger(L, sent);
195                 return 1;
196         } else {
197                 return nixio__perror(L);
198         }
199 }
200
201 static int nixio_file_read(lua_State *L) {
202         int fd = nixio__checkfd(L, 1);
203         char buffer[NIXIO_BUFFERSIZE];
204         uint req = luaL_checkinteger(L, 2);
205         int readc;
206
207         /* We limit the readsize to NIXIO_BUFFERSIZE */
208         req = (req > NIXIO_BUFFERSIZE) ? NIXIO_BUFFERSIZE : req;
209
210         do {
211                 readc = read(fd, buffer, req);
212         } while (readc == -1 && errno == EINTR);
213
214         if (readc < 0) {
215                 return nixio__perror(L);
216         } else {
217                 lua_pushlstring(L, buffer, readc);
218                 return 1;
219         }
220 }
221
222
223 static int nixio_file_seek(lua_State *L) {
224         int fd = nixio__checkfd(L, 1);
225         off_t len = (off_t)luaL_checknumber(L, 2);
226         int whence;
227         const char *whstr = luaL_optlstring(L, 3, "set", NULL);
228         if (!strcmp(whstr, "set")) {
229                 whence = SEEK_SET;
230         } else if (!strcmp(whstr, "cur")) {
231                 whence = SEEK_CUR;
232         } else if (!strcmp(whstr, "end")) {
233                 whence = SEEK_END;
234         } else {
235                 return luaL_argerror(L, 3, "supported values: set, cur, end");
236         }
237         len = lseek(fd, len, whence);
238         if (len == -1) {
239                 return nixio__perror(L);
240         } else {
241                 lua_pushnumber(L, len);
242                 return 1;
243         }
244 }
245
246 static int nixio_file_tell(lua_State *L) {
247         int fd = nixio__checkfd(L, 1);
248         off_t pos = lseek(fd, 0, SEEK_CUR);
249         if (pos < 0) {
250                 return nixio__perror(L);
251         } else {
252                 lua_pushnumber(L, pos);
253                 return 1;
254         }
255 }
256
257 static int nixio_file_stat(lua_State *L) {
258         nixio_stat_t buf;
259         if (fstat(nixio__checkfd(L, 1), &buf)) {
260                 return nixio__perror(L);
261         } else {
262                 nixio__push_stat(L, &buf);
263                 if (lua_isstring(L, 2)) {
264                         lua_getfield(L, -1, lua_tostring(L, 2));
265                 }
266                 return 1;
267         }
268 }
269
270 static int nixio_file_sync(lua_State *L) {
271         int fd = nixio__checkfd(L, 1);
272         int stat;
273 #if (!defined BSD && !defined __WINNT__)
274         int dataonly = lua_toboolean(L, 2);
275         do {
276                 stat = (dataonly) ? fdatasync(fd) : fsync(fd);
277         } while (stat == -1 && errno == EINTR);
278         return nixio__pstatus(L, !stat);
279 #else
280         do {
281                 stat = fsync(fd);
282         } while (stat == -1 && errno == EINTR);
283         return nixio__pstatus(L, !stat);
284 #endif
285 }
286
287 static int nixio_file_lock(lua_State *L) {
288         int fd = nixio__checkfd(L, 1);
289         const char *flag = luaL_checkstring(L, 2);
290         off_t len = (off_t)luaL_optnumber(L, 3, 0);
291         int stat;
292
293         int cmd = 0;
294         if (!strcmp(flag, "lock")) {
295                 cmd = F_LOCK;
296         } else if (!strcmp(flag, "tlock")) {
297                 cmd = F_TLOCK;
298         } else if (!strcmp(flag, "ulock")) {
299                 cmd = F_ULOCK;
300         } else if (!strcmp(flag, "test")) {
301                 cmd = F_TEST;
302         } else {
303                 return luaL_argerror(L, 2,
304                                 "supported values: lock, tlock, ulock, test");
305         }
306
307         do {
308                 stat = lockf(fd, cmd, len);
309         } while (stat == -1 && errno == EINTR);
310
311         return nixio__pstatus(L, !stat);
312 }
313
314 static int nixio_file_close(lua_State *L) {
315         int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
316         luaL_argcheck(L, *fdp != -1, 1, "invalid file object");
317         int res;
318         do {
319                 res = close(*fdp);
320         } while (res == -1 && errno == EINTR);
321         *fdp = -1;
322         return nixio__pstatus(L, !res);
323 }
324
325 static int nixio_file__gc(lua_State *L) {
326         int *fdp = luaL_checkudata(L, 1, NIXIO_FILE_META);
327         int res;
328         if (*fdp != -1) {
329                 do {
330                         res = close(*fdp);
331                 } while (res == -1 && errno == EINTR);
332                 *fdp = -1;
333         }
334         return 0;
335 }
336
337 /**
338  * string representation
339  */
340 static int nixio_file__tostring(lua_State *L) {
341         lua_pushfstring(L, "nixio file %d", nixio__tofd(L, 1));
342         return 1;
343 }
344
345 /* method table */
346 static const luaL_reg M[] = {
347         {"write",               nixio_file_write},
348         {"read",                nixio_file_read},
349         {"tell",                nixio_file_tell},
350         {"seek",                nixio_file_seek},
351         {"stat",                nixio_file_stat},
352         {"sync",                nixio_file_sync},
353         {"lock",                nixio_file_lock},
354         {"close",               nixio_file_close},
355         {"__gc",                nixio_file__gc},
356         {"__tostring",  nixio_file__tostring},
357         {NULL,                  NULL}
358 };
359
360 /* module table */
361 static const luaL_reg R[] = {
362         {"dup",                 nixio_dup},
363         {"open",                nixio_open},
364         {"open_flags",  nixio_open_flags},
365         {"pipe",                nixio_pipe},
366         {NULL,                  NULL}
367 };
368
369 void nixio_open_file(lua_State *L) {
370         luaL_register(L, NULL, R);
371
372         luaL_newmetatable(L, NIXIO_FILE_META);
373         luaL_register(L, NULL, M);
374         lua_pushvalue(L, -1);
375         lua_setfield(L, -2, "__index");
376
377         int *uin  = lua_newuserdata(L, sizeof(int));
378         int *uout = lua_newuserdata(L, sizeof(int));
379         int *uerr = lua_newuserdata(L, sizeof(int));
380
381         if (!uin || !uout || !uerr) {
382                 luaL_error(L, "out of memory");
383         }
384
385         *uin  = STDIN_FILENO;
386         *uout = STDOUT_FILENO;
387         *uerr = STDERR_FILENO;
388
389         for (int i = -4; i < -1; i++) {
390                 lua_pushvalue(L, -4);
391                 lua_setmetatable(L, i);
392         }
393
394         lua_setfield(L, -5, "stderr");
395         lua_setfield(L, -4, "stdout");
396         lua_setfield(L, -3, "stdin");
397         lua_setfield(L, -2, "meta_file");
398 }