diff --git a/src/Makefile_Elua.am b/src/Makefile_Elua.am index 7f1e7fa926..c44ce1d0ea 100644 --- a/src/Makefile_Elua.am +++ b/src/Makefile_Elua.am @@ -4,7 +4,8 @@ bin_PROGRAMS += bin/elua/elua bin_elua_elua_SOURCES = \ bin/elua/cache.c \ - bin/elua/main.c + bin/elua/main.c \ + bin/elua/io.c bin_elua_elua_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -DELUA_CORE_DIR="\"$(datadir)/elua/core\"" \ diff --git a/src/bin/elua/io.c b/src/bin/elua/io.c new file mode 100644 index 0000000000..eb3e22082f --- /dev/null +++ b/src/bin/elua/io.c @@ -0,0 +1,284 @@ +/* elua io extras, largely taken from lua io lib source */ + +#ifndef _WIN32 +#include +#include +#include +#define pipe(x, mode) pipe(x) +#else +#include +#include "fork_win.c" +int fork_win(void); +#define fork fork_win +#define fdopen _fdopen +#define execv _execv +#define close _close +#define dup2 _dup2 +#define pipe(x, mode) _pipe(x, 4096, ((mode && mode[0] && mode[1] == 'b') \ + ? _O_BINARY : _O_TEXT) | _NO_NOINHERIT) +#endif + +#include "main.h" + +static FILE *elua_popen_c(const char *path, const char *argv[], const char *mode) { + int read = (!mode || mode[0] == 'r'); + int binary = mode && mode[0] && mode[1] == 'b'; + pid_t pid; + + int des[2]; + if (pipe(des, mode)) return NULL; + + pid = fork(); + if (!pid) { + /* if read, stdout (1) is still open here + * (parent can read, child can write) */ + close(des[!read]); + dup2(des[read], read ? STDOUT_FILENO : STDIN_FILENO); + execv(path, (char * const *)argv); + return NULL; + } else { + /* if read, stdin (0) is still open here + * (child can read, parent can write) */ + close(des[read]); + return fdopen(des[!read], read ? (binary ? "rb" : "r") + : (binary ? "wb" : "w")); + } +} + +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"); + } + 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_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 + 2) * sizeof(char*)); + memset(argv, 0, (nargs + 2) * sizeof(char*)); + for (; nargs; --nargs) { + argv[nargs] = lua_tostring(L, nargs); + } + argv[0] = fname; + *pf = elua_popen_c(fname, argv, mode); + } else { + const char **argv = (const char**)alloca(2 * sizeof(char*)); + argv[0] = fname; + argv[1] = NULL; + *pf = elua_popen_c(fname, argv, mode); + } + return (!*pf) ? push_ret(L, 0, fname) : 1; +} \ No newline at end of file diff --git a/src/bin/elua/main.c b/src/bin/elua/main.c index ee06b87bb7..f332eb5f65 100644 --- a/src/bin/elua/main.c +++ b/src/bin/elua/main.c @@ -1,23 +1,6 @@ /* The Lua runtime component of the EFL */ #include -#ifndef _WIN32 -#include -#include -#include -#define pipe(x, mode) pipe(x) -#else -#include -#include "fork_win.c" -int fork_win(void); -#define fork fork_win -#define fdopen _fdopen -#define execv _execv -#define close _close -#define dup2 _dup2 -#define pipe(x, mode) _pipe(x, 4096, ((mode && mode[0] && mode[1] == 'b') \ - ? _O_BINARY : _O_TEXT) | _NO_NOINHERIT) -#endif #include "main.h" @@ -241,113 +224,6 @@ static int register_callbacks(lua_State *L) { return 1; } -static FILE *elua_popen(const char *path, const char *argv[], const char *mode) { - int read = (!mode || mode[0] == 'r'); - int binary = mode && mode[0] && mode[1] == 'b'; - pid_t pid; - - int des[2]; - if (pipe(des, mode)) return NULL; - - pid = fork(); - if (!pid) { - /* if read, stdout (1) is still open here - * (parent can read, child can write) */ - close(des[!read]); - dup2(des[read], read ? STDOUT_FILENO : STDIN_FILENO); - execv(path, (char * const *)argv); - return NULL; - } else { - /* if read, stdin (0) is still open here - * (child can read, parent can write) */ - close(des[read]); - return fdopen(des[!read], read ? (binary ? "rb" : "r") - : (binary ? "wb" : "w")); - } -} - -static int elua_exec(lua_State *L) { -#ifndef _WIN32 - pid_t cpid = fork(); - if (cpid < 0) { - lua_pushnil(L); - lua_pushstring(L, strerror(errno)); - return 2; - } - if (!cpid) { - const char **argv = alloca((lua_gettop(L) + 1) * sizeof(char*)); - int i; - for (i = 0; i < lua_gettop(L); ++i) { - argv[i] = lua_tostring(L, i + 1); - } - argv[lua_gettop(L)] = NULL; - execv(argv[0], (char*const*)argv); - exit(1); - } else { - int status; - if (waitpid(cpid, &status, 0) < 0) { - lua_pushnil(L); - lua_pushstring(L, strerror(errno)); - return 2; - } - lua_pushinteger(L, status); - return 1; - } -#else - char *buf, *cptr; - size_t buflen = 1; - int i; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - ZeroMemory(&si, sizeof(pi)); - - for (i = 1; i < lua_gettop(L); ++i) { - buflen += lua_objlen(L, i + 1) + 2; - } - - buf = alloca(buflen + 1); - cptr = buf; - - for (i = 1; i < lua_gettop(L); ++i) { - size_t l; - const char *arg = lua_tolstring(L, i + 1, &l); - *(cptr++) = '"'; - memcpy(cptr, l + 1, arg); - cptr += l; - *(cptr++) = '"' - if (i != (lua_gettop(L) - 1)) { - *(cptr++) = ' '; - } else { - cptr[0] = '\0'; - } - } - - if (!CreateProcess(lua_tostring(L, 1), buf, NULL, NULL, FALSE, 0, NULL, - NULL, &si, &pi)) { - LPVOID msgbuf; - DWORD dw = GetLastError(); - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER - | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msgbuf, - 0, NULL); - lua_pushnil(L); - lua_pushstring(L, (const char*)msgbuf); - LocalFree(msgbuf); - return 2; - } - - int status; - if (!GetExitCodeProcess(pi.hProcess, &status)) status = 0; - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - lua_pushinteger(L, status); - return 1; -#endif -} - static int elua_build_args(lua_State *L) { /* todo: will probably need adjustments for Windows */ int nargs = lua_gettop(L), n = 0, i; @@ -369,11 +245,13 @@ struct Main_Data { int status; }; +int elua_popen(lua_State *L); + const luaL_reg cutillib[] = { { "init_module" , init_module }, { "register_callbacks", register_callbacks }, - { "exec" , elua_exec }, { "build_args" , elua_build_args }, + { "popenv" , elua_popen }, { NULL , NULL } };