efl/src/lib/elua/io.c

371 lines
8.2 KiB
C

#ifdef _WIN32
# include <evil_private.h> /* realpath */
#endif
#include "elua_private.h"
/* 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 (elua_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 || elua_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");
elua_register(L, 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;
}