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