Implement subprocess.wait
[lua-subprocess:lua-subprocess.git] / liolib-copy.c
1 #include "liolib-copy.h"
2 #include "errno.h"
3 #include "stdio.h"
4 #include "stdlib.h"
5 #include "string.h"
6
7 #define LUA_LIB
8 #include "lua.h"
9 #include "lauxlib.h"
10 #include "lualib.h"
11
12 static int pushresult(lua_State *L, int i, const char *filename)
13 {
14     int en = errno;  /* calls to Lua API may change this value */
15     if (i) {
16         lua_pushboolean(L, 1);
17         return 1;
18     } else {
19         lua_pushnil(L);
20         if (filename)
21             lua_pushfstring(L, "%s: %s", filename, strerror(en));
22         else
23             lua_pushfstring(L, "%s", strerror(en));
24         lua_pushinteger(L, en);
25         return 3;
26     }
27 }
28
29 /* If SHARE_LIOLIB is defined, Lua's own FILE* metatable can be used. This is
30    fine when the C runtime library used by Lua and by this module is the same,
31    but FILE* objects are not compatible between different runtime libraries.
32    If Lua was compiled with a different runtime library, DO NOT set SHARE_LIOLIB.
33    In this case, copies of Lua's IO functions will be compiled in.
34    If SHARE_LIOLIB is set and crashes ensue, turn it off! */
35 #ifndef SHARE_LIOLIB
36
37 #undef LUA_FILEHANDLE
38 #define LUA_FILEHANDLE "lio2_FILE*"
39
40 #define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
41
42 static FILE *tofile(lua_State *L)
43 {
44     FILE **f = tofilep(L);
45     if (*f == NULL)
46         luaL_error(L, "attempt to use a closed file");
47     return *f;
48 }
49
50 static int io_close(lua_State *L)
51 {
52     FILE **p = tofilep(L);
53     if (*p != NULL){
54         int ok = (fclose(*p) == 0);
55         *p = NULL;
56         return pushresult(L, ok, NULL);
57     } else {
58         return 0;
59     }
60 }
61
62 static int io_tostring(lua_State *L)
63 {
64     FILE *f = *tofilep(L);
65     if (f == NULL)
66         lua_pushliteral(L, "file (closed)");
67     else
68         lua_pushfstring(L, "file (%p)", f);
69     return 1;
70 }
71
72 static int io_readline(lua_State *L);
73
74 static void aux_lines(lua_State *L, int idx, int toclose)
75 {
76     lua_pushvalue(L, idx);
77     lua_pushboolean(L, toclose);  /* close/not close file when finished */
78     lua_pushcclosure(L, io_readline, 2);
79 }
80
81 static int f_lines(lua_State *L)
82 {
83     tofile(L);  /* check that it's a valid file handle */
84     aux_lines(L, 1, 0);
85     return 1;
86 }
87
88 /*
89 ** {======================================================
90 ** READ
91 ** =======================================================
92 */
93
94 static int read_number(lua_State *L, FILE *f)
95 {
96     lua_Number d;
97     if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
98         lua_pushnumber(L, d);
99         return 1;
100     } else return 0;  /* read fails */
101 }
102
103 static int test_eof(lua_State *L, FILE *f)
104 {
105     int c = getc(f);
106     ungetc(c, f);
107     lua_pushlstring(L, NULL, 0);
108     return (c != EOF);
109 }
110
111 static int read_line(lua_State *L, FILE *f)
112 {
113     luaL_Buffer b;
114     luaL_buffinit(L, &b);
115     for (;;) {
116         size_t l;
117         char *p = luaL_prepbuffer(&b);
118         if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) {  /* eof? */
119             luaL_pushresult(&b);  /* close buffer */
120             return (lua_objlen(L, -1) > 0);  /* check whether read something */
121         }
122         l = strlen(p);
123         if (l == 0 || p[l-1] != '\n')
124             luaL_addsize(&b, l);
125         else {
126             luaL_addsize(&b, l - 1);  /* do not include `eol' */
127             luaL_pushresult(&b);  /* close buffer */
128             return 1;  /* read at least an `eol' */
129         }
130     }
131 }
132
133 static int read_chars(lua_State *L, FILE *f, size_t n)
134 {
135     size_t rlen;  /* how much to read */
136     size_t nr;  /* number of chars actually read */
137     luaL_Buffer b;
138     luaL_buffinit(L, &b);
139     rlen = LUAL_BUFFERSIZE;  /* try to read that much each time */
140     do {
141         char *p = luaL_prepbuffer(&b);
142         if (rlen > n) rlen = n;  /* cannot read more than asked */
143         nr = fread(p, sizeof(char), rlen, f);
144         luaL_addsize(&b, nr);
145         n -= nr;  /* still have to read `n' chars */
146     } while (n > 0 && nr == rlen);  /* until end of count or eof */
147     luaL_pushresult(&b);  /* close buffer */
148     return (n == 0 || lua_objlen(L, -1) > 0);
149 }
150
151 static int g_read(lua_State *L, FILE *f, int first)
152 {
153     int nargs = lua_gettop(L) - 1;
154     int success;
155     int n;
156     clearerr(f);
157     if (nargs == 0) {  /* no arguments? */
158         success = read_line(L, f);
159         n = first+1;  /* to return 1 result */
160     } else {  /* ensure stack space for all results and for auxlib's buffer */
161         luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
162         success = 1;
163         for (n = first; nargs-- && success; n++) {
164             if (lua_type(L, n) == LUA_TNUMBER) {
165                 size_t l = (size_t)lua_tointeger(L, n);
166                 success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
167             }
168             else {
169                 const char *p = lua_tostring(L, n);
170                 luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
171                 switch (p[1]) {
172                 case 'n':  /* number */
173                     success = read_number(L, f);
174                     break;
175                 case 'l':  /* line */
176                     success = read_line(L, f);
177                     break;
178                 case 'a':  /* file */
179                     read_chars(L, f, ~((size_t)0));  /* read MAX_SIZE_T chars */
180                     success = 1; /* always success */
181                     break;
182                 default:
183                     return luaL_argerror(L, n, "invalid format");
184                 }
185             }
186         }
187     }
188     if (ferror(f))
189         return pushresult(L, 0, NULL);
190     if (!success) {
191         lua_pop(L, 1);  /* remove last result */
192         lua_pushnil(L);  /* push nil instead */
193     }
194     return n - first;
195 }
196
197 static int f_read(lua_State *L) {
198   return g_read(L, tofile(L), 2);
199 }
200
201 static int io_readline (lua_State *L)
202 {
203     FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
204     int sucess;
205     if (f == NULL)  /* file is already closed? */
206         luaL_error(L, "file is already closed");
207     sucess = read_line(L, f);
208     if (ferror(f))
209         return luaL_error(L, "%s", strerror(errno));
210     if (sucess) return 1;
211     else {  /* EOF */
212         if (lua_toboolean(L, lua_upvalueindex(2))) {  /* generator created file? */
213             lua_settop(L, 0);
214             lua_pushvalue(L, lua_upvalueindex(1));
215             io_close(L);  /* close it */
216         }
217         return 0;
218     }
219 }
220
221 /* }====================================================== */
222
223
224 static int g_write(lua_State *L, FILE *f, int arg)
225 {
226     int nargs = lua_gettop(L) - 1;
227     int status = 1;
228     for (; nargs--; arg++) {
229         if (lua_type(L, arg) == LUA_TNUMBER) {
230           /* optimization: could be done exactly as for strings */
231             status = status &&
232               fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
233         } else {
234             size_t l;
235             const char *s = luaL_checklstring(L, arg, &l);
236             status = status && (fwrite(s, sizeof(char), l, f) == l);
237         }
238     }
239     return pushresult(L, status, NULL);
240 }
241
242 static int f_write(lua_State *L)
243 {
244     return g_write(L, tofile(L), 2);
245 }
246
247 static int f_seek(lua_State *L)
248 {
249     static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
250     static const char *const modenames[] = {"set", "cur", "end", NULL};
251     FILE *f = tofile(L);
252     int op = luaL_checkoption(L, 2, "cur", modenames);
253     long offset = luaL_optlong(L, 3, 0);
254     op = fseek(f, offset, mode[op]);
255     if (op)
256         return pushresult(L, 0, NULL);  /* error */
257     else {
258         lua_pushinteger(L, ftell(f));
259         return 1;
260     }
261 }
262
263 static int f_setvbuf(lua_State *L)
264 {
265     static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
266     static const char *const modenames[] = {"no", "full", "line", NULL};
267     FILE *f = tofile(L);
268     int op = luaL_checkoption(L, 2, NULL, modenames);
269     lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
270     int res = setvbuf(f, NULL, mode[op], sz);
271     return pushresult(L, res == 0, NULL);
272 }
273
274 static int f_flush(lua_State *L)
275 {
276     return pushresult(L, fflush(tofile(L)) == 0, NULL);
277 }
278
279 static const luaL_Reg flib[] = {
280     {"close", io_close},
281     {"flush", f_flush},
282     {"lines", f_lines},
283     {"read", f_read},
284     {"seek", f_seek},
285     {"setvbuf", f_setvbuf},
286     {"write", f_write},
287     {"__gc", io_close},
288     {"__tostring", io_tostring},
289     {NULL, NULL}
290 };
291
292 static void createmeta(lua_State *L)
293 {
294     luaL_newmetatable(L, LUA_FILEHANDLE);  /* create metatable for file handles */
295     lua_pushvalue(L, -1);  /* push metatable */
296     lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
297     luaL_register(L, NULL, flib);  /* file methods */
298 }
299
300 #else /* #ifndef SHARE_LIOLIB */
301
302 static int io_fclose(lua_State *L)
303 {
304     FILE **p = luaL_checkudata(L, 1, LUA_FILEHANDLE);
305     int ok = (fclose(*p) == 0);
306     *p = NULL;
307     return pushresult(L, ok, NULL);
308 }
309
310 #endif /* #ifndef SHARE_LIOLIB */
311
312 FILE *liolib_copy_tofile(lua_State *L, int index)
313 {
314     int eq;
315     if (lua_type(L, index) != LUA_TTABLE) return NULL;
316     lua_getmetatable(L, index);
317     luaL_getmetatable(L, LUA_FILEHANDLE);
318     eq = lua_equal(L, -2, -1);
319     lua_pop(L, 2);
320     if (!eq) return NULL;
321     return *(FILE **) lua_touserdata(L, index);
322 }
323
324 /*
325 ** When creating file handles, always creates a `closed' file handle
326 ** before opening the actual file; so, if there is a memory error, the
327 ** file is not left opened.
328 */
329 FILE **liolib_copy_newfile(lua_State *L)
330 {
331     FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
332     *pf = NULL;  /* file handle is currently `closed' */
333     luaL_getmetatable(L, LUA_FILEHANDLE);
334 #ifdef SHARE_LIOLIB
335     /* create environment for the file */
336     lua_createtable(L, 0, 1);
337     lua_pushcfunction(L, io_fclose);
338     lua_setfield(L, -2, "__close");
339     lua_setfenv(L, -3);
340 #else
341     /* create metatable as needed */
342     if (lua_isnil(L, -1)){
343         lua_pop(L, 1);
344         createmeta(L);
345     }
346 #endif
347     lua_setmetatable(L, -2);
348     /* leave file object on stack */
349     return pf;
350 }