#include "elua_private.h" #ifdef _WIN32 # include /* realpath */ #endif /* expand fname to full path name (so that PATH is ignored) plus turn * stuff into a command, and also verify whether the path exists */ static char * get_cmdline_from_argv(const char *fname, const char **argv) { Eina_Strbuf *buf; char *ret; char pbuf[PATH_MAX]; const char *arg = NULL; FILE *testf = fopen(fname, "rb"); if (!testf) return NULL; fclose(testf); /* for windows, we have realpath in evil, no need for GetFullPathName */ if (!realpath(fname, pbuf)) return NULL; buf = eina_strbuf_new(); eina_strbuf_append_char(buf, '"'); eina_strbuf_append(buf, pbuf); eina_strbuf_append_char(buf, '"'); while ((arg = *(argv++))) { char c; eina_strbuf_append_char(buf, ' '); eina_strbuf_append_char(buf, '"'); while ((c = *(arg++))) { #ifndef _WIN32 if (c == '"' || c == '$') eina_strbuf_append_char(buf, '\\'); eina_strbuf_append_char(buf, c); #else if (c == '"') eina_strbuf_append_char(buf, '\\'); else if (c == '%') eina_strbuf_append_char(buf, '"'); eina_strbuf_append_char(buf, c); if (c == '%') eina_strbuf_append_char(buf, '"'); #endif } eina_strbuf_append_char(buf, '"'); } ret = strdup(eina_strbuf_string_get(buf)); eina_strbuf_free(buf); return ret; } static FILE * elua_popen_c(const char *path, const char *md, const char *argv[]) { FILE *ret; char *cmdline = get_cmdline_from_argv(path, argv); if (!cmdline) return NULL; #ifndef _WIN32 ret = popen(cmdline, md); #else ret = _popen(cmdline, md); #endif free(cmdline); if (!ret) return NULL; return ret; } static int push_ret(lua_State *L, int i, const char *fname) { int en = errno; if (i) { lua_pushboolean(L, 1); return 1; } else { lua_pushnil(L); if (fname) lua_pushfstring(L, "%s: %s", fname, strerror(en)); else lua_pushfstring(L, "%s", strerror(en)); lua_pushinteger(L, en); return 3; } } static FILE * tofile(lua_State *L) { FILE **f = (FILE**)luaL_checkudata(L, 1, "ELUA_FILE*"); if (!*f) { luaL_error(L, "attempt to use a closed file"); } return *f; } static int elua_close(lua_State *L) { FILE **f = (FILE**)luaL_checkudata(L, 1, "ELUA_FILE*"); int ok = (fclose(*f) == 0); if (ok) *f = NULL; return push_ret(L, ok, NULL); } static int elua_flush(lua_State *L) { return push_ret(L, fflush(tofile(L)) == 0, NULL); } static int elua_readline(lua_State *L); static int elua_lines(lua_State *L) { lua_pushvalue(L, 1); lua_pushcclosure(L, elua_readline, 1); return 1; } static int read_number(lua_State *L, FILE *f) { lua_Number d; if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { lua_pushnumber(L, d); return 1; } return 0; } static int test_eof(lua_State *L, FILE *f) { int c = getc(f); ungetc(c, f); lua_pushlstring(L, NULL, 0); return (c != EOF); } static int read_line(lua_State *L, FILE *f) { luaL_Buffer b; luaL_buffinit(L, &b); for (;;) { size_t l; char *p = luaL_prepbuffer(&b); if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { luaL_pushresult(&b); return (lua_strlen(L, -1) > 0); } l = strlen(p); if (!l || p[l - 1] != '\n') luaL_addsize(&b, l); else { luaL_addsize(&b, l - 1); luaL_pushresult(&b); return 1; } } } static int read_chars(lua_State *L, FILE *f, size_t n) { size_t rlen; size_t nr; luaL_Buffer b; luaL_buffinit(L, &b); rlen = LUAL_BUFFERSIZE; do { char *p = luaL_prepbuffer(&b); if (rlen > n) rlen = n; nr = fread(p, sizeof(char), rlen, f); luaL_addsize(&b, nr); n -= nr; } while (n > 0 && nr == rlen); luaL_pushresult(&b); return (n == 0 || lua_strlen(L, -1) > 0); } static int elua_readline(lua_State *L) { FILE *f = *(FILE**)lua_touserdata(L, lua_upvalueindex(1)); int success; if (!f) { luaL_error(L, "file is already closed"); return 0; /* shut up coverity; luaL_error does a longjmp */ } success = read_line(L, f); if (ferror(f)) return luaL_error(L, "%s", strerror(errno)); return success; } static int elua_read(lua_State *L) { FILE *f = tofile(L); int nargs = lua_gettop(L) - 1; int first = 2; int success, n; clearerr(f); if (!nargs) { success = read_line(L, f); n = first + 1; } else { luaL_checkstack(L, nargs + LUA_MINSTACK, "too many arguments"); success = 1; for (n = first; nargs-- && success; ++n) { if (lua_type(L, n) == LUA_TNUMBER) { size_t l = (size_t)lua_tointeger(L, n); success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); } else { const char *p = lua_tostring(L, n); luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); switch (p[1]) { case 'n': success = read_number(L, f); break; case 'l': success = read_line(L, f); break; case 'a': read_chars(L, f, ~((size_t)0)); success = 1; break; default: return luaL_argerror(L, n, "invalid format"); } } } } if (ferror(f)) return push_ret(L, 0, NULL); if (!success) { lua_pop(L, 1); lua_pushnil(L); } return n - first; } static int elua_write(lua_State *L) { FILE *f = tofile(L); int nargs = lua_gettop(L) - 1; int status = 1, arg = 2; for (; nargs--; ++arg) { if (lua_type(L, arg) == LUA_TNUMBER) status = status && (fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0); else { size_t l; const char *s = luaL_checklstring(L, arg, &l); status = status && (fwrite(s, sizeof(char), l, f) == l); } } return push_ret(L, status, NULL); } static int elua_fgc(lua_State *L) { FILE **f = (FILE**)luaL_checkudata(L, 1, "ELUA_FILE*"); if (*f) { fclose(*f); *f = NULL; } return 0; } static int elua_ftostring(lua_State *L) { FILE *f = *((FILE**)luaL_checkudata(L, 1, "ELUA_FILE*")); if (!f) lua_pushliteral(L, "file (closed)"); else lua_pushfstring(L, "file (%p)", f); return 1; } static const luaL_Reg elua_popenlib[] = { { "close" , elua_close }, { "flush" , elua_flush }, { "lines" , elua_lines }, { "read" , elua_read }, { "write" , elua_write }, { "__gc" , elua_fgc }, { "__tostring", elua_ftostring }, { NULL , NULL } }; static FILE ** elua_newfile(lua_State *L) { FILE **f = (FILE**)lua_newuserdata(L, sizeof(FILE*)); *f = NULL; if (luaL_newmetatable(L, "ELUA_FILE*")) { lua_pushvalue(L, -1); lua_setfield (L, -2, "__index"); luaL_register(L, NULL, elua_popenlib); } lua_setmetatable(L, -2); return f; } int _elua_io_popen(lua_State *L) { const char *fname = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); int nargs = lua_gettop(L) - 2; FILE **pf = elua_newfile(L); if (nargs > 0) { const char **argv = (const char**)alloca((nargs + 1) * sizeof(char*)); memset(argv, 0, (nargs + 1) * sizeof(char*)); for (; nargs; --nargs) { argv[nargs - 1] = lua_tostring(L, nargs + 2); } *pf = elua_popen_c(fname, mode, argv); } else { const char *argv = NULL; *pf = elua_popen_c(fname, mode, &argv); } return (!*pf) ? push_ret(L, 0, fname) : 1; }