1 #ifdef _WIN32
2 # include <evil_private.h> /* realpath */
3 #endif
4
5 #include "elua_private.h"
6
7 /* expand fname to full path name (so that PATH is ignored) plus turn
8 * stuff into a command, and also verify whether the path exists */
9 static char *
get_cmdline_from_argv(const char * fname,const char ** argv)10 get_cmdline_from_argv(const char *fname, const char **argv)
11 {
12 Eina_Strbuf *buf;
13 char *ret;
14 char pbuf[PATH_MAX];
15 const char *arg = NULL;
16
17 FILE *testf = fopen(fname, "rb");
18 if (!testf)
19 return NULL;
20
21 fclose(testf);
22
23 /* for windows, we have realpath in evil, no need for GetFullPathName */
24 if (!realpath(fname, pbuf))
25 return NULL;
26
27 buf = eina_strbuf_new();
28 eina_strbuf_append_char(buf, '"');
29 eina_strbuf_append(buf, pbuf);
30 eina_strbuf_append_char(buf, '"');
31
32 while ((arg = *(argv++)))
33 {
34 char c;
35 eina_strbuf_append_char(buf, ' ');
36 eina_strbuf_append_char(buf, '"');
37
38 while ((c = *(arg++)))
39 {
40 #ifndef _WIN32
41 if (c == '"' || c == '$') eina_strbuf_append_char(buf, '\\');
42 eina_strbuf_append_char(buf, c);
43 #else
44 if (c == '"') eina_strbuf_append_char(buf, '\\');
45 else if (c == '%') eina_strbuf_append_char(buf, '"');
46 eina_strbuf_append_char(buf, c);
47 if (c == '%') eina_strbuf_append_char(buf, '"');
48 #endif
49 }
50
51 eina_strbuf_append_char(buf, '"');
52 }
53
54 ret = strdup(eina_strbuf_string_get(buf));
55 eina_strbuf_free(buf);
56 return ret;
57 }
58
59 static FILE *
elua_popen_c(const char * path,const char * md,const char * argv[])60 elua_popen_c(const char *path, const char *md, const char *argv[])
61 {
62 FILE *ret;
63
64 char *cmdline = get_cmdline_from_argv(path, argv);
65 if (!cmdline) return NULL;
66
67 #ifndef _WIN32
68 ret = popen(cmdline, md);
69 #else
70 ret = _popen(cmdline, md);
71 #endif
72
73 free(cmdline);
74
75 if (!ret) return NULL;
76
77 return ret;
78 }
79
80 static int
push_ret(lua_State * L,int i,const char * fname)81 push_ret(lua_State *L, int i, const char *fname)
82 {
83 int en = errno;
84 if (i)
85 {
86 lua_pushboolean(L, 1);
87 return 1;
88 }
89 else
90 {
91 lua_pushnil(L);
92 if (fname)
93 lua_pushfstring(L, "%s: %s", fname, strerror(en));
94 else
95 lua_pushfstring(L, "%s", strerror(en));
96 lua_pushinteger(L, en);
97 return 3;
98 }
99 }
100
101 static FILE *
tofile(lua_State * L)102 tofile(lua_State *L)
103 {
104 FILE **f = (FILE**)luaL_checkudata(L, 1, "ELUA_FILE*");
105 if (!*f)
106 {
107 luaL_error(L, "attempt to use a closed file");
108 }
109 return *f;
110 }
111
112 static int
elua_close(lua_State * L)113 elua_close(lua_State *L)
114 {
115 FILE **f = (FILE**)luaL_checkudata(L, 1, "ELUA_FILE*");
116 int ok = (fclose(*f) == 0);
117 if (ok) *f = NULL;
118 return push_ret(L, ok, NULL);
119 }
120
121 static int
elua_flush(lua_State * L)122 elua_flush(lua_State *L)
123 {
124 return push_ret(L, fflush(tofile(L)) == 0, NULL);
125 }
126
127 static int elua_readline(lua_State *L);
128
129 static int
elua_lines(lua_State * L)130 elua_lines(lua_State *L)
131 {
132 lua_pushvalue(L, 1);
133 lua_pushcclosure(L, elua_readline, 1);
134 return 1;
135 }
136
137 static int
read_number(lua_State * L,FILE * f)138 read_number(lua_State *L, FILE *f)
139 {
140 lua_Number d;
141 if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1)
142 {
143 lua_pushnumber(L, d);
144 return 1;
145 }
146 return 0;
147 }
148
149 static int
test_eof(lua_State * L,FILE * f)150 test_eof(lua_State *L, FILE *f)
151 {
152 int c = getc(f);
153 ungetc(c, f);
154 lua_pushlstring(L, NULL, 0);
155 return (c != EOF);
156 }
157
158 static int
read_line(lua_State * L,FILE * f)159 read_line(lua_State *L, FILE *f)
160 {
161 luaL_Buffer b;
162 luaL_buffinit(L, &b);
163 for (;;)
164 {
165 size_t l;
166 char *p = luaL_prepbuffer(&b);
167 if (fgets(p, LUAL_BUFFERSIZE, f) == NULL)
168 {
169 luaL_pushresult(&b);
170 return (elua_strlen(L, -1) > 0);
171 }
172 l = strlen(p);
173 if (!l || p[l - 1] != '\n')
174 luaL_addsize(&b, l);
175 else
176 {
177 luaL_addsize(&b, l - 1);
178 luaL_pushresult(&b);
179 return 1;
180 }
181 }
182 }
183
184 static int
read_chars(lua_State * L,FILE * f,size_t n)185 read_chars(lua_State *L, FILE *f, size_t n)
186 {
187 size_t rlen;
188 size_t nr;
189 luaL_Buffer b;
190 luaL_buffinit(L, &b);
191 rlen = LUAL_BUFFERSIZE;
192 do
193 {
194 char *p = luaL_prepbuffer(&b);
195 if (rlen > n) rlen = n;
196 nr = fread(p, sizeof(char), rlen, f);
197 luaL_addsize(&b, nr);
198 n -= nr;
199 } while (n > 0 && nr == rlen);
200 luaL_pushresult(&b);
201 return (n == 0 || elua_strlen(L, -1) > 0);
202 }
203
204 static int
elua_readline(lua_State * L)205 elua_readline(lua_State *L)
206 {
207 FILE *f = *(FILE**)lua_touserdata(L, lua_upvalueindex(1));
208 int success;
209 if (!f)
210 {
211 luaL_error(L, "file is already closed");
212 return 0; /* shut up coverity; luaL_error does a longjmp */
213 }
214 success = read_line(L, f);
215 if (ferror(f))
216 return luaL_error(L, "%s", strerror(errno));
217 return success;
218 }
219
220 static int
elua_read(lua_State * L)221 elua_read(lua_State *L)
222 {
223 FILE *f = tofile(L);
224 int nargs = lua_gettop(L) - 1;
225 int first = 2;
226 int success, n;
227 clearerr(f);
228 if (!nargs)
229 {
230 success = read_line(L, f);
231 n = first + 1;
232 }
233 else
234 {
235 luaL_checkstack(L, nargs + LUA_MINSTACK, "too many arguments");
236 success = 1;
237 for (n = first; nargs-- && success; ++n)
238 {
239 if (lua_type(L, n) == LUA_TNUMBER)
240 {
241 size_t l = (size_t)lua_tointeger(L, n);
242 success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
243 }
244 else
245 {
246 const char *p = lua_tostring(L, n);
247 luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
248 switch (p[1])
249 {
250 case 'n':
251 success = read_number(L, f);
252 break;
253 case 'l':
254 success = read_line(L, f);
255 break;
256 case 'a':
257 read_chars(L, f, ~((size_t)0));
258 success = 1;
259 break;
260 default:
261 return luaL_argerror(L, n, "invalid format");
262 }
263 }
264 }
265 }
266 if (ferror(f))
267 return push_ret(L, 0, NULL);
268 if (!success)
269 {
270 lua_pop(L, 1);
271 lua_pushnil(L);
272 }
273 return n - first;
274 }
275
276 static int
elua_write(lua_State * L)277 elua_write(lua_State *L)
278 {
279 FILE *f = tofile(L);
280 int nargs = lua_gettop(L) - 1;
281 int status = 1, arg = 2;
282 for (; nargs--; ++arg)
283 {
284 if (lua_type(L, arg) == LUA_TNUMBER)
285 status = status && (fprintf(f, LUA_NUMBER_FMT,
286 lua_tonumber(L, arg)) > 0);
287 else
288 {
289 size_t l;
290 const char *s = luaL_checklstring(L, arg, &l);
291 status = status && (fwrite(s, sizeof(char), l, f) == l);
292 }
293 }
294 return push_ret(L, status, NULL);
295 }
296
297 static int
elua_fgc(lua_State * L)298 elua_fgc(lua_State *L)
299 {
300 FILE **f = (FILE**)luaL_checkudata(L, 1, "ELUA_FILE*");
301 if (*f)
302 {
303 fclose(*f);
304 *f = NULL;
305 }
306 return 0;
307 }
308
309 static int
elua_ftostring(lua_State * L)310 elua_ftostring(lua_State *L)
311 {
312 FILE *f = *((FILE**)luaL_checkudata(L, 1, "ELUA_FILE*"));
313 if (!f)
314 lua_pushliteral(L, "file (closed)");
315 else
316 lua_pushfstring(L, "file (%p)", f);
317 return 1;
318 }
319
320 static const luaL_Reg elua_popenlib[] =
321 {
322 { "close" , elua_close },
323 { "flush" , elua_flush },
324 { "lines" , elua_lines },
325 { "read" , elua_read },
326 { "write" , elua_write },
327 { "__gc" , elua_fgc },
328 { "__tostring", elua_ftostring },
329 { NULL , NULL }
330 };
331
332 static FILE **
elua_newfile(lua_State * L)333 elua_newfile(lua_State *L)
334 {
335 FILE **f = (FILE**)lua_newuserdata(L, sizeof(FILE*));
336 *f = NULL;
337 if (luaL_newmetatable(L, "ELUA_FILE*"))
338 {
339 lua_pushvalue(L, -1);
340 lua_setfield (L, -2, "__index");
341 elua_register(L, elua_popenlib);
342 }
343 lua_setmetatable(L, -2);
344 return f;
345 }
346
347 int
_elua_io_popen(lua_State * L)348 _elua_io_popen(lua_State *L)
349 {
350 const char *fname = luaL_checkstring(L, 1);
351 const char *mode = luaL_optstring(L, 2, "r");
352 int nargs = lua_gettop(L) - 2;
353 FILE **pf = elua_newfile(L);
354 if (nargs > 0)
355 {
356 const char **argv = (const char**)alloca((nargs + 1) * sizeof(char*));
357 memset(argv, 0, (nargs + 1) * sizeof(char*));
358 for (; nargs; --nargs)
359 {
360 argv[nargs - 1] = lua_tostring(L, nargs + 2);
361 }
362 *pf = elua_popen_c(fname, mode, argv);
363 }
364 else
365 {
366 const char *argv = NULL;
367 *pf = elua_popen_c(fname, mode, &argv);
368 }
369 return (!*pf) ? push_ret(L, 0, fname) : 1;
370 }
371