diff options
Diffstat (limited to 'src/lib_io.c')
-rw-r--r-- | src/lib_io.c | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/src/lib_io.c b/src/lib_io.c new file mode 100644 index 00000000..01623258 --- /dev/null +++ b/src/lib_io.c | |||
@@ -0,0 +1,538 @@ | |||
1 | /* | ||
2 | ** I/O library. | ||
3 | ** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h | ||
4 | ** | ||
5 | ** Major portions taken verbatim or adapted from the Lua interpreter. | ||
6 | ** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h | ||
7 | */ | ||
8 | |||
9 | #include <errno.h> | ||
10 | #include <stdio.h> | ||
11 | |||
12 | #define lib_io_c | ||
13 | #define LUA_LIB | ||
14 | |||
15 | #include "lua.h" | ||
16 | #include "lauxlib.h" | ||
17 | #include "lualib.h" | ||
18 | |||
19 | #include "lj_obj.h" | ||
20 | #include "lj_err.h" | ||
21 | #include "lj_gc.h" | ||
22 | #include "lj_ff.h" | ||
23 | #include "lj_lib.h" | ||
24 | |||
25 | /* Index of standard handles in function environment. */ | ||
26 | #define IO_INPUT 1 | ||
27 | #define IO_OUTPUT 2 | ||
28 | |||
29 | /* -- Error handling ------------------------------------------------------ */ | ||
30 | |||
31 | static int io_pushresult(lua_State *L, int ok, const char *fname) | ||
32 | { | ||
33 | if (ok) { | ||
34 | setboolV(L->top++, 1); | ||
35 | return 1; | ||
36 | } else { | ||
37 | int en = errno; /* Lua API calls may change this value. */ | ||
38 | lua_pushnil(L); | ||
39 | if (fname) | ||
40 | lua_pushfstring(L, "%s: %s", fname, strerror(en)); | ||
41 | else | ||
42 | lua_pushfstring(L, "%s", strerror(en)); | ||
43 | lua_pushinteger(L, en); | ||
44 | return 3; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | static void io_file_error(lua_State *L, int arg, const char *fname) | ||
49 | { | ||
50 | lua_pushfstring(L, "%s: %s", fname, strerror(errno)); | ||
51 | luaL_argerror(L, arg, lua_tostring(L, -1)); | ||
52 | } | ||
53 | |||
54 | /* -- Open helpers -------------------------------------------------------- */ | ||
55 | |||
56 | #define io_tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE)) | ||
57 | |||
58 | static FILE *io_tofile(lua_State *L) | ||
59 | { | ||
60 | FILE **f = io_tofilep(L); | ||
61 | if (*f == NULL) | ||
62 | lj_err_caller(L, LJ_ERR_IOCLFL); | ||
63 | return *f; | ||
64 | } | ||
65 | |||
66 | static FILE **io_file_new(lua_State *L) | ||
67 | { | ||
68 | FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); | ||
69 | *pf = NULL; | ||
70 | luaL_getmetatable(L, LUA_FILEHANDLE); | ||
71 | lua_setmetatable(L, -2); | ||
72 | return pf; | ||
73 | } | ||
74 | |||
75 | /* -- Close helpers ------------------------------------------------------- */ | ||
76 | |||
77 | static int lj_cf_io_std_close(lua_State *L) | ||
78 | { | ||
79 | lua_pushnil(L); | ||
80 | lua_pushliteral(L, "cannot close standard file"); | ||
81 | return 2; | ||
82 | } | ||
83 | |||
84 | static int lj_cf_io_pipe_close(lua_State *L) | ||
85 | { | ||
86 | FILE **p = io_tofilep(L); | ||
87 | #if defined(LUA_USE_POSIX) | ||
88 | int ok = (pclose(*p) != -1); | ||
89 | #elif defined(LUA_USE_WIN) | ||
90 | int ok = (_pclose(*p) != -1); | ||
91 | #else | ||
92 | int ok = 0; | ||
93 | #endif | ||
94 | *p = NULL; | ||
95 | return io_pushresult(L, ok, NULL); | ||
96 | } | ||
97 | |||
98 | static int lj_cf_io_file_close(lua_State *L) | ||
99 | { | ||
100 | FILE **p = io_tofilep(L); | ||
101 | int ok = (fclose(*p) == 0); | ||
102 | *p = NULL; | ||
103 | return io_pushresult(L, ok, NULL); | ||
104 | } | ||
105 | |||
106 | static int io_file_close(lua_State *L) | ||
107 | { | ||
108 | lua_getfenv(L, 1); | ||
109 | lua_getfield(L, -1, "__close"); | ||
110 | return (lua_tocfunction(L, -1))(L); | ||
111 | } | ||
112 | |||
113 | /* -- Read/write helpers -------------------------------------------------- */ | ||
114 | |||
115 | static int io_file_readnum(lua_State *L, FILE *fp) | ||
116 | { | ||
117 | lua_Number d; | ||
118 | if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) { | ||
119 | lua_pushnumber(L, d); | ||
120 | return 1; | ||
121 | } else { | ||
122 | return 0; /* read fails */ | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static int test_eof(lua_State *L, FILE *fp) | ||
127 | { | ||
128 | int c = getc(fp); | ||
129 | ungetc(c, fp); | ||
130 | lua_pushlstring(L, NULL, 0); | ||
131 | return (c != EOF); | ||
132 | } | ||
133 | |||
134 | static int io_file_readline(lua_State *L, FILE *fp) | ||
135 | { | ||
136 | luaL_Buffer b; | ||
137 | luaL_buffinit(L, &b); | ||
138 | for (;;) { | ||
139 | size_t len; | ||
140 | char *p = luaL_prepbuffer(&b); | ||
141 | if (fgets(p, LUAL_BUFFERSIZE, fp) == NULL) { /* EOF? */ | ||
142 | luaL_pushresult(&b); | ||
143 | return (strV(L->top-1)->len > 0); /* Anything read? */ | ||
144 | } | ||
145 | len = strlen(p); | ||
146 | if (len == 0 || p[len-1] != '\n') { /* Partial line? */ | ||
147 | luaL_addsize(&b, len); | ||
148 | } else { | ||
149 | luaL_addsize(&b, len - 1); /* Don't include EOL. */ | ||
150 | luaL_pushresult(&b); | ||
151 | return 1; /* Got at least an EOL. */ | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | static int io_file_readchars(lua_State *L, FILE *fp, size_t n) | ||
157 | { | ||
158 | size_t rlen; /* how much to read */ | ||
159 | size_t nr; /* number of chars actually read */ | ||
160 | luaL_Buffer b; | ||
161 | luaL_buffinit(L, &b); | ||
162 | rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ | ||
163 | do { | ||
164 | char *p = luaL_prepbuffer(&b); | ||
165 | if (rlen > n) rlen = n; /* cannot read more than asked */ | ||
166 | nr = fread(p, 1, rlen, fp); | ||
167 | luaL_addsize(&b, nr); | ||
168 | n -= nr; /* still have to read `n' chars */ | ||
169 | } while (n > 0 && nr == rlen); /* until end of count or eof */ | ||
170 | luaL_pushresult(&b); /* close buffer */ | ||
171 | return (n == 0 || lua_objlen(L, -1) > 0); | ||
172 | } | ||
173 | |||
174 | static int io_file_read(lua_State *L, FILE *fp, int start) | ||
175 | { | ||
176 | int ok, n, nargs = (L->top - L->base) - start; | ||
177 | clearerr(fp); | ||
178 | if (nargs == 0) { | ||
179 | ok = io_file_readline(L, fp); | ||
180 | n = start+1; /* Return 1 result. */ | ||
181 | } else { | ||
182 | /* The results plus the buffers go on top of the args. */ | ||
183 | luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); | ||
184 | ok = 1; | ||
185 | for (n = start; nargs-- && ok; n++) { | ||
186 | if (tvisstr(L->base+n)) { | ||
187 | const char *p = strVdata(L->base+n); | ||
188 | if (p[0] != '*') | ||
189 | lj_err_arg(L, n+1, LJ_ERR_INVOPT); | ||
190 | if (p[1] == 'n') | ||
191 | ok = io_file_readnum(L, fp); | ||
192 | else if (p[1] == 'l') | ||
193 | ok = io_file_readline(L, fp); | ||
194 | else if (p[1] == 'a') | ||
195 | io_file_readchars(L, fp, ~((size_t)0)); | ||
196 | else | ||
197 | lj_err_arg(L, n+1, LJ_ERR_INVFMT); | ||
198 | } else if (tvisnum(L->base+n)) { | ||
199 | size_t len = (size_t)lj_lib_checkint(L, n+1); | ||
200 | ok = len ? io_file_readchars(L, fp, len) : test_eof(L, fp); | ||
201 | } else { | ||
202 | lj_err_arg(L, n+1, LJ_ERR_INVOPT); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | if (ferror(fp)) | ||
207 | return io_pushresult(L, 0, NULL); | ||
208 | if (!ok) | ||
209 | setnilV(L->top-1); /* Replace last result with nil. */ | ||
210 | return n - start; | ||
211 | } | ||
212 | |||
213 | static int io_file_write(lua_State *L, FILE *fp, int start) | ||
214 | { | ||
215 | cTValue *tv; | ||
216 | int status = 1; | ||
217 | for (tv = L->base+start; tv < L->top; tv++) { | ||
218 | if (tvisstr(tv)) { | ||
219 | MSize len = strV(tv)->len; | ||
220 | status = status && (fwrite(strVdata(tv), 1, len, fp) == len); | ||
221 | } else if (tvisnum(tv)) { | ||
222 | status = status && (fprintf(fp, LUA_NUMBER_FMT, numV(tv)) > 0); | ||
223 | } else { | ||
224 | lj_lib_checkstr(L, tv-L->base+1); | ||
225 | } | ||
226 | } | ||
227 | return io_pushresult(L, status, NULL); | ||
228 | } | ||
229 | |||
230 | /* -- I/O file methods ---------------------------------------------------- */ | ||
231 | |||
232 | #define LJLIB_MODULE_io_method | ||
233 | |||
234 | LJLIB_CF(io_method_close) | ||
235 | { | ||
236 | if (lua_isnone(L, 1)) | ||
237 | lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); | ||
238 | io_tofile(L); | ||
239 | return io_file_close(L); | ||
240 | } | ||
241 | |||
242 | LJLIB_CF(io_method_read) | ||
243 | { | ||
244 | return io_file_read(L, io_tofile(L), 1); | ||
245 | } | ||
246 | |||
247 | LJLIB_CF(io_method_write) | ||
248 | { | ||
249 | return io_file_write(L, io_tofile(L), 1); | ||
250 | } | ||
251 | |||
252 | LJLIB_CF(io_method_flush) | ||
253 | { | ||
254 | return io_pushresult(L, fflush(io_tofile(L)) == 0, NULL); | ||
255 | } | ||
256 | |||
257 | LJLIB_CF(io_method_seek) | ||
258 | { | ||
259 | FILE *fp = io_tofile(L); | ||
260 | int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end"); | ||
261 | lua_Number ofs; | ||
262 | int res; | ||
263 | if (opt == 0) opt = SEEK_SET; | ||
264 | else if (opt == 1) opt = SEEK_CUR; | ||
265 | else if (opt == 2) opt = SEEK_END; | ||
266 | lj_lib_opt(L, 3, | ||
267 | ofs = lj_lib_checknum(L, 3); | ||
268 | , | ||
269 | ofs = 0; | ||
270 | ) | ||
271 | #if defined(LUA_USE_POSIX) | ||
272 | res = fseeko(fp, (int64_t)ofs, opt); | ||
273 | #elif _MSC_VER >= 1400 | ||
274 | res = _fseeki64(fp, (int64_t)ofs, opt); | ||
275 | #elif defined(__MINGW32__) | ||
276 | res = fseeko64(fp, (int64_t)ofs, opt); | ||
277 | #else | ||
278 | res = fseek(fp, (long)ofs, opt); | ||
279 | #endif | ||
280 | if (res) | ||
281 | return io_pushresult(L, 0, NULL); | ||
282 | #if defined(LUA_USE_POSIX) | ||
283 | ofs = cast_num(ftello(fp)); | ||
284 | #elif _MSC_VER >= 1400 | ||
285 | ofs = cast_num(_ftelli64(fp)); | ||
286 | #elif defined(__MINGW32__) | ||
287 | ofs = cast_num(ftello64(fp)); | ||
288 | #else | ||
289 | ofs = cast_num(ftell(fp)); | ||
290 | #endif | ||
291 | setnumV(L->top-1, ofs); | ||
292 | return 1; | ||
293 | } | ||
294 | |||
295 | LJLIB_CF(io_method_setvbuf) | ||
296 | { | ||
297 | FILE *fp = io_tofile(L); | ||
298 | int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no"); | ||
299 | size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE); | ||
300 | if (opt == 0) opt = _IOFBF; | ||
301 | else if (opt == 1) opt = _IOLBF; | ||
302 | else if (opt == 2) opt = _IONBF; | ||
303 | return io_pushresult(L, (setvbuf(fp, NULL, opt, sz) == 0), NULL); | ||
304 | } | ||
305 | |||
306 | /* Forward declaration. */ | ||
307 | static void io_file_lines(lua_State *L, int idx, int toclose); | ||
308 | |||
309 | LJLIB_CF(io_method_lines) | ||
310 | { | ||
311 | io_tofile(L); | ||
312 | io_file_lines(L, 1, 0); | ||
313 | return 1; | ||
314 | } | ||
315 | |||
316 | LJLIB_CF(io_method___gc) | ||
317 | { | ||
318 | FILE *fp = *io_tofilep(L); | ||
319 | if (fp != NULL) io_file_close(L); | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | LJLIB_CF(io_method___tostring) | ||
324 | { | ||
325 | FILE *fp = *io_tofilep(L); | ||
326 | if (fp == NULL) | ||
327 | lua_pushliteral(L, "file (closed)"); | ||
328 | else | ||
329 | lua_pushfstring(L, "file (%p)", fp); | ||
330 | return 1; | ||
331 | } | ||
332 | |||
333 | LJLIB_PUSH(top-1) LJLIB_SET(__index) | ||
334 | |||
335 | #include "lj_libdef.h" | ||
336 | |||
337 | /* -- I/O library functions ----------------------------------------------- */ | ||
338 | |||
339 | #define LJLIB_MODULE_io | ||
340 | |||
341 | LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */ | ||
342 | |||
343 | static FILE *io_file_get(lua_State *L, int findex) | ||
344 | { | ||
345 | GCtab *fenv = tabref(curr_func(L)->c.env); | ||
346 | GCudata *ud = udataV(&tvref(fenv->array)[findex]); | ||
347 | FILE *fp = *(FILE **)uddata(ud); | ||
348 | if (fp == NULL) | ||
349 | lj_err_caller(L, LJ_ERR_IOSTDCL); | ||
350 | return fp; | ||
351 | } | ||
352 | |||
353 | LJLIB_CF(io_open) | ||
354 | { | ||
355 | const char *fname = luaL_checkstring(L, 1); | ||
356 | const char *mode = luaL_optstring(L, 2, "r"); | ||
357 | FILE **pf = io_file_new(L); | ||
358 | *pf = fopen(fname, mode); | ||
359 | return (*pf == NULL) ? io_pushresult(L, 0, fname) : 1; | ||
360 | } | ||
361 | |||
362 | LJLIB_CF(io_tmpfile) | ||
363 | { | ||
364 | FILE **pf = io_file_new(L); | ||
365 | *pf = tmpfile(); | ||
366 | return (*pf == NULL) ? io_pushresult(L, 0, NULL) : 1; | ||
367 | } | ||
368 | |||
369 | LJLIB_CF(io_close) | ||
370 | { | ||
371 | return lj_cf_io_method_close(L); | ||
372 | } | ||
373 | |||
374 | LJLIB_CF(io_read) | ||
375 | { | ||
376 | return io_file_read(L, io_file_get(L, IO_INPUT), 0); | ||
377 | } | ||
378 | |||
379 | LJLIB_CF(io_write) | ||
380 | { | ||
381 | return io_file_write(L, io_file_get(L, IO_OUTPUT), 0); | ||
382 | } | ||
383 | |||
384 | LJLIB_CF(io_flush) | ||
385 | { | ||
386 | return io_pushresult(L, fflush(io_file_get(L, IO_OUTPUT)) == 0, NULL); | ||
387 | } | ||
388 | |||
389 | LJLIB_NOREG LJLIB_CF(io_lines_iter) | ||
390 | { | ||
391 | FILE *fp = *(FILE **)uddata(udataV(lj_lib_upvalue(L, 1))); | ||
392 | int ok; | ||
393 | if (fp == NULL) | ||
394 | lj_err_caller(L, LJ_ERR_IOCLFL); | ||
395 | ok = io_file_readline(L, fp); | ||
396 | if (ferror(fp)) | ||
397 | return luaL_error(L, "%s", strerror(errno)); | ||
398 | if (ok) | ||
399 | return 1; | ||
400 | if (tvistrue(lj_lib_upvalue(L, 2))) { /* Need to close file? */ | ||
401 | L->top = L->base+1; | ||
402 | setudataV(L, L->base, udataV(lj_lib_upvalue(L, 1))); | ||
403 | io_file_close(L); | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static void io_file_lines(lua_State *L, int idx, int toclose) | ||
409 | { | ||
410 | lua_pushvalue(L, idx); | ||
411 | lua_pushboolean(L, toclose); | ||
412 | lua_pushcclosure(L, lj_cf_io_lines_iter, 2); | ||
413 | funcV(L->top-1)->c.ffid = FF_io_lines_iter; | ||
414 | } | ||
415 | |||
416 | LJLIB_CF(io_lines) | ||
417 | { | ||
418 | if (lua_isnoneornil(L, 1)) { /* no arguments? */ | ||
419 | /* will iterate over default input */ | ||
420 | lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); | ||
421 | return lj_cf_io_method_lines(L); | ||
422 | } else { | ||
423 | const char *fname = luaL_checkstring(L, 1); | ||
424 | FILE **pf = io_file_new(L); | ||
425 | *pf = fopen(fname, "r"); | ||
426 | if (*pf == NULL) | ||
427 | io_file_error(L, 1, fname); | ||
428 | io_file_lines(L, lua_gettop(L), 1); | ||
429 | return 1; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | static int io_std_get(lua_State *L, int fp, const char *mode) | ||
434 | { | ||
435 | if (!lua_isnoneornil(L, 1)) { | ||
436 | const char *fname = lua_tostring(L, 1); | ||
437 | if (fname) { | ||
438 | FILE **pf = io_file_new(L); | ||
439 | *pf = fopen(fname, mode); | ||
440 | if (*pf == NULL) | ||
441 | io_file_error(L, 1, fname); | ||
442 | } else { | ||
443 | io_tofile(L); /* check that it's a valid file handle */ | ||
444 | lua_pushvalue(L, 1); | ||
445 | } | ||
446 | lua_rawseti(L, LUA_ENVIRONINDEX, fp); | ||
447 | } | ||
448 | /* return current value */ | ||
449 | lua_rawgeti(L, LUA_ENVIRONINDEX, fp); | ||
450 | return 1; | ||
451 | } | ||
452 | |||
453 | LJLIB_CF(io_input) | ||
454 | { | ||
455 | return io_std_get(L, IO_INPUT, "r"); | ||
456 | } | ||
457 | |||
458 | LJLIB_CF(io_output) | ||
459 | { | ||
460 | return io_std_get(L, IO_OUTPUT, "w"); | ||
461 | } | ||
462 | |||
463 | LJLIB_CF(io_type) | ||
464 | { | ||
465 | void *ud; | ||
466 | luaL_checkany(L, 1); | ||
467 | ud = lua_touserdata(L, 1); | ||
468 | lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); | ||
469 | if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) | ||
470 | lua_pushnil(L); /* not a file */ | ||
471 | else if (*((FILE **)ud) == NULL) | ||
472 | lua_pushliteral(L, "closed file"); | ||
473 | else | ||
474 | lua_pushliteral(L, "file"); | ||
475 | return 1; | ||
476 | } | ||
477 | |||
478 | LJLIB_PUSH(top-3) LJLIB_SET(!) /* Set environment. */ | ||
479 | |||
480 | LJLIB_CF(io_popen) | ||
481 | { | ||
482 | #if defined(LUA_USE_POSIX) || defined(LUA_USE_WIN) | ||
483 | const char *fname = luaL_checkstring(L, 1); | ||
484 | const char *mode = luaL_optstring(L, 2, "r"); | ||
485 | FILE **pf = io_file_new(L); | ||
486 | #ifdef LUA_USE_POSIX | ||
487 | fflush(NULL); | ||
488 | *pf = popen(fname, mode); | ||
489 | #else | ||
490 | *pf = _popen(fname, mode); | ||
491 | #endif | ||
492 | return (*pf == NULL) ? io_pushresult(L, 0, fname) : 1; | ||
493 | #else | ||
494 | luaL_error(L, LUA_QL("popen") " not supported"); | ||
495 | #endif | ||
496 | } | ||
497 | |||
498 | #include "lj_libdef.h" | ||
499 | |||
500 | /* ------------------------------------------------------------------------ */ | ||
501 | |||
502 | static void io_std_new(lua_State *L, FILE *fp, int k, const char *fname) | ||
503 | { | ||
504 | FILE **pf = io_file_new(L); | ||
505 | GCudata *ud = udataV(L->top-1); | ||
506 | GCtab *envt = tabV(L->top-2); | ||
507 | *pf = fp; | ||
508 | setgcref(ud->env, obj2gco(envt)); | ||
509 | lj_gc_objbarrier(L, obj2gco(ud), envt); | ||
510 | if (k > 0) { | ||
511 | lua_pushvalue(L, -1); | ||
512 | lua_rawseti(L, -5, k); | ||
513 | } | ||
514 | lua_setfield(L, -3, fname); | ||
515 | } | ||
516 | |||
517 | static void io_fenv_new(lua_State *L, int narr, lua_CFunction cls) | ||
518 | { | ||
519 | lua_createtable(L, narr, 1); | ||
520 | lua_pushcfunction(L, cls); | ||
521 | lua_setfield(L, -2, "__close"); | ||
522 | } | ||
523 | |||
524 | LUALIB_API int luaopen_io(lua_State *L) | ||
525 | { | ||
526 | LJ_LIB_REG_(L, NULL, io_method); | ||
527 | lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); | ||
528 | io_fenv_new(L, 0, lj_cf_io_pipe_close); /* top-3 */ | ||
529 | io_fenv_new(L, 2, lj_cf_io_file_close); /* top-2 */ | ||
530 | LJ_LIB_REG(L, io); | ||
531 | io_fenv_new(L, 0, lj_cf_io_std_close); | ||
532 | io_std_new(L, stdin, IO_INPUT, "stdin"); | ||
533 | io_std_new(L, stdout, IO_OUTPUT, "stdout"); | ||
534 | io_std_new(L, stderr, 0, "stderr"); | ||
535 | lua_pop(L, 1); | ||
536 | return 1; | ||
537 | } | ||
538 | |||