reformatted -- use spaces instead of tabs.

master
mark 19 years ago
parent bb1d518bc8
commit ff7e489f7b

@ -3,7 +3,7 @@ include ../conf
CFLAGS= $(WARNINGS) $(DEFINES) $(INCLUDES) CFLAGS= $(WARNINGS) $(DEFINES) $(INCLUDES)
DEFINES= -D_XOPEN_SOURCE=600 $(POSIX_SPAWN) $(ENVIRON) DEFINES= -D_XOPEN_SOURCE=600 $(POSIX_SPAWN) $(ENVIRON)
INCLUDES= $(LUAINC) INCLUDES= $(LUAINC)
WARNINGS= -W -Wall WARNINGS= -W -Wall -std=c89
LIBS= $(LUALIB) LIBS= $(LUALIB)
T= ex.so T= ex.so

@ -27,120 +27,109 @@ ENVIRON_DECL
/* -- nil error */ /* -- nil error */
extern int push_error(lua_State *L) extern int push_error(lua_State *L)
{ {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, strerror(errno)); lua_pushstring(L, strerror(errno));
return 2; return 2;
} }
/* name -- value/nil */ /* name -- value/nil */
static int ex_getenv(lua_State *L) static int ex_getenv(lua_State *L)
{ {
const char *nam = luaL_checkstring(L, 1); const char *nam = luaL_checkstring(L, 1);
char *val = getenv(nam); char *val = getenv(nam);
if (!val) if (!val)
return push_error(L); return push_error(L);
lua_pushstring(L, val); lua_pushstring(L, val);
return 1; return 1;
} }
/* name value -- true/nil error /* name value -- true/nil error
* name nil -- true/nil error */ * name nil -- true/nil error */
static int ex_setenv(lua_State *L) static int ex_setenv(lua_State *L)
{ {
const char *nam = luaL_checkstring(L, 1); const char *nam = luaL_checkstring(L, 1);
const char *val = lua_tostring(L, 2); const char *val = lua_tostring(L, 2);
int err = val ? setenv(nam, val, 1) : unsetenv(nam); int err = val ? setenv(nam, val, 1) : unsetenv(nam);
if (err == -1) return push_error(L); if (err == -1) return push_error(L);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
/* -- environment-table */ /* -- environment-table */
static int ex_environ(lua_State *L) static int ex_environ(lua_State *L)
{ {
const char *nam, *val, *end; const char *nam, *val, *end;
const char **env; const char **env;
lua_newtable(L); lua_newtable(L);
for (env = (const char **)environ; (nam = *env); env++) { for (env = (const char **)environ; (nam = *env); env++) {
end = strchr(val = strchr(nam, '=') + 1, '\0'); end = strchr(val = strchr(nam, '=') + 1, '\0');
lua_pushlstring(L, nam, val - nam - 1); lua_pushlstring(L, nam, val - nam - 1);
lua_pushlstring(L, val, end - val); lua_pushlstring(L, val, end - val);
lua_settable(L, -3); lua_settable(L, -3);
} }
return 1; return 1;
} }
/* -- pathname/nil error */ /* -- pathname/nil error */
static int ex_currentdir(lua_State *L) static int ex_currentdir(lua_State *L)
{ {
char pathname[PATH_MAX + 1]; char pathname[PATH_MAX + 1];
if (!getcwd(pathname, sizeof pathname)) if (!getcwd(pathname, sizeof pathname))
return push_error(L); return push_error(L);
lua_pushstring(L, pathname); lua_pushstring(L, pathname);
return 1; return 1;
} }
/* pathname -- true/nil error */ /* pathname -- true/nil error */
static int ex_chdir(lua_State *L) static int ex_chdir(lua_State *L)
{ {
const char *pathname = luaL_checkstring(L, 1); const char *pathname = luaL_checkstring(L, 1);
if (-1 == chdir(pathname)) if (-1 == chdir(pathname))
return push_error(L); return push_error(L);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
/* pathname -- true/nil error */ /* pathname -- true/nil error */
static int ex_mkdir(lua_State *L) static int ex_mkdir(lua_State *L)
{ {
const char *pathname = luaL_checkstring(L, 1); const char *pathname = luaL_checkstring(L, 1);
if (-1 == mkdir(pathname, 0777)) if (-1 == mkdir(pathname, 0777))
return push_error(L); return push_error(L);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
/* os.remove provides the correct semantics on POSIX systems */ /* Lua os.remove provides the correct semantics on POSIX systems */
#if 0
/* pathname -- true/nil error */
static int ex_remove(lua_State *L)
{
const char *pathname = luaL_checkstring(L, 1);
if (-1 == remove(pathname))
return push_error(L);
lua_pushboolean(L, 1);
return 1;
}
#endif
static FILE *check_file(lua_State *L, int idx, const char *argname) static FILE *check_file(lua_State *L, int idx, const char *argname)
{ {
FILE **pf; FILE **pf;
if (idx > 0) pf = luaL_checkudata(L, idx, LUA_FILEHANDLE); if (idx > 0) pf = luaL_checkudata(L, idx, LUA_FILEHANDLE);
else { else {
idx = absindex(L, idx); idx = absindex(L, idx);
pf = lua_touserdata(L, idx); pf = lua_touserdata(L, idx);
luaL_getmetatable(L, LUA_FILEHANDLE); luaL_getmetatable(L, LUA_FILEHANDLE);
if (!pf || !lua_getmetatable(L, idx) || !lua_rawequal(L, -1, -2)) if (!pf || !lua_getmetatable(L, idx) || !lua_rawequal(L, -1, -2))
luaL_error(L, "bad %s option (%s expected, got %s)", luaL_error(L, "bad %s option (%s expected, got %s)",
argname, LUA_FILEHANDLE, luaL_typename(L, idx)); argname, LUA_FILEHANDLE, luaL_typename(L, idx));
lua_pop(L, 2); lua_pop(L, 2);
} }
if (!*pf) return luaL_error(L, "attempt to use a closed file"), NULL; if (!*pf) return luaL_error(L, "attempt to use a closed file"), NULL;
return *pf; return *pf;
} }
static FILE **new_file(lua_State *L, int fd, const char *mode) static FILE **new_file(lua_State *L, int fd, const char *mode)
{ {
FILE **pf = lua_newuserdata(L, sizeof *pf); FILE **pf = lua_newuserdata(L, sizeof *pf);
*pf = 0; *pf = 0;
luaL_getmetatable(L, LUA_FILEHANDLE); luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
*pf = fdopen(fd, mode); *pf = fdopen(fd, mode);
return pf; return pf;
} }
@ -149,35 +138,35 @@ static FILE **new_file(lua_State *L, int fd, const char *mode)
/* pathname/file [entry] -- entry */ /* pathname/file [entry] -- entry */
static int ex_dirent(lua_State *L) static int ex_dirent(lua_State *L)
{ {
struct stat st; struct stat st;
switch (lua_type(L, 1)) { switch (lua_type(L, 1)) {
default: return luaL_typerror(L, 1, "file or pathname"); default: return luaL_typerror(L, 1, "file or pathname");
case LUA_TSTRING: { case LUA_TSTRING: {
const char *name = lua_tostring(L, 1); const char *name = lua_tostring(L, 1);
if (-1 == stat(name, &st)) if (-1 == stat(name, &st))
return push_error(L); return push_error(L);
} break; } break;
case LUA_TUSERDATA: { case LUA_TUSERDATA: {
FILE *f = check_file(L, 1, NULL); FILE *f = check_file(L, 1, NULL);
if (-1 == fstat(fileno(f), &st)) if (-1 == fstat(fileno(f), &st))
return push_error(L); return push_error(L);
} break; } break;
} }
if (lua_type(L, 2) != LUA_TTABLE) { if (lua_type(L, 2) != LUA_TTABLE) {
lua_settop(L, 1); lua_settop(L, 1);
new_dirent(L); new_dirent(L);
} }
else { else {
lua_settop(L, 2); lua_settop(L, 2);
} }
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode))
lua_pushliteral(L, "directory"); lua_pushliteral(L, "directory");
else else
lua_pushliteral(L, "file"); lua_pushliteral(L, "file");
lua_setfield(L, 2, "type"); lua_setfield(L, 2, "type");
lua_pushnumber(L, st.st_size); lua_pushnumber(L, st.st_size);
lua_setfield(L, 2, "size"); lua_setfield(L, 2, "size");
return 1; return 1;
} }
#define DIR_HANDLE "DIR*" #define DIR_HANDLE "DIR*"
@ -185,140 +174,141 @@ static int ex_dirent(lua_State *L)
/* ...diriter... -- ...diriter... pathname */ /* ...diriter... -- ...diriter... pathname */
static int diriter_getpathname(lua_State *L, int index) static int diriter_getpathname(lua_State *L, int index)
{ {
lua_pushvalue(L, index); lua_pushvalue(L, index);
lua_gettable(L, LUA_REGISTRYINDEX); lua_gettable(L, LUA_REGISTRYINDEX);
return 1; return 1;
} }
/* ...diriter... pathname -- ...diriter... */ /* ...diriter... pathname -- ...diriter... */
static int diriter_setpathname(lua_State *L, int index) static int diriter_setpathname(lua_State *L, int index)
{ {
size_t len; size_t len;
const char *path = lua_tolstring(L, -1, &len); const char *path = lua_tolstring(L, -1, &len);
if (path && path[len - 1] != *LUA_DIRSEP) { if (path && path[len - 1] != *LUA_DIRSEP) {
lua_pushliteral(L, LUA_DIRSEP); lua_pushliteral(L, LUA_DIRSEP);
lua_concat(L, 2); lua_concat(L, 2);
} }
lua_pushvalue(L, index); /* ... pathname diriter */ lua_pushvalue(L, index); /* ... pathname diriter */
lua_insert(L, -2); /* ... diriter pathname */ lua_insert(L, -2); /* ... diriter pathname */
lua_settable(L, LUA_REGISTRYINDEX); /* ... */ lua_settable(L, LUA_REGISTRYINDEX); /* ... */
return 0; return 0;
} }
/* diriter -- diriter */ /* diriter -- diriter */
static int diriter_close(lua_State *L) static int diriter_close(lua_State *L)
{ {
DIR **pd = lua_touserdata(L, 1); DIR **pd = lua_touserdata(L, 1);
if (*pd) { if (*pd) {
closedir(*pd); closedir(*pd);
*pd = 0; *pd = 0;
} }
lua_pushnil(L); lua_pushnil(L);
diriter_setpathname(L, 1); diriter_setpathname(L, 1);
return 0; return 0;
} }
static int isdotfile(const char *name) static int isdotfile(const char *name)
{ {
return name[0] == '.' && (name[1] == '\0' return name[0] == '.' && (name[1] == '\0'
|| (name[1] == '.' && name[2] == '\0')); || (name[1] == '.' && name[2] == '\0'));
} }
/* pathname -- iter state nil */ /* pathname -- iter state nil */
/* diriter ... -- entry */ /* diriter ... -- entry */
static int ex_dir(lua_State *L) static int ex_dir(lua_State *L)
{ {
const char *pathname; const char *pathname;
DIR **pd; DIR **pd;
struct dirent *d; struct dirent *d;
switch (lua_type(L, 1)) { switch (lua_type(L, 1)) {
default: return luaL_typerror(L, 1, "pathname"); default: return luaL_typerror(L, 1, "pathname");
case LUA_TSTRING: case LUA_TSTRING:
pathname = lua_tostring(L, 1); pathname = lua_tostring(L, 1);
lua_pushcfunction(L, ex_dir); /* pathname ... iter */ lua_pushcfunction(L, ex_dir); /* pathname ... iter */
pd = lua_newuserdata(L, sizeof *pd);/* pathname ... iter state */ pd = lua_newuserdata(L, sizeof *pd);/* pathname ... iter state */
*pd = opendir(pathname); *pd = opendir(pathname);
if (!*pd) return push_error(L); if (!*pd) return push_error(L);
luaL_getmetatable(L, DIR_HANDLE); /* pathname ... iter state M */ luaL_getmetatable(L, DIR_HANDLE); /* pathname ... iter state M */
lua_setmetatable(L, -2); /* pathname ... iter state */ lua_setmetatable(L, -2); /* pathname ... iter state */
lua_pushvalue(L, 1); /* pathname ... iter state pathname */ lua_pushvalue(L, 1); /* pathname ... iter state pathname */
diriter_setpathname(L, -2); /* pathname ... iter state */ diriter_setpathname(L, -2); /* pathname ... iter state */
return 2; return 2;
case LUA_TUSERDATA: case LUA_TUSERDATA:
pd = luaL_checkudata(L, 1, DIR_HANDLE); pd = luaL_checkudata(L, 1, DIR_HANDLE);
do d = readdir(*pd); do d = readdir(*pd);
while (d && isdotfile(d->d_name)); while (d && isdotfile(d->d_name)) continue;
if (!d) return push_error(L); if (!d) return push_error(L);
new_dirent(L); /* diriter ... entry */ new_dirent(L); /* diriter ... entry */
diriter_getpathname(L, 1); /* diriter ... entry dirpath */ diriter_getpathname(L, 1); /* diriter ... entry dir */
lua_pushstring(L, d->d_name); /* diriter ... entry dirpath name */ lua_pushstring(L, d->d_name); /* diriter ... entry dir name */
lua_pushvalue(L, -1); /* diriter ... entry dirpath name name */ lua_pushvalue(L, -1); /* diriter ... entry dir name name */
lua_setfield(L, -4, "name"); /* diriter ... entry dirpath name */ lua_setfield(L, -4, "name"); /* diriter ... entry dir name */
lua_concat(L, 2); /* diriter ... entry fullpath */ lua_concat(L, 2); /* diriter ... entry fullpath */
lua_replace(L, 1); /* fullpath ... entry */ lua_replace(L, 1); /* fullpath ... entry */
lua_replace(L, 2); /* fullpath entry ... */ lua_replace(L, 2); /* fullpath entry ... */
return ex_dirent(L); return ex_dirent(L);
} }
/*NOTREACHED*/ /*NOTREACHED*/
} }
static int file_lock(lua_State *L, FILE *f, const char *mode, long offset, long length) static int file_lock(lua_State *L,
FILE *f, const char *mode, long offset, long length)
{ {
struct flock k; struct flock k;
switch (*mode) { switch (*mode) {
case 'w': k.l_type = F_WRLCK; break; case 'w': k.l_type = F_WRLCK; break;
case 'r': k.l_type = F_RDLCK; break; case 'r': k.l_type = F_RDLCK; break;
case 'u': k.l_type = F_UNLCK; break; case 'u': k.l_type = F_UNLCK; break;
default: return luaL_error(L, "invalid mode"); default: return luaL_error(L, "invalid mode");
} }
k.l_whence = SEEK_SET; k.l_whence = SEEK_SET;
k.l_start = offset; k.l_start = offset;
k.l_len = length; k.l_len = length;
if (-1 == fcntl(fileno(f), F_SETLK, &k)) if (-1 == fcntl(fileno(f), F_SETLK, &k))
return push_error(L); return push_error(L);
lua_settop(L, 1); lua_settop(L, 1);
return 1; return 1;
} }
/* file mode [offset [length]] -- file/nil error */ /* file mode [offset [length]] -- file/nil error */
static int ex_lock(lua_State *L) static int ex_lock(lua_State *L)
{ {
FILE *f = check_file(L, 1, NULL); FILE *f = check_file(L, 1, NULL);
const char *mode = luaL_checkstring(L, 2); const char *mode = luaL_checkstring(L, 2);
long offset = luaL_optnumber(L, 3, 0); long offset = luaL_optnumber(L, 3, 0);
long length = luaL_optnumber(L, 4, 0); long length = luaL_optnumber(L, 4, 0);
return file_lock(L, f, mode, offset, length); return file_lock(L, f, mode, offset, length);
} }
/* file [offset [length]] -- file/nil error */ /* file [offset [length]] -- file/nil error */
static int ex_unlock(lua_State *L) static int ex_unlock(lua_State *L)
{ {
lua_pushliteral(L, "u"); lua_pushliteral(L, "u");
lua_insert(L, 2); lua_insert(L, 2);
return ex_lock(L); return ex_lock(L);
} }
static int closeonexec(int d) static int closeonexec(int d)
{ {
int fl = fcntl(d, F_GETFD); int fl = fcntl(d, F_GETFD);
if (fl != -1) if (fl != -1)
fl = fcntl(d, F_SETFD, fl | FD_CLOEXEC); fl = fcntl(d, F_SETFD, fl | FD_CLOEXEC);
return fl; return fl;
} }
/* -- in out/nil error */ /* -- in out/nil error */
static int ex_pipe(lua_State *L) static int ex_pipe(lua_State *L)
{ {
int fd[2]; int fd[2];
if (-1 == pipe(fd)) if (-1 == pipe(fd))
return push_error(L); return push_error(L);
closeonexec(fd[0]); closeonexec(fd[0]);
closeonexec(fd[1]); closeonexec(fd[1]);
new_file(L, fd[0], "r"); new_file(L, fd[0], "r");
new_file(L, fd[1], "w"); new_file(L, fd[1], "w");
return 2; return 2;
} }
@ -326,98 +316,97 @@ static int ex_pipe(lua_State *L)
* interval units -- */ * interval units -- */
static int ex_sleep(lua_State *L) static int ex_sleep(lua_State *L)
{ {
lua_Number interval = luaL_checknumber(L, 1); lua_Number interval = luaL_checknumber(L, 1);
lua_Number units = luaL_optnumber(L, 2, 1); lua_Number units = luaL_optnumber(L, 2, 1);
usleep(1e6 * interval / units); usleep(1e6 * interval / units);
return 0; return 0;
} }
static void get_redirect(lua_State *L, int idx, const char *stdname, struct spawn_params *p) static void get_redirect(lua_State *L,
int idx, const char *stdname, struct spawn_params *p)
{ {
lua_getfield(L, idx, stdname); lua_getfield(L, idx, stdname);
if (!lua_isnil(L, -1)) if (!lua_isnil(L, -1))
spawn_param_redirect(p, stdname, fileno(check_file(L, -1, stdname))); spawn_param_redirect(p, stdname, fileno(check_file(L, -1, stdname)));
lua_pop(L, 1); lua_pop(L, 1);
} }
/* filename [args-opts] -- proc/nil error */ /* filename [args-opts] -- proc/nil error */
/* args-opts -- proc/nil error */ /* args-opts -- proc/nil error */
static int ex_spawn(lua_State *L) static int ex_spawn(lua_State *L)
{ {
struct spawn_params *params; struct spawn_params *params;
int have_options; int have_options;
switch (lua_type(L, 1)) {
switch (lua_type(L, 1)) { default: return luaL_typerror(L, 1, "string or table");
default: return luaL_typerror(L, 1, "string or table"); case LUA_TSTRING:
case LUA_TSTRING: switch (lua_type(L, 2)) {
switch (lua_type(L, 2)) { default: return luaL_typerror(L, 2, "table");
default: return luaL_typerror(L, 2, "table"); case LUA_TNONE: have_options = 0; break;
case LUA_TNONE: have_options = 0; break; case LUA_TTABLE: have_options = 1; break;
case LUA_TTABLE: have_options = 1; break; }
} break;
break; case LUA_TTABLE:
case LUA_TTABLE: have_options = 1;
have_options = 1; lua_getfield(L, 1, "command"); /* opts ... cmd */
lua_getfield(L, 1, "command"); /* opts ... cmd */ if (!lua_isnil(L, -1)) {
if (!lua_isnil(L, -1)) { /* convert {command=command,arg1,...} to command {arg1,...} */
/* convert {command=command,arg1,...} to command {arg1,...} */ lua_insert(L, 1); /* cmd opts ... */
lua_insert(L, 1); /* cmd opts ... */ }
} else {
else { /* convert {arg0,arg1,...} to arg0 {arg1,...} */
/* convert {arg0,arg1,...} to arg0 {arg1,...} */ size_t i, n = lua_objlen(L, 1);
size_t i, n = lua_objlen(L, 1); lua_rawgeti(L, 1, 1); /* opts ... nil cmd */
lua_rawgeti(L, 1, 1); /* opts ... nil cmd */ lua_insert(L, 1); /* cmd opts ... nil */
lua_insert(L, 1); /* cmd opts ... nil */ for (i = 2; i <= n; i++) {
for (i = 2; i <= n; i++) { lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */
lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */ lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */
lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */ }
} lua_rawseti(L, 2, n); /* cmd opts ... */
lua_rawseti(L, 2, n); /* cmd opts ... */ }
} if (lua_type(L, 1) != LUA_TSTRING)
if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad command option (string expected, got %s)",
return luaL_error(L, "bad command option (string expected, got %s)", luaL_typename(L, 1));
luaL_typename(L, 1)); break;
break; }
} params = spawn_param_init(L);
/* get filename to execute */
params = spawn_param_init(L); spawn_param_filename(params, lua_tostring(L, 1));
/* get arguments, environment, and redirections */
/* get filename to execute */ if (have_options) {
spawn_param_filename(params, lua_tostring(L, 1)); lua_getfield(L, 2, "args"); /* cmd opts ... argtab */
switch (lua_type(L, -1)) {
/* get arguments, environment, and redirections */ default:
if (have_options) { return luaL_error(L, "bad args option (table expected, got %s)",
lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ luaL_typename(L, -1));
switch (lua_type(L, -1)) { case LUA_TNIL:
default: return luaL_error(L, "bad args option (table expected, got %s)", lua_pop(L, 1); /* cmd opts ... */
luaL_typename(L, -1)); lua_pushvalue(L, 2); /* cmd opts ... opts */
case LUA_TNIL: if (0) /*FALLTHRU*/
lua_pop(L, 1); /* cmd opts ... */ case LUA_TTABLE:
lua_pushvalue(L, 2); /* cmd opts ... opts */ if (lua_objlen(L, 2) > 0)
if (0) /*FALLTHRU*/ return
case LUA_TTABLE: luaL_error(L, "cannot specify both the args option and array values");
if (lua_objlen(L, 2) > 0) spawn_param_args(params); /* cmd opts ... */
return luaL_error(L, "cannot specify both the args option and array values"); break;
spawn_param_args(params); /* cmd opts ... */ }
break; lua_getfield(L, 2, "env"); /* cmd opts ... envtab */
} switch (lua_type(L, -1)) {
lua_getfield(L, 2, "env"); /* cmd opts ... envtab */ default:
switch (lua_type(L, -1)) { return luaL_error(L, "bad env option (table expected, got %s)",
default: return luaL_error(L, "bad env option (table expected, got %s)", luaL_typename(L, -1));
luaL_typename(L, -1)); case LUA_TNIL:
case LUA_TNIL: break;
break; case LUA_TTABLE:
case LUA_TTABLE: spawn_param_env(params); /* cmd opts ... */
spawn_param_env(params); /* cmd opts ... */ break;
break; }
} get_redirect(L, 2, "stdin", params); /* cmd opts ... */
get_redirect(L, 2, "stdin", params); /* cmd opts ... */ get_redirect(L, 2, "stdout", params); /* cmd opts ... */
get_redirect(L, 2, "stdout", params); /* cmd opts ... */ get_redirect(L, 2, "stderr", params); /* cmd opts ... */
get_redirect(L, 2, "stderr", params); /* cmd opts ... */ }
} return spawn_param_execute(params); /* proc/nil error */
return spawn_param_execute(params); /* proc/nil error */
} }
@ -425,90 +414,79 @@ static int ex_spawn(lua_State *L)
* closures from table 'from' or by creating new closures */ * closures from table 'from' or by creating new closures */
static void copyfields(lua_State *L, const luaL_reg *l, int from, int to) static void copyfields(lua_State *L, const luaL_reg *l, int from, int to)
{ {
for (to = absindex(L, to); l->name; l++) { for (to = absindex(L, to); l->name; l++) {
lua_getfield(L, from, l->name); lua_getfield(L, from, l->name);
if (lua_isnil(L, -1)) { if (lua_isnil(L, -1)) {
lua_pop(L, 1); lua_pop(L, 1);
lua_pushcfunction(L, l->func); lua_pushcfunction(L, l->func);
} }
lua_setfield(L, to, l->name); lua_setfield(L, to, l->name);
} }
} }
int luaopen_ex(lua_State *L) int luaopen_ex(lua_State *L)
{ {
const luaL_reg ex_iolib[] = { const char *name = lua_tostring(L, 1);
{"pipe", ex_pipe}, int ex;
const luaL_reg ex_iolib[] = {
{"pipe", ex_pipe},
#define ex_iofile_methods (ex_iolib + 1) #define ex_iofile_methods (ex_iolib + 1)
{"lock", ex_lock}, {"lock", ex_lock},
{"unlock", ex_unlock}, {"unlock", ex_unlock},
{0,0} }; {0,0} };
const luaL_reg ex_oslib[] = { const luaL_reg ex_oslib[] = {
{"getenv", ex_getenv}, /* environment */
{"setenv", ex_setenv}, {"getenv", ex_getenv},
{"environ", ex_environ}, {"setenv", ex_setenv},
{"environ", ex_environ},
{"currentdir", ex_currentdir}, /* file system */
{"chdir", ex_chdir}, {"currentdir", ex_currentdir},
{"mkdir", ex_mkdir}, {"chdir", ex_chdir},
#if 0 {"mkdir", ex_mkdir},
{"remove", ex_remove}, {"dir", ex_dir},
#endif {"dirent", ex_dirent},
/* process control */
{"dir", ex_dir}, {"sleep", ex_sleep},
{"dirent", ex_dirent}, {"spawn", ex_spawn},
{0,0} };
{"sleep", ex_sleep}, const luaL_reg ex_diriter_methods[] = {
{"spawn", ex_spawn}, {"__gc", diriter_close},
{0,0} }; {0,0} };
const luaL_reg ex_diriter_methods[] = { const luaL_reg ex_process_methods[] = {
{"__gc", diriter_close}, {"__tostring", process_tostring},
{0,0} };
const luaL_reg ex_process_methods[] = {
{"__tostring", process_tostring},
#define ex_process_functions (ex_process_methods + 1) #define ex_process_functions (ex_process_methods + 1)
{"wait", process_wait}, {"wait", process_wait},
{0,0} }; {0,0} };
/* diriter metatable */
int ex; luaL_newmetatable(L, DIR_HANDLE); /* . D */
const char *name = lua_tostring(L, 1); luaL_register(L, 0, ex_diriter_methods); /* . D */
/* proc metatable */
/* diriter metatable */ luaL_newmetatable(L, PROCESS_HANDLE); /* . P */
luaL_newmetatable(L, DIR_HANDLE); /* . D */ luaL_register(L, 0, ex_process_methods); /* . P */
luaL_register(L, 0, ex_diriter_methods); /* . D */ lua_pushvalue(L, -1); /* . P P */
lua_setfield(L, -2, "__index"); /* . P */
/* proc metatable */ /* make all functions available via ex. namespace */
luaL_newmetatable(L, PROCESS_HANDLE); /* . P */ luaL_register(L, name, ex_oslib); /* . P ex */
luaL_register(L, 0, ex_process_methods); /* . P */ luaL_register(L, 0, ex_iolib);
lua_pushvalue(L, -1); /* . P P */ copyfields(L, ex_process_functions, -2, -1);
lua_setfield(L, -2, "__index"); /* . P */ ex = lua_gettop(L);
/* extend the os table */
/* make all functions available via ex. namespace */ lua_getglobal(L, "os"); /* . os */
luaL_register(L, name, ex_oslib); /* . P ex */ if (lua_isnil(L, -1)) return luaL_error(L, "os not loaded");
luaL_register(L, 0, ex_iolib); copyfields(L, ex_oslib, ex, -1);
copyfields(L, ex_process_functions, -2, -1); lua_getfield(L, -1, "remove");
ex = lua_gettop(L); lua_setfield(L, ex, "remove");
/* extend the io table */
/* extend the os table */ lua_getglobal(L, "io"); /* . io */
lua_getglobal(L, "os"); /* . os */ if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded");
if (lua_isnil(L, -1)) return luaL_error(L, "os not loaded"); copyfields(L, ex_iolib, ex, -1);
copyfields(L, ex_oslib, ex, -1); lua_getfield(L, ex, "pipe"); /* . io ex_pipe */
lua_getfield(L, -1, "remove"); lua_getfield(L, -2, "stderr"); /* . io ex_pipe io_stderr */
lua_setfield(L, ex, "remove"); lua_getfenv(L, -1); /* . io ex_pipe io_stderr E */
lua_setfenv(L, -3); /* . io ex_pipe io_stderr */
/* extend the io table */ /* extend the io.file metatable */
lua_getglobal(L, "io"); /* . io */ luaL_getmetatable(L, LUA_FILEHANDLE); /* . F */
if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded"); if (lua_isnil(L, -1)) return luaL_error(L, "can't find FILE* metatable");
copyfields(L, ex_iolib, ex, -1); copyfields(L, ex_iofile_methods, ex, -1);
lua_getfield(L, ex, "pipe"); /* . io ex_pipe */ return 1;
lua_getfield(L, -2, "stderr"); /* . io ex_pipe io_stderr */
lua_getfenv(L, -1); /* . io ex_pipe io_stderr E */
lua_setfenv(L, -3); /* . io ex_pipe io_stderr */
/* extend the io.file metatable */
luaL_getmetatable(L, LUA_FILEHANDLE); /* . F */
if (lua_isnil(L, -1)) return luaL_error(L, "can't find FILE* metatable");
copyfields(L, ex_iofile_methods, ex, -1);
return 1;
} }

@ -19,57 +19,63 @@ ENVIRON_DECL
#endif #endif
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *act) int posix_spawn_file_actions_init(
posix_spawn_file_actions_t *act)
{ {
act->dups[0] = act->dups[1] = act->dups[2] = -1; act->dups[0] = act->dups[1] = act->dups[2] = -1;
return 0; return 0;
} }
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *act, int d, int n) int posix_spawn_file_actions_adddup2(
posix_spawn_file_actions_t *act,
int d,
int n)
{ {
/* good faith effort to determine validity of descriptors */ /* good faith effort to determine validity of descriptors */
if (d < 0 || OPEN_MAX < d || n < 0 || OPEN_MAX < n) { if (d < 0 || OPEN_MAX < d || n < 0 || OPEN_MAX < n) {
errno = EBADF; errno = EBADF;
return -1; return -1;
} }
/* we only support duplication to 0,1,2 */ /* we only support duplication to 0,1,2 */
if (2 < n) { if (2 < n) {
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
act->dups[n] = d; act->dups[n] = d;
return 0; return 0;
} }
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *act) int posix_spawn_file_actions_destroy(
posix_spawn_file_actions_t *act)
{ {
return 0; return 0;
} }
int posix_spawnp(pid_t *restrict ppid, int posix_spawnp(
const char *restrict path, pid_t *restrict ppid,
const posix_spawn_file_actions_t *act, const char *restrict path,
const posix_spawnattr_t *restrict attrp, const posix_spawn_file_actions_t *act,
char *const argv[restrict], const posix_spawnattr_t *restrict attrp,
char *const envp[restrict]) char *const argv[restrict],
char *const envp[restrict])
{ {
if (!ppid || !path || !argv || !envp) if (!ppid || !path || !argv || !envp)
return EINVAL; return EINVAL;
if (attrp) if (attrp)
return EINVAL; return EINVAL;
switch (*ppid = fork()) { switch (*ppid = fork()) {
case -1: return -1; case -1: return -1;
default: return 0; default: return 0;
case 0: case 0:
if (act) { if (act) {
int i; int i;
for (i = 0; i < 3; i++) for (i = 0; i < 3; i++)
if (act->dups[i] != -1 && -1 == dup2(act->dups[i], i)) if (act->dups[i] != -1 && -1 == dup2(act->dups[i], i))
_exit(111); _exit(111);
} }
environ = (char **)envp; environ = (char **)envp;
execvp(path, argv); execvp(path, argv);
_exit(111); _exit(111);
/*NOTREACHED*/ /*NOTREACHED*/
} }
} }

@ -14,39 +14,85 @@
typedef void *posix_spawnattr_t; typedef void *posix_spawnattr_t;
enum { enum {
POSIX_SPAWN_RESETIDS, POSIX_SPAWN_RESETIDS,
POSIX_SPAWN_SETPGROUP, POSIX_SPAWN_SETPGROUP,
POSIX_SPAWN_SETSCHEDPARAM, POSIX_SPAWN_SETSCHEDPARAM,
POSIX_SPAWN_SETSCHEDULER, POSIX_SPAWN_SETSCHEDULER,
POSIX_SPAWN_SETSIGDEF, POSIX_SPAWN_SETSIGDEF,
POSIX_SPAWN_SETSIGMASK, POSIX_SPAWN_SETSIGMASK,
}; };
int posix_spawnattr_init(posix_spawnattr_t *attrp); int posix_spawnattr_init(posix_spawnattr_t *attrp);
int posix_spawnattr_getflags(const posix_spawnattr_t *restrict attrp, short *restrict flags); int posix_spawnattr_getflags(
int posix_spawnattr_setflags(posix_spawnattr_t *attrp, short flags); const posix_spawnattr_t *restrict attrp,
int posix_spawnattr_getpgroup(const posix_spawnattr_t *restrict attrp, pid_t *restrict pgroup); short *restrict flags);
int posix_spawnattr_setpgroup(posix_spawnattr_t *attrp, pid_t pgroup); int posix_spawnattr_setflags(
int posix_spawnattr_getschedparam(const posix_spawnattr_t *restrict attrp, struct sched_param *restrict schedparam); posix_spawnattr_t *attrp,
int posix_spawnattr_setschedparam(posix_spawnattr_t *restrict attrp, const struct sched_param *restrict schedparam); short flags);
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *restrict attrp, int *restrict schedpolicy); int posix_spawnattr_getpgroup(
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attrp, int schedpolicy); const posix_spawnattr_t *restrict attrp,
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *restrict attrp, sigset_t *restrict sigdefault); pid_t *restrict pgroup);
int posix_spawnattr_setsigdefault(posix_spawnattr_t *restrict attrp, const sigset_t *restrict sigdefault); int posix_spawnattr_setpgroup(
int posix_spawnattr_getsigmask(const posix_spawnattr_t *restrict attrp, sigset_t *restrict sigmask); posix_spawnattr_t *attrp,
int posix_spawnattr_setsigmask(posix_spawnattr_t *restrict attrp, const sigset_t *restrict sigmask); pid_t pgroup);
int posix_spawnattr_getschedparam(
const posix_spawnattr_t *restrict attrp,
struct sched_param *restrict schedparam);
int posix_spawnattr_setschedparam(
posix_spawnattr_t *restrict attrp,
const struct sched_param *restrict schedparam);
int posix_spawnattr_getschedpolicy(
const posix_spawnattr_t *restrict attrp,
int *restrict schedpolicy);
int posix_spawnattr_setschedpolicy(
posix_spawnattr_t *attrp,
int schedpolicy);
int posix_spawnattr_getsigdefault(
const posix_spawnattr_t *restrict attrp,
sigset_t *restrict sigdefault);
int posix_spawnattr_setsigdefault(
posix_spawnattr_t *restrict attrp,
const sigset_t *restrict sigdefault);
int posix_spawnattr_getsigmask(
const posix_spawnattr_t *restrict attrp,
sigset_t *restrict sigmask);
int posix_spawnattr_setsigmask(
posix_spawnattr_t *restrict attrp,
const sigset_t *restrict sigmask);
int posix_spawnattr_destroy(posix_spawnattr_t *attrp); int posix_spawnattr_destroy(posix_spawnattr_t *attrp);
typedef struct posix_spawn_file_actions posix_spawn_file_actions_t; typedef struct posix_spawn_file_actions posix_spawn_file_actions_t;
struct posix_spawn_file_actions { struct posix_spawn_file_actions {
int dups[3]; int dups[3];
}; };
int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions); int posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions);
int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, int filedes, int newfiledes); int posix_spawn_file_actions_adddup2(
int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, int filedes); posix_spawn_file_actions_t *file_actions,
int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *restrict file_actions, int filedes, const char *restrict path, int oflag, mode_t mode); int filedes,
int newfiledes);
int posix_spawn_file_actions_addclose(
posix_spawn_file_actions_t *file_actions,
int filedes);
int posix_spawn_file_actions_addopen(
posix_spawn_file_actions_t *restrict file_actions,
int filedes,
const char *restrict path,
int oflag,
mode_t mode);
int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions); int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions);
int posix_spawn(pid_t *restrict, const char *restrict, const posix_spawn_file_actions_t *, const posix_spawnattr_t *restrict, char *const argv[restrict], char *const envp[restrict]); int posix_spawn(
int posix_spawnp(pid_t *restrict, const char *restrict, const posix_spawn_file_actions_t *, const posix_spawnattr_t *restrict, char *const argv[restrict], char *const envp[restrict]); pid_t *restrict,
const char *restrict,
const posix_spawn_file_actions_t *,
const posix_spawnattr_t *restrict,
char *const argv[restrict],
char *const envp[restrict]);
int posix_spawnp(
pid_t *restrict,
const char *restrict,
const posix_spawn_file_actions_t *,
const posix_spawnattr_t *restrict,
char *const argv[restrict],
char *const envp[restrict]);

@ -18,26 +18,26 @@ ENVIRON_DECL
#include "spawn.h" #include "spawn.h"
struct spawn_params { struct spawn_params {
lua_State *L; lua_State *L;
const char *command, **argv, **envp; const char *command, **argv, **envp;
posix_spawn_file_actions_t redirect; posix_spawn_file_actions_t redirect;
}; };
extern int push_error(lua_State *L); extern int push_error(lua_State *L);
struct spawn_params *spawn_param_init(lua_State *L) struct spawn_params *spawn_param_init(lua_State *L)
{ {
struct spawn_params *p = lua_newuserdata(L, sizeof *p); struct spawn_params *p = lua_newuserdata(L, sizeof *p);
p->L = L; p->L = L;
p->command = 0; p->command = 0;
p->argv = p->envp = 0; p->argv = p->envp = 0;
posix_spawn_file_actions_init(&p->redirect); posix_spawn_file_actions_init(&p->redirect);
return p; return p;
} }
void spawn_param_filename(struct spawn_params *p, const char *filename) void spawn_param_filename(struct spawn_params *p, const char *filename)
{ {
p->command = filename; p->command = filename;
} }
/* Converts a Lua array of strings to a null-terminated array of char pointers. /* Converts a Lua array of strings to a null-terminated array of char pointers.
@ -51,108 +51,109 @@ void spawn_param_filename(struct spawn_params *p, const char *filename)
/* ... array -- ... vector */ /* ... array -- ... vector */
static const char **make_vector(lua_State *L) static const char **make_vector(lua_State *L)
{ {
size_t i, n = lua_objlen(L, -1); size_t i, n = lua_objlen(L, -1);
const char **vec = lua_newuserdata(L, (n + 2) * sizeof *vec); const char **vec = lua_newuserdata(L, (n + 2) * sizeof *vec);
/* ... arr vec */ /* ... arr vec */
for (i = 0; i <= n; i++) { for (i = 0; i <= n; i++) {
lua_rawgeti(L, -2, i); /* ... arr vec elem */ lua_rawgeti(L, -2, i); /* ... arr vec elem */
vec[i] = lua_tostring(L, -1); vec[i] = lua_tostring(L, -1);
lua_pop(L, 1); /* ... arr vec */ lua_pop(L, 1); /* ... arr vec */
} }
vec[n + 1] = 0; vec[n + 1] = 0;
lua_replace(L, -2); /* ... vector */ lua_replace(L, -2); /* ... vector */
return vec; return vec;
} }
/* ... argtab */ /* ... argtab */
void spawn_param_args(struct spawn_params *p) void spawn_param_args(struct spawn_params *p)
{ {
const char **argv = make_vector(p->L); const char **argv = make_vector(p->L);
if (!argv[0]) argv[0] = p->command; if (!argv[0]) argv[0] = p->command;
p->argv = argv; p->argv = argv;
} }
/* ... envtab */ /* ... envtab */
void spawn_param_env(struct spawn_params *p) void spawn_param_env(struct spawn_params *p)
{ {
size_t i = 0; size_t i = 0;
luaL_Buffer estr; luaL_Buffer estr;
luaL_buffinit(p->L, &estr); luaL_buffinit(p->L, &estr);
lua_newtable(p->L); /* ... envtab arr */ lua_newtable(p->L); /* ... envtab arr */
lua_pushnil(p->L); /* ... envtab arr nil */ lua_pushnil(p->L); /* ... envtab arr nil */
for (i = 0; lua_next(p->L, -3); i++) { /* ... envtab arr k v */ for (i = 0; lua_next(p->L, -3); i++) {/* ... envtab arr k v */
luaL_prepbuffer(&estr); luaL_prepbuffer(&estr);
lua_pushvalue(p->L, -2); /* ... envtab arr k v k */ lua_pushvalue(p->L, -2); /* ... envtab arr k v k */
luaL_addvalue(&estr); luaL_addvalue(&estr);
luaL_putchar(&estr, '='); luaL_putchar(&estr, '=');
lua_pop(p->L, 1); /* ... envtab arr k v */ lua_pop(p->L, 1); /* ... envtab arr k v */
luaL_addvalue(&estr); luaL_addvalue(&estr);
lua_pop(p->L, 1); /* ... envtab arr k */ lua_pop(p->L, 1); /* ... envtab arr k */
luaL_pushresult(&estr); /* ... envtab arr k estr */ luaL_pushresult(&estr); /* ... envtab arr k estr */
lua_rawseti(p->L, -3, i); /* ... envtab arr[n]=estr k */ lua_rawseti(p->L, -3, i); /* ... envtab arr[n]=estr k */
} /* ... envtab arr */ } /* ... envtab arr */
lua_replace(p->L, -2); /* ... arr */ lua_replace(p->L, -2); /* ... arr */
make_vector(p->L); /* ... arr */ make_vector(p->L); /* ... arr */
} }
void spawn_param_redirect(struct spawn_params *p, const char *stdname, int fd) void spawn_param_redirect(struct spawn_params *p, const char *stdname, int fd)
{ {
int d; int d;
switch (stdname[3]) { switch (stdname[3]) {
case 'i': d = STDIN_FILENO; break; case 'i': d = STDIN_FILENO; break;
case 'o': d = STDOUT_FILENO; break; case 'o': d = STDOUT_FILENO; break;
case 'e': d = STDERR_FILENO; break; case 'e': d = STDERR_FILENO; break;
} }
posix_spawn_file_actions_adddup2(&p->redirect, fd, d); posix_spawn_file_actions_adddup2(&p->redirect, fd, d);
} }
struct process { struct process {
int status; int status;
pid_t pid; pid_t pid;
}; };
int spawn_param_execute(struct spawn_params *p) int spawn_param_execute(struct spawn_params *p)
{ {
lua_State *L = p->L; lua_State *L = p->L;
int ret; int ret;
struct process *proc; struct process *proc;
if (!p->argv) { if (!p->argv) {
p->argv = lua_newuserdata(L, 2 * sizeof *p->argv); p->argv = lua_newuserdata(L, 2 * sizeof *p->argv);
p->argv[0] = p->command; p->argv[0] = p->command;
p->argv[1] = 0; p->argv[1] = 0;
} }
if (!p->envp) if (!p->envp)
p->envp = (const char **)environ; p->envp = (const char **)environ;
proc = lua_newuserdata(L, sizeof *proc); proc = lua_newuserdata(L, sizeof *proc);
luaL_getmetatable(L, PROCESS_HANDLE); luaL_getmetatable(L, PROCESS_HANDLE);
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
proc->status = -1; proc->status = -1;
ret = posix_spawnp(&proc->pid, p->command, &p->redirect, 0, (char *const *)p->argv, (char *const *)p->envp); ret = posix_spawnp(&proc->pid, p->command, &p->redirect, 0,
posix_spawn_file_actions_destroy(&p->redirect); (char *const *)p->argv, (char *const *)p->envp);
return ret != 0 ? push_error(L) : 1; posix_spawn_file_actions_destroy(&p->redirect);
return ret != 0 ? push_error(L) : 1;
} }
/* proc -- exitcode/nil error */ /* proc -- exitcode/nil error */
int process_wait(lua_State *L) int process_wait(lua_State *L)
{ {
struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE);
if (p->status == -1) { if (p->status == -1) {
int status; int status;
if (-1 == waitpid(p->pid, &status, 0)) if (-1 == waitpid(p->pid, &status, 0))
return push_error(L); return push_error(L);
p->status = WEXITSTATUS(status); p->status = WEXITSTATUS(status);
} }
lua_pushnumber(L, p->status); lua_pushnumber(L, p->status);
return 1; return 1;
} }
/* proc -- string */ /* proc -- string */
int process_tostring(lua_State *L) int process_tostring(lua_State *L)
{ {
struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE);
char buf[40]; char buf[40];
lua_pushlstring(L, buf, lua_pushlstring(L, buf,
sprintf(buf, "process (%lu, %s)", (unsigned long)p->pid, sprintf(buf, "process (%lu, %s)", (unsigned long)p->pid,
p->status==-1 ? "running" : "terminated")); p->status==-1 ? "running" : "terminated"));
return 1; return 1;
} }

@ -19,6 +19,7 @@ void spawn_param_args(struct spawn_params *p);
void spawn_param_env(struct spawn_params *p); void spawn_param_env(struct spawn_params *p);
void spawn_param_redirect(struct spawn_params *p, const char *stdname, int fd); void spawn_param_redirect(struct spawn_params *p, const char *stdname, int fd);
int spawn_param_execute(struct spawn_params *p); int spawn_param_execute(struct spawn_params *p);
int process_wait(lua_State *L); int process_wait(lua_State *L);
int process_tostring(lua_State *L); int process_tostring(lua_State *L);

@ -8,66 +8,62 @@
#include "dirent.h" #include "dirent.h"
struct DIR_tag { struct DIR_tag {
int first; int first;
HANDLE hf; HANDLE hf;
WIN32_FIND_DATA fd; WIN32_FIND_DATA fd;
}; };
static char *mkpattern(const char *name) static char *mkpattern(const char *name)
{ {
static const char suffix[] = "\\*"; static const char suffix[] = "\\*";
size_t len = strlen(name); size_t len = strlen(name);
char *pattern = malloc(len + sizeof suffix); char *pattern = malloc(len + sizeof suffix);
if (pattern) if (pattern)
strcpy(memcpy(pattern, name, len) + len, suffix); strcpy(memcpy(pattern, name, len) + len, suffix);
return pattern; return pattern;
} }
DIR * DIR *opendir(const char *name)
opendir(const char *name)
{ {
DIR *pi = malloc(sizeof *pi); DIR *pi = malloc(sizeof *pi);
char *pattern = mkpattern(name); char *pattern = mkpattern(name);
if (!pi || !pattern if (!pi || !pattern
|| INVALID_HANDLE_VALUE == (pi->hf = FindFirstFile(pattern, &pi->fd))) { || INVALID_HANDLE_VALUE == (pi->hf = FindFirstFile(pattern, &pi->fd))) {
free(pi); free(pi);
pi = 0; pi = 0;
} }
else { else {
pi->first = 1; pi->first = 1;
} }
free(pattern); free(pattern);
return pi; return pi;
} }
static int isdotfile(const char *name) static int isdotfile(const char *name)
{ {
return name[0] == '.' && (name[1] == 0 return name[0] == '.' && (name[1] == 0
|| (name[1] == '.' && name[2] == 0)); || (name[1] == '.' && name[2] == 0));
} }
const WIN32_FIND_DATA * const WIN32_FIND_DATA *readdir(DIR *pi)
readdir(DIR *pi)
{ {
switch (pi->first) do { switch (pi->first) do {
default: default:
if (!FindNextFile(pi->hf, &pi->fd)) { if (!FindNextFile(pi->hf, &pi->fd)) {
FindClose(pi->hf); FindClose(pi->hf);
pi->hf = INVALID_HANDLE_VALUE; pi->hf = INVALID_HANDLE_VALUE;
return 0; return 0;
} }
case 1: pi->first = 0; case 1: pi->first = 0;
} while (isdotfile(pi->fd.cFileName)); } while (isdotfile(pi->fd.cFileName));
return &pi->fd; return &pi->fd;
} }
void void closedir(DIR *pi)
closedir(DIR *pi)
{ {
if (pi->hf != INVALID_HANDLE_VALUE) if (pi->hf != INVALID_HANDLE_VALUE) {
{ FindClose(pi->hf);
FindClose(pi->hf); pi->hf = INVALID_HANDLE_VALUE;
pi->hf = INVALID_HANDLE_VALUE; }
} free(pi);
free(pi);
} }

@ -29,149 +29,150 @@
/* name -- value/nil */ /* name -- value/nil */
static int ex_getenv(lua_State *L) static int ex_getenv(lua_State *L)
{ {
const char *nam = luaL_checkstring(L, 1); const char *nam = luaL_checkstring(L, 1);
char sval[256], *val = sval; char sval[256], *val = sval;
size_t len = GetEnvironmentVariable(nam, val, sizeof sval); size_t len = GetEnvironmentVariable(nam, val, sizeof sval);
if (sizeof sval < len) { if (sizeof sval < len) {
len = GetEnvironmentVariable(nam, val = lua_newuserdata(len), len); len = GetEnvironmentVariable(nam, val = lua_newuserdata(len), len);
} }
if (len == 0) if (len == 0)
return push_error(L); return push_error(L);
lua_pushlstring(L, val, len); lua_pushlstring(L, val, len);
return 1; return 1;
} }
/* name value -- true/nil error /* name value -- true/nil error
* name nil -- true/nil error */ * name nil -- true/nil error */
static int ex_setenv(lua_State *L) static int ex_setenv(lua_State *L)
{ {
const char *nam = luaL_checkstring(L, 1); const char *nam = luaL_checkstring(L, 1);
const char *val = lua_tostring(L, 2); const char *val = lua_tostring(L, 2);
if (!SetEnvironmentVariable(nam, val)) if (!SetEnvironmentVariable(nam, val))
return push_error(L); return push_error(L);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
/* -- environment-table */ /* -- environment-table */
static int ex_environ(lua_State *L) static int ex_environ(lua_State *L)
{ {
const char *nam, *val, *end; const char *nam, *val, *end;
const char *envs = GetEnvironmentStrings(); const char *envs = GetEnvironmentStrings();
if (!envs) return push_error(L); if (!envs) return push_error(L);
lua_newtable(L); lua_newtable(L);
for (nam = envs; *nam; nam = end + 1) { for (nam = envs; *nam; nam = end + 1) {
end = strchr(val = strchr(nam, '=') + 1, '\0'); end = strchr(val = strchr(nam, '=') + 1, '\0');
lua_pushlstring(L, nam, val - nam - 1); lua_pushlstring(L, nam, val - nam - 1);
lua_pushlstring(L, val, end - val); lua_pushlstring(L, val, end - val);
lua_settable(L, -3); lua_settable(L, -3);
} }
return 1; return 1;
} }
/* -- pathname/nil error */ /* -- pathname/nil error */
static int ex_currentdir(lua_State *L) static int ex_currentdir(lua_State *L)
{ {
char pathname[MAX_PATH + 1]; char pathname[MAX_PATH + 1];
size_t len = GetCurrentDirectory(sizeof pathname, pathname); size_t len = GetCurrentDirectory(sizeof pathname, pathname);
if (len == 0) return push_error(L); if (len == 0) return push_error(L);
lua_pushlstring(L, pathname, len); lua_pushlstring(L, pathname, len);
return 1; return 1;
} }
/* pathname -- true/nil error */ /* pathname -- true/nil error */
static int ex_chdir(lua_State *L) static int ex_chdir(lua_State *L)
{ {
const char *pathname = luaL_checkstring(L, 1); const char *pathname = luaL_checkstring(L, 1);
if (!SetCurrentDirectory(pathname)) if (!SetCurrentDirectory(pathname))
return push_error(L); return push_error(L);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
/* pathname -- true/nil error */ /* pathname -- true/nil error */
static int ex_mkdir(lua_State *L) static int ex_mkdir(lua_State *L)
{ {
const char *pathname = luaL_checkstring(L, 1); const char *pathname = luaL_checkstring(L, 1);
if (!CreateDirectory(pathname, 0)) if (!CreateDirectory(pathname, 0))
return push_error(L); return push_error(L);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
/* pathname -- true/nil error */ /* pathname -- true/nil error */
static int ex_remove(lua_State *L) static int ex_remove(lua_State *L)
{ {
const char *pathname = luaL_checkstring(L, 1); const char *pathname = luaL_checkstring(L, 1);
DWORD attr = GetFileAttributes(pathname); DWORD attr = GetFileAttributes(pathname);
if (attr == (DWORD)-1 if (attr == (DWORD)-1
|| (attr & FILE_ATTRIBUTE_DIRECTORY || (attr & FILE_ATTRIBUTE_DIRECTORY
? !RemoveDirectory(pathname) ? !RemoveDirectory(pathname)
: !DeleteFile(pathname))) : !DeleteFile(pathname)))
return push_error(L); return push_error(L);
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
return 1; return 1;
} }
static FILE *check_file(lua_State *L, int idx, const char *argname) static FILE *check_file(lua_State *L, int idx, const char *argname)
{ {
FILE **pf; FILE **pf;
if (idx > 0) pf = luaL_checkudata(L, idx, LUA_FILEHANDLE); if (idx > 0) pf = luaL_checkudata(L, idx, LUA_FILEHANDLE);
else { else {
idx = absindex(L, idx); idx = absindex(L, idx);
pf = lua_touserdata(L, idx); pf = lua_touserdata(L, idx);
luaL_getmetatable(L, LUA_FILEHANDLE); luaL_getmetatable(L, LUA_FILEHANDLE);
if (!pf || !lua_getmetatable(L, idx) || !lua_rawequal(L, -1, -2)) if (!pf || !lua_getmetatable(L, idx) || !lua_rawequal(L, -1, -2))
luaL_error(L, "bad %s option (%s expected, got %s)", luaL_error(L, "bad %s option (%s expected, got %s)",
argname, LUA_FILEHANDLE, luaL_typename(L, idx)); argname, LUA_FILEHANDLE, luaL_typename(L, idx));
lua_pop(L, 2); lua_pop(L, 2);
} }
if (!*pf) return luaL_error(L, "attempt to use a closed file"), NULL; if (!*pf) return luaL_error(L, "attempt to use a closed file"), NULL;
return *pf; return *pf;
} }
static FILE **new_file(lua_State *L, HANDLE h, int dmode, const char *mode) static FILE **new_file(lua_State *L, HANDLE h, int dmode, const char *mode)
{ {
FILE **pf = lua_newuserdata(L, sizeof *pf); FILE **pf = lua_newuserdata(L, sizeof *pf);
*pf = 0; *pf = 0;
luaL_getmetatable(L, LUA_FILEHANDLE); luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
*pf = _fdopen(_open_osfhandle((long)h, dmode), mode); *pf = _fdopen(_open_osfhandle((long)h, dmode), mode);
return pf; return pf;
} }
#define file_handle(fp) (HANDLE)_get_osfhandle(_fileno(fp)) #define file_handle(fp) (HANDLE)_get_osfhandle(_fileno(fp))
static lua_Number qword_to_number(DWORD hi, DWORD lo) static lua_Number qword_to_number(DWORD hi, DWORD lo)
{ {
/* lua_Number must be floating-point or as large or larger than /* lua_Number must be floating-point or as large or larger than
* two DWORDs in order to be considered adequate for representing * two DWORDs in order to be considered adequate for representing
* large file sizes */ * large file sizes */
assert( hi == 0 assert(hi == 0
|| (lua_Number)0.5 > 0 || (lua_Number)0.5 > 0
|| sizeof(lua_Number) > 2 * sizeof(DWORD) || sizeof(lua_Number) > 2 * sizeof(DWORD)
|| !"lua_Number cannot adequately represent large file sizes" ); || !"lua_Number cannot adequately represent large file sizes" );
return hi * (1.0 + (DWORD)-1) + lo; return hi * (1.0 + (DWORD)-1) + lo;
} }
static lua_Number get_file_size(const char *name) static lua_Number get_file_size(const char *name)
{ {
HANDLE h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); HANDLE h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, 0,
DWORD lo, hi; OPEN_EXISTING, 0, 0);
lua_Number size; DWORD lo, hi;
if (h == INVALID_HANDLE_VALUE) lua_Number size;
size = 0; if (h == INVALID_HANDLE_VALUE)
else { size = 0;
lo = GetFileSize(h, &hi); else {
if (lo == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) lo = GetFileSize(h, &hi);
size = 0; if (lo == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
else size = 0;
size = qword_to_number(hi, lo); else
CloseHandle(h); size = qword_to_number(hi, lo);
} CloseHandle(h);
return size; }
return size;
} }
#define new_dirent(L) lua_newtable(L) #define new_dirent(L) lua_newtable(L)
@ -179,47 +180,47 @@ static lua_Number get_file_size(const char *name)
/* pathname/file [entry] -- entry */ /* pathname/file [entry] -- entry */
static int ex_dirent(lua_State *L) static int ex_dirent(lua_State *L)
{ {
int isdir; int isdir;
lua_Number size; lua_Number size;
DWORD attr; DWORD attr;
switch (lua_type(L, 1)) { switch (lua_type(L, 1)) {
default: return luaL_typerror(L, 1, "file or pathname"); default: return luaL_typerror(L, 1, "file or pathname");
case LUA_TSTRING: { case LUA_TSTRING: {
const char *name = lua_tostring(L, 1); const char *name = lua_tostring(L, 1);
attr = GetFileAttributes(name); attr = GetFileAttributes(name);
if (attr == (DWORD)-1) if (attr == (DWORD)-1)
return push_error(L); return push_error(L);
isdir = attr & FILE_ATTRIBUTE_DIRECTORY; isdir = attr & FILE_ATTRIBUTE_DIRECTORY;
if (isdir) if (isdir)
size = 0; size = 0;
else else
size = get_file_size(name); size = get_file_size(name);
} break; } break;
case LUA_TUSERDATA: { case LUA_TUSERDATA: {
FILE *f = check_file(L, 1, NULL); FILE *f = check_file(L, 1, NULL);
BY_HANDLE_FILE_INFORMATION info; BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(file_handle(f), &info)) if (!GetFileInformationByHandle(file_handle(f), &info))
return push_error(L); return push_error(L);
attr = info.dwFileAttributes; attr = info.dwFileAttributes;
isdir = attr & FILE_ATTRIBUTE_DIRECTORY; isdir = attr & FILE_ATTRIBUTE_DIRECTORY;
size = qword_to_number(info.nFileSizeHigh, info.nFileSizeLow); size = qword_to_number(info.nFileSizeHigh, info.nFileSizeLow);
} break; } break;
} }
if (lua_type(L, 2) != LUA_TTABLE) { if (lua_type(L, 2) != LUA_TTABLE) {
lua_settop(L, 1); lua_settop(L, 1);
new_dirent(L); new_dirent(L);
} }
else { else {
lua_settop(L, 2); lua_settop(L, 2);
} }
if (isdir) if (isdir)
lua_pushliteral(L, "directory"); lua_pushliteral(L, "directory");
else else
lua_pushliteral(L, "file"); lua_pushliteral(L, "file");
lua_setfield(L, 2, "type"); lua_setfield(L, 2, "type");
lua_pushnumber(L, size); lua_pushnumber(L, size);
lua_setfield(L, 2, "size"); lua_setfield(L, 2, "size");
return 1; return 1;
} }
#define DIR_HANDLE "DIR*" #define DIR_HANDLE "DIR*"
@ -227,138 +228,139 @@ static int ex_dirent(lua_State *L)
/* ...diriter... -- ...diriter... pathname */ /* ...diriter... -- ...diriter... pathname */
static int diriter_getpathname(lua_State *L, int index) static int diriter_getpathname(lua_State *L, int index)
{ {
lua_pushvalue(L, index); lua_pushvalue(L, index);
lua_gettable(L, LUA_REGISTRYINDEX); lua_gettable(L, LUA_REGISTRYINDEX);
return 1; return 1;
} }
/* ...diriter... pathname -- ...diriter... */ /* ...diriter... pathname -- ...diriter... */
static int diriter_setpathname(lua_State *L, int index) static int diriter_setpathname(lua_State *L, int index)
{ {
size_t len; size_t len;
const char *path = lua_tolstring(L, -1, &len); const char *path = lua_tolstring(L, -1, &len);
if (path && path[len - 1] != *LUA_DIRSEP) { if (path && path[len - 1] != *LUA_DIRSEP) {
lua_pushliteral(L, LUA_DIRSEP); lua_pushliteral(L, LUA_DIRSEP);
lua_concat(L, 2); lua_concat(L, 2);
} }
lua_pushvalue(L, index); /* ... pathname diriter */ lua_pushvalue(L, index); /* ... pathname diriter */
lua_insert(L, -2); /* ... diriter pathname */ lua_insert(L, -2); /* ... diriter pathname */
lua_settable(L, LUA_REGISTRYINDEX); /* ... */ lua_settable(L, LUA_REGISTRYINDEX); /* ... */
return 0; return 0;
} }
/* diriter -- diriter */ /* diriter -- diriter */
static int diriter_close(lua_State *L) static int diriter_close(lua_State *L)
{ {
DIR **pd = lua_touserdata(L, 1); DIR **pd = lua_touserdata(L, 1);
if (*pd) { if (*pd) {
closedir(*pd); closedir(*pd);
*pd = 0; *pd = 0;
} }
lua_pushnil(L); lua_pushnil(L);
diriter_setpathname(L, 1); diriter_setpathname(L, 1);
return 0; return 0;
} }
static int isdotfile(const char *name) static int isdotfile(const char *name)
{ {
return name[0] == '.' && (name[1] == '\0' return name[0] == '.' && (name[1] == '\0'
|| (name[1] == '.' && name[2] == '\0')); || (name[1] == '.' && name[2] == '\0'));
} }
/* pathname -- iter state nil */ /* pathname -- iter state nil */
/* diriter ... -- entry */ /* diriter ... -- entry */
static int ex_dir(lua_State *L) static int ex_dir(lua_State *L)
{ {
const char *pathname; const char *pathname;
DIR **pd; DIR **pd;
const WIN32_FIND_DATA *d; const WIN32_FIND_DATA *d;
switch (lua_type(L, 1)) { switch (lua_type(L, 1)) {
default: return luaL_typerror(L, 1, "pathname"); default: return luaL_typerror(L, 1, "pathname");
case LUA_TSTRING: case LUA_TSTRING:
pathname = lua_tostring(L, 1); pathname = lua_tostring(L, 1);
lua_pushcfunction(L, ex_dir); /* pathname ... iter */ lua_pushcfunction(L, ex_dir); /* pathname ... iter */
pd = lua_newuserdata(L, sizeof *pd);/* pathname ... iter state */ pd = lua_newuserdata(L, sizeof *pd);/* pathname ... iter state */
*pd = opendir(pathname); *pd = opendir(pathname);
if (!*pd) return push_error(L); if (!*pd) return push_error(L);
luaL_getmetatable(L, DIR_HANDLE); /* pathname ... iter state M */ luaL_getmetatable(L, DIR_HANDLE); /* pathname ... iter state M */
lua_setmetatable(L, -2); /* pathname ... iter state */ lua_setmetatable(L, -2); /* pathname ... iter state */
lua_pushvalue(L, 1); /* pathname ... iter state pathname */ lua_pushvalue(L, 1); /* pathname ... iter state pathname */
diriter_setpathname(L, -2); /* pathname ... iter state */ diriter_setpathname(L, -2); /* pathname ... iter state */
return 2; return 2;
case LUA_TUSERDATA: case LUA_TUSERDATA:
pd = luaL_checkudata(L, 1, DIR_HANDLE); pd = luaL_checkudata(L, 1, DIR_HANDLE);
do d = readdir(*pd); do d = readdir(*pd);
while (d && isdotfile(d->cFileName)); while (d && isdotfile(d->cFileName)) continue;
if (!d) return push_error(L); if (!d) return push_error(L);
new_dirent(L); /* diriter ... entry */ new_dirent(L); /* diriter ... entry */
diriter_getpathname(L, 1); /* diriter ... entry dirpath */ diriter_getpathname(L, 1); /* diriter ... entry dir */
lua_pushstring(L, d->cFileName); /* diriter ... entry dirpath name */ lua_pushstring(L, d->cFileName); /* diriter ... entry dir name */
lua_pushvalue(L, -1); /* diriter ... entry dirpath name name */ lua_pushvalue(L, -1); /* diriter ... entry dir name name */
lua_setfield(L, -4, "name"); /* diriter ... entry dirpath name */ lua_setfield(L, -4, "name"); /* diriter ... entry dir name */
lua_concat(L, 2); /* diriter ... entry fullpath */ lua_concat(L, 2); /* diriter ... entry fullpath */
lua_replace(L, 1); /* fullpath ... entry */ lua_replace(L, 1); /* fullpath ... entry */
lua_replace(L, 2); /* fullpath entry ... */ lua_replace(L, 2); /* fullpath entry ... */
return ex_dirent(L); return ex_dirent(L);
} }
/*NOTREACHED*/ /*NOTREACHED*/
} }
static int file_lock(lua_State *L, FILE *f, const char *mode, long offset, long length) static int file_lock(lua_State *L,
FILE *f, const char *mode, long offset, long length)
{ {
HANDLE h = file_handle(f); HANDLE h = file_handle(f);
DWORD flags; DWORD flags;
ULARGE_INTEGER len = {0}; ULARGE_INTEGER len = {0};
OVERLAPPED ov = {0}; OVERLAPPED ov = {0};
BOOL ret; BOOL ret;
if (length) len.LowPart = length; if (length) len.LowPart = length;
else len.LowPart = GetFileSize(h, &len.HighPart); else len.LowPart = GetFileSize(h, &len.HighPart);
ov.Offset = offset; ov.Offset = offset;
switch (*mode) { switch (*mode) {
case 'w': flags = LOCKFILE_EXCLUSIVE_LOCK; /*FALLTHRU*/ case 'w': flags = LOCKFILE_EXCLUSIVE_LOCK; /*FALLTHRU*/
case 'r': flags |= LOCKFILE_FAIL_IMMEDIATELY; break; case 'r': flags |= LOCKFILE_FAIL_IMMEDIATELY; break;
case 'u': flags = 0; break; case 'u': flags = 0; break;
default: return luaL_error(L, "invalid mode"); default: return luaL_error(L, "invalid mode");
} }
ret = flags ? LockFileEx(h, flags, 0, len.LowPart, len.HighPart, &ov) ret = flags ? LockFileEx(h, flags, 0, len.LowPart, len.HighPart, &ov)
: UnlockFileEx(h, 0, len.LowPart, len.HighPart, &ov); : UnlockFileEx(h, 0, len.LowPart, len.HighPart, &ov);
if (!ret) if (!ret)
return push_error(L); return push_error(L);
lua_settop(L, 1); lua_settop(L, 1);
return 1; return 1;
} }
/* file mode [offset [length]] -- file/nil error */ /* file mode [offset [length]] -- file/nil error */
static int ex_lock(lua_State *L) static int ex_lock(lua_State *L)
{ {
FILE *f = check_file(L, 1, NULL); FILE *f = check_file(L, 1, NULL);
const char *mode = luaL_checkstring(L, 2); const char *mode = luaL_checkstring(L, 2);
long offset = luaL_optnumber(L, 3, 0); long offset = luaL_optnumber(L, 3, 0);
long length = luaL_optnumber(L, 4, 0); long length = luaL_optnumber(L, 4, 0);
return file_lock(L, f, mode, offset, length); return file_lock(L, f, mode, offset, length);
} }
/* file [offset [length]] -- file/nil error */ /* file [offset [length]] -- file/nil error */
static int ex_unlock(lua_State *L) static int ex_unlock(lua_State *L)
{ {
lua_pushliteral(L, "u"); lua_pushliteral(L, "u");
lua_insert(L, 2); lua_insert(L, 2);
return ex_lock(L); return ex_lock(L);
} }
/* -- in out/nil error */ /* -- in out/nil error */
static int ex_pipe(lua_State *L) static int ex_pipe(lua_State *L)
{ {
HANDLE ph[2]; HANDLE ph[2];
if (!CreatePipe(ph + 0, ph + 1, 0, 0)) if (!CreatePipe(ph + 0, ph + 1, 0, 0))
return push_error(L); return push_error(L);
SetHandleInformation(ph[0], HANDLE_FLAG_INHERIT, 0); SetHandleInformation(ph[0], HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(ph[1], HANDLE_FLAG_INHERIT, 0); SetHandleInformation(ph[1], HANDLE_FLAG_INHERIT, 0);
new_file(L, ph[0], _O_RDONLY, "r"); new_file(L, ph[0], _O_RDONLY, "r");
new_file(L, ph[1], _O_WRONLY, "w"); new_file(L, ph[1], _O_WRONLY, "w");
return 2; return 2;
} }
@ -366,98 +368,97 @@ static int ex_pipe(lua_State *L)
* interval units -- */ * interval units -- */
static int ex_sleep(lua_State *L) static int ex_sleep(lua_State *L)
{ {
lua_Number interval = luaL_checknumber(L, 1); lua_Number interval = luaL_checknumber(L, 1);
lua_Number units = luaL_optnumber(L, 2, 1); lua_Number units = luaL_optnumber(L, 2, 1);
Sleep(1e3 * interval / units); Sleep(1e3 * interval / units);
return 0; return 0;
} }
static void get_redirect(lua_State *L, int idx, const char *stdname, struct spawn_params *p) static void get_redirect(lua_State *L,
int idx, const char *stdname, struct spawn_params *p)
{ {
lua_getfield(L, idx, stdname); lua_getfield(L, idx, stdname);
if (!lua_isnil(L, -1)) if (!lua_isnil(L, -1))
spawn_param_redirect(p, stdname, file_handle(check_file(L, -1, stdname))); spawn_param_redirect(p, stdname, file_handle(check_file(L, -1, stdname)));
lua_pop(L, 1); lua_pop(L, 1);
} }
/* filename [args-opts] -- proc/nil error */ /* filename [args-opts] -- proc/nil error */
/* args-opts -- proc/nil error */ /* args-opts -- proc/nil error */
static int ex_spawn(lua_State *L) static int ex_spawn(lua_State *L)
{ {
struct spawn_params *params; struct spawn_params *params;
int have_options; int have_options;
switch (lua_type(L, 1)) {
switch (lua_type(L, 1)) { default: return luaL_typerror(L, 1, "string or table");
default: return luaL_typerror(L, 1, "string or table"); case LUA_TSTRING:
case LUA_TSTRING: switch (lua_type(L, 2)) {
switch (lua_type(L, 2)) { default: return luaL_typerror(L, 2, "table");
default: return luaL_typerror(L, 2, "table"); case LUA_TNONE: have_options = 0; break;
case LUA_TNONE: have_options = 0; break; case LUA_TTABLE: have_options = 1; break;
case LUA_TTABLE: have_options = 1; break; }
} break;
break; case LUA_TTABLE:
case LUA_TTABLE: have_options = 1;
have_options = 1; lua_getfield(L, 1, "command"); /* opts ... cmd */
lua_getfield(L, 1, "command"); /* opts ... cmd */ if (!lua_isnil(L, -1)) {
if (!lua_isnil(L, -1)) { /* convert {command=command,arg1,...} to command {arg1,...} */
/* convert {command=command,arg1,...} to command {arg1,...} */ lua_insert(L, 1); /* cmd opts ... */
lua_insert(L, 1); /* cmd opts ... */ }
} else {
else { /* convert {arg0,arg1,...} to arg0 {arg1,...} */
/* convert {arg0,arg1,...} to arg0 {arg1,...} */ size_t i, n = lua_objlen(L, 1);
size_t i, n = lua_objlen(L, 1); lua_rawgeti(L, 1, 1); /* opts ... nil cmd */
lua_rawgeti(L, 1, 1); /* opts ... nil cmd */ lua_insert(L, 1); /* cmd opts ... nil */
lua_insert(L, 1); /* cmd opts ... nil */ for (i = 2; i <= n; i++) {
for (i = 2; i <= n; i++) { lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */
lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */ lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */
lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */ }
} lua_rawseti(L, 2, n); /* cmd opts ... */
lua_rawseti(L, 2, n); /* cmd opts ... */ }
} if (lua_type(L, 1) != LUA_TSTRING)
if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad command option (string expected, got %s)",
return luaL_error(L, "bad command option (string expected, got %s)", luaL_typename(L, 1));
luaL_typename(L, 1)); break;
break; }
} params = spawn_param_init(L);
/* get filename to execute */
params = spawn_param_init(L); spawn_param_filename(params, lua_tostring(L, 1));
/* get arguments, environment, and redirections */
/* get filename to execute */ if (have_options) {
spawn_param_filename(params, lua_tostring(L, 1)); lua_getfield(L, 2, "args"); /* cmd opts ... argtab */
switch (lua_type(L, -1)) {
/* get arguments, environment, and redirections */ default:
if (have_options) { return luaL_error(L, "bad args option (table expected, got %s)",
lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ luaL_typename(L, -1));
switch (lua_type(L, -1)) { case LUA_TNIL:
default: return luaL_error(L, "bad args option (table expected, got %s)", lua_pop(L, 1); /* cmd opts ... */
luaL_typename(L, -1)); lua_pushvalue(L, 2); /* cmd opts ... opts */
case LUA_TNIL: if (0) /*FALLTHRU*/
lua_pop(L, 1); /* cmd opts ... */ case LUA_TTABLE:
lua_pushvalue(L, 2); /* cmd opts ... opts */ if (lua_objlen(L, 2) > 0)
if (0) /*FALLTHRU*/ return
case LUA_TTABLE: luaL_error(L, "cannot specify both the args option and array values");
if (lua_objlen(L, 2) > 0) spawn_param_args(params); /* cmd opts ... */
return luaL_error(L, "cannot specify both the args option and array values"); break;
spawn_param_args(params); /* cmd opts ... */ }
break; lua_getfield(L, 2, "env"); /* cmd opts ... envtab */
} switch (lua_type(L, -1)) {
lua_getfield(L, 2, "env"); /* cmd opts ... envtab */ default:
switch (lua_type(L, -1)) { return luaL_error(L, "bad env option (table expected, got %s)",
default: return luaL_error(L, "bad env option (table expected, got %s)", luaL_typename(L, -1));
luaL_typename(L, -1)); case LUA_TNIL:
case LUA_TNIL: break;
break; case LUA_TTABLE:
case LUA_TTABLE: spawn_param_env(params); /* cmd opts ... */
spawn_param_env(params); /* cmd opts ... */ break;
break; }
} get_redirect(L, 2, "stdin", params); /* cmd opts ... */
get_redirect(L, 2, "stdin", params); /* cmd opts ... */ get_redirect(L, 2, "stdout", params); /* cmd opts ... */
get_redirect(L, 2, "stdout", params); /* cmd opts ... */ get_redirect(L, 2, "stderr", params); /* cmd opts ... */
get_redirect(L, 2, "stderr", params); /* cmd opts ... */ }
} return spawn_param_execute(params); /* proc/nil error */
return spawn_param_execute(params); /* proc/nil error */
} }
@ -465,86 +466,78 @@ static int ex_spawn(lua_State *L)
* closures from table 'from' or by creating new closures */ * closures from table 'from' or by creating new closures */
static void copyfields(lua_State *L, const luaL_reg *l, int from, int to) static void copyfields(lua_State *L, const luaL_reg *l, int from, int to)
{ {
for (to = absindex(L, to); l->name; l++) { for (to = absindex(L, to); l->name; l++) {
lua_getfield(L, from, l->name); lua_getfield(L, from, l->name);
if (lua_isnil(L, -1)) { if (lua_isnil(L, -1)) {
lua_pop(L, 1); lua_pop(L, 1);
lua_pushcfunction(L, l->func); lua_pushcfunction(L, l->func);
} }
lua_setfield(L, to, l->name); lua_setfield(L, to, l->name);
} }
} }
int luaopen_ex(lua_State *L) int luaopen_ex(lua_State *L)
{ {
const luaL_reg ex_iolib[] = { const char *name = lua_tostring(L, 1);
{"pipe", ex_pipe}, int ex;
const luaL_reg ex_iolib[] = {
{"pipe", ex_pipe},
#define ex_iofile_methods (ex_iolib + 1) #define ex_iofile_methods (ex_iolib + 1)
{"lock", ex_lock}, {"lock", ex_lock},
{"unlock", ex_unlock}, {"unlock", ex_unlock},
{0,0} }; {0,0} };
const luaL_reg ex_oslib[] = { const luaL_reg ex_oslib[] = {
{"getenv", ex_getenv}, /* environment */
{"setenv", ex_setenv}, {"getenv", ex_getenv},
{"environ", ex_environ}, {"setenv", ex_setenv},
{"environ", ex_environ},
{"currentdir", ex_currentdir}, /* file system */
{"chdir", ex_chdir}, {"currentdir", ex_currentdir},
{"mkdir", ex_mkdir}, {"chdir", ex_chdir},
{"remove", ex_remove}, {"mkdir", ex_mkdir},
{"remove", ex_remove},
{"dir", ex_dir}, {"dir", ex_dir},
{"dirent", ex_dirent}, {"dirent", ex_dirent},
/* process control */
{"sleep", ex_sleep}, {"sleep", ex_sleep},
{"spawn", ex_spawn}, {"spawn", ex_spawn},
{0,0} }; {0,0} };
const luaL_reg ex_diriter_methods[] = { const luaL_reg ex_diriter_methods[] = {
{"__gc", diriter_close}, {"__gc", diriter_close},
{0,0} }; {0,0} };
const luaL_reg ex_process_methods[] = { const luaL_reg ex_process_methods[] = {
{"__tostring", process_tostring}, {"__tostring", process_tostring},
#define ex_process_functions (ex_process_methods + 1) #define ex_process_functions (ex_process_methods + 1)
{"wait", process_wait}, {"wait", process_wait},
{0,0} }; {0,0} };
/* diriter metatable */
int ex; luaL_newmetatable(L, DIR_HANDLE); /* . D */
const char *name = lua_tostring(L, 1); luaL_register(L, 0, ex_diriter_methods); /* . D */
/* proc metatable */
/* diriter metatable */ luaL_newmetatable(L, PROCESS_HANDLE); /* . P */
luaL_newmetatable(L, DIR_HANDLE); /* . D */ luaL_register(L, 0, ex_process_methods); /* . P */
luaL_register(L, 0, ex_diriter_methods); /* . D */ lua_pushvalue(L, -1); /* . P P */
lua_setfield(L, -2, "__index"); /* . P */
/* proc metatable */ /* make all functions available via ex. namespace */
luaL_newmetatable(L, PROCESS_HANDLE); /* . P */ luaL_register(L, name, ex_oslib); /* . P ex */
luaL_register(L, 0, ex_process_methods); /* . P */ luaL_register(L, 0, ex_iolib);
lua_pushvalue(L, -1); /* . P P */ copyfields(L, ex_process_functions, -2, -1);
lua_setfield(L, -2, "__index"); /* . P */ ex = lua_gettop(L);
/* extend the os table */
/* make all functions available via ex. namespace */ lua_getglobal(L, "os"); /* . os */
luaL_register(L, name, ex_oslib); /* . P ex */ if (lua_isnil(L, -1)) return luaL_error(L, "os not loaded");
luaL_register(L, 0, ex_iolib); copyfields(L, ex_oslib, ex, -1);
copyfields(L, ex_process_functions, -2, -1); /* extend the io table */
ex = lua_gettop(L); lua_getglobal(L, "io"); /* . io */
if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded");
/* extend the os table */ copyfields(L, ex_iolib, ex, -1);
lua_getglobal(L, "os"); /* . os */ lua_getfield(L, ex, "pipe"); /* . io ex_pipe */
if (lua_isnil(L, -1)) return luaL_error(L, "os not loaded"); lua_getfield(L, -2, "stderr"); /* . io ex_pipe io_stderr */
copyfields(L, ex_oslib, ex, -1); lua_getfenv(L, -1); /* . io ex_pipe io_stderr E */
lua_setfenv(L, -3); /* . io ex_pipe io_stderr */
/* extend the io table */ /* extend the io.file metatable */
lua_getglobal(L, "io"); /* . io */ luaL_getmetatable(L, LUA_FILEHANDLE); /* . F */
if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded"); if (lua_isnil(L, -1)) return luaL_error(L, "can't find FILE* metatable");
copyfields(L, ex_iolib, ex, -1); copyfields(L, ex_iofile_methods, ex, -1);
lua_getfield(L, ex, "pipe"); /* . io ex_pipe */ return 1;
lua_getfield(L, -2, "stderr"); /* . io ex_pipe io_stderr */
lua_getfenv(L, -1); /* . io ex_pipe io_stderr E */
lua_setfenv(L, -3); /* . io ex_pipe io_stderr */
/* extend the io.file metatable */
luaL_getmetatable(L, LUA_FILEHANDLE); /* . F */
if (lua_isnil(L, -1)) return luaL_error(L, "can't find FILE* metatable");
copyfields(L, ex_iofile_methods, ex, -1);
return 1;
} }

@ -18,33 +18,29 @@
* nresults is -1 and error is NO_ERROR, then push true and return 1. * nresults is -1 and error is NO_ERROR, then push true and return 1.
* Otherwise, if error is NO_ERROR, return nresults. * Otherwise, if error is NO_ERROR, return nresults.
*/ */
int int windows_pusherror(lua_State *L, DWORD error, int nresults)
windows_pusherror(lua_State *L, DWORD error, int nresults)
{ {
if (error != NO_ERROR || nresults == -2) { if (error != NO_ERROR || nresults == -2) {
char buffer[1024]; char buffer[1024];
size_t len, res; size_t len = sprintf(buffer, "%lu (0x%lX): ", error, error);
size_t res = FormatMessage(
len = sprintf(buffer, "%lu (0x%lX): ", error, error); FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
0, error, 0, buffer + len, sizeof buffer - len, 0);
res = FormatMessage( if (res) {
FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, len += res;
0, error, 0, buffer + len, sizeof buffer - len, 0); while (len > 0 && isspace(buffer[len - 1]))
if (res) { len--;
len += res; }
while (len > 0 && isspace(buffer[len - 1])) else {
len--; len += sprintf(buffer + len, "<error string not available>");
} }
else lua_pushnil(L);
len += sprintf(buffer + len, "<error string not available>"); lua_pushlstring(L, buffer, len);
nresults = 2;
lua_pushnil(L); }
lua_pushlstring(L, buffer, len); else if (nresults < 0) {
nresults = 2; lua_pushboolean(L, 1);
} nresults = 1;
else if (nresults < 0) { }
lua_pushboolean(L, 1); return nresults;
nresults = 1;
}
return nresults;
} }

@ -20,162 +20,160 @@
static int needs_quoting(const char *s) static int needs_quoting(const char *s)
{ {
return s[0] != '"' && strchr(s, ' '); return s[0] != '"' && strchr(s, ' ');
} }
struct spawn_params { struct spawn_params {
lua_State *L; lua_State *L;
const char *cmdline; const char *cmdline;
const char *environment; const char *environment;
STARTUPINFO si; STARTUPINFO si;
}; };
struct spawn_params *spawn_param_init(lua_State *L) struct spawn_params *spawn_param_init(lua_State *L)
{ {
static const STARTUPINFO si = {sizeof si}; static const STARTUPINFO si = {sizeof si};
struct spawn_params *p = lua_newuserdata(L, sizeof *p); struct spawn_params *p = lua_newuserdata(L, sizeof *p);
p->L = L; p->L = L;
p->cmdline = p->environment = 0; p->cmdline = p->environment = 0;
p->si = si; p->si = si;
return p; return p;
} }
void spawn_param_filename(struct spawn_params *p, const char *filename) void spawn_param_filename(struct spawn_params *p, const char *filename)
{ {
p->cmdline = filename; p->cmdline = filename;
if (needs_quoting(p->cmdline)) { if (needs_quoting(p->cmdline)) {
lua_pushliteral(p->L, "\""); /* cmd ... q */ lua_pushliteral(p->L, "\""); /* cmd ... q */
lua_pushstring(p->L, p->cmdline); /* cmd ... q cmd */ lua_pushstring(p->L, p->cmdline); /* cmd ... q cmd */
lua_pushvalue(p->L, -2); /* cmd ... q cmd q */ lua_pushvalue(p->L, -2); /* cmd ... q cmd q */
lua_concat(p->L, 3); /* cmd ... "cmd" */ lua_concat(p->L, 3); /* cmd ... "cmd" */
p->cmdline = lua_tostring(p->L, -1); p->cmdline = lua_tostring(p->L, -1);
} }
} }
/* cmd opts ... argtab -- cmd opts ... cmdline */ /* cmd opts ... argtab -- cmd opts ... cmdline */
void spawn_param_args(struct spawn_params *p) void spawn_param_args(struct spawn_params *p)
{ {
lua_State *L = p->L; lua_State *L = p->L;
size_t i, n = lua_objlen(L, -1); size_t i, n = lua_objlen(L, -1);
luaL_Buffer args; luaL_Buffer args;
debug("spawn_param_args:"); debug_stack(L); debug("spawn_param_args:"); debug_stack(L);
luaL_buffinit(L, &args); luaL_buffinit(L, &args);
/* concatenate the arg array to a string */ /* concatenate the arg array to a string */
for (i = 1; i <= n; i++) { for (i = 1; i <= n; i++) {
int quote; int quote;
lua_rawgeti(L, -1, i); /* ... argtab arg */ lua_rawgeti(L, -1, i); /* ... argtab arg */
/* XXX checkstring is confusing here */ /* XXX checkstring is confusing here */
quote = needs_quoting(luaL_checkstring(L, -1)); quote = needs_quoting(luaL_checkstring(L, -1));
luaL_putchar(&args, ' '); luaL_putchar(&args, ' ');
if (quote) luaL_putchar(&args, '"'); if (quote) luaL_putchar(&args, '"');
luaL_addvalue(&args); luaL_addvalue(&args);
if (quote) luaL_putchar(&args, '"'); if (quote) luaL_putchar(&args, '"');
lua_pop(L, 1); /* ... argtab */ lua_pop(L, 1); /* ... argtab */
} }
luaL_pushresult(&args); /* ... argtab argstr */ luaL_pushresult(&args); /* ... argtab argstr */
lua_pushvalue(L, 1); /* cmd opts ... argtab argstr cmd */ lua_pushvalue(L, 1); /* cmd opts ... argtab argstr cmd */
lua_insert(L, -2); /* cmd opts ... argtab cmd argstr */ lua_insert(L, -2); /* cmd opts ... argtab cmd argstr */
lua_concat(L, 2); /* cmd opts ... argtab cmdline */ lua_concat(L, 2); /* cmd opts ... argtab cmdline */
lua_replace(L, -2); /* cmd opts ... cmdline */ lua_replace(L, -2); /* cmd opts ... cmdline */
p->cmdline = lua_tostring(L, -1); p->cmdline = lua_tostring(L, -1);
} }
/* ... envtab/nil */ /* ... envtab/nil */
void spawn_param_env(struct spawn_params *p) void spawn_param_env(struct spawn_params *p)
{ {
lua_State *L = p->L; lua_State *L = p->L;
luaL_Buffer env; luaL_Buffer env;
/* convert env table to zstring list */ /* convert env table to zstring list */
/* {nam1=val1,nam2=val2} => "nam1=val1\0nam2=val2\0\0" */ /* {nam1=val1,nam2=val2} => "nam1=val1\0nam2=val2\0\0" */
luaL_buffinit(L, &env); luaL_buffinit(L, &env);
lua_pushnil(L); /* ... envtab nil */ lua_pushnil(L); /* ... envtab nil */
while (lua_next(L, -2)) { /* ... envtab k v */ while (lua_next(L, -2)) { /* ... envtab k v */
/* XXX luaL_checktype is confusing here */ /* XXX luaL_checktype is confusing here */
luaL_checktype(L, -2, LUA_TSTRING); luaL_checktype(L, -2, LUA_TSTRING);
luaL_checktype(L, -1, LUA_TSTRING); luaL_checktype(L, -1, LUA_TSTRING);
lua_pushvalue(L, -2); /* ... envtab k v k */ lua_pushvalue(L, -2); /* ... envtab k v k */
luaL_addvalue(&env); luaL_addvalue(&env);
luaL_putchar(&env, '='); luaL_putchar(&env, '=');
lua_pop(L, 1); /* ... envtab k v */ lua_pop(L, 1); /* ... envtab k v */
luaL_addvalue(&env); luaL_addvalue(&env);
luaL_putchar(&env, '\0'); luaL_putchar(&env, '\0');
lua_pop(L, 1); /* ... envtab k */ lua_pop(L, 1); /* ... envtab k */
} }
luaL_putchar(&env, '\0'); luaL_putchar(&env, '\0');
luaL_pushresult(&env); /* ... envtab envstr */ luaL_pushresult(&env); /* ... envtab envstr */
lua_replace(L, -2); /* ... envtab envstr */ lua_replace(L, -2); /* ... envtab envstr */
p->environment = lua_tostring(L, -1); p->environment = lua_tostring(L, -1);
} }
void spawn_param_redirect(struct spawn_params *p, const char *stdname, HANDLE h) void spawn_param_redirect(struct spawn_params *p, const char *stdname, HANDLE h)
{ {
SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
if (!(p->si.dwFlags & STARTF_USESTDHANDLES)) { if (!(p->si.dwFlags & STARTF_USESTDHANDLES)) {
p->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); p->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
p->si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); p->si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
p->si.hStdError = GetStdHandle(STD_ERROR_HANDLE); p->si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
p->si.dwFlags |= STARTF_USESTDHANDLES; p->si.dwFlags |= STARTF_USESTDHANDLES;
} }
switch (stdname[3]) { switch (stdname[3]) {
case 'i': p->si.hStdInput = h; break; case 'i': p->si.hStdInput = h; break;
case 'o': p->si.hStdOutput = h; break; case 'o': p->si.hStdOutput = h; break;
case 'e': p->si.hStdError = h; break; case 'e': p->si.hStdError = h; break;
} }
} }
struct process { struct process {
int status; int status;
HANDLE hProcess; HANDLE hProcess;
DWORD dwProcessId; DWORD dwProcessId;
}; };
int spawn_param_execute(struct spawn_params *p) int spawn_param_execute(struct spawn_params *p)
{ {
lua_State *L = p->L; lua_State *L = p->L;
struct process *proc; char *c, *e;
char *c, *e; PROCESS_INFORMATION pi;
PROCESS_INFORMATION pi; BOOL ret;
BOOL ret; struct process *proc = lua_newuserdata(L, sizeof *proc);
luaL_getmetatable(L, PROCESS_HANDLE);
proc = lua_newuserdata(L, sizeof *proc); /* cmd opts ... proc */ lua_setmetatable(L, -2);
luaL_getmetatable(L, PROCESS_HANDLE); /* cmd opts ... proc M */ proc->status = -1;
lua_setmetatable(L, -2); /* cmd opts ... proc */ c = strdup(p->cmdline);
proc->status = -1; e = (char *)p->environment; /* strdup(p->environment); */
c = strdup(p->cmdline); /* XXX does CreateProcess modify its environment argument? */
e = (char *)p->environment; /* strdup(p->environment); */ ret = CreateProcess(0, c, 0, 0, TRUE, 0, e, 0, &p->si, &pi);
/* XXX does CreateProcess modify its environment argument? */ /* if (e) free(e); */
ret = CreateProcess(0, c, 0, 0, TRUE, 0, e, 0, &p->si, &pi); free(c);
/* if (e) free(e); */ if (!ret)
free(c); return windows_pushlasterror(L);
if (!ret) proc->hProcess = pi.hProcess;
return windows_pushlasterror(L); proc->dwProcessId = pi.dwProcessId;
proc->hProcess = pi.hProcess; return 1;
proc->dwProcessId = pi.dwProcessId;
return 1;
} }
/* proc -- exitcode/nil error */ /* proc -- exitcode/nil error */
int process_wait(lua_State *L) int process_wait(lua_State *L)
{ {
struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE);
if (p->status == -1) { if (p->status == -1) {
DWORD exitcode; DWORD exitcode;
if (WAIT_FAILED == WaitForSingleObject(p->hProcess, INFINITE) if (WAIT_FAILED == WaitForSingleObject(p->hProcess, INFINITE)
|| !GetExitCodeProcess(p->hProcess, &exitcode)) || !GetExitCodeProcess(p->hProcess, &exitcode))
return windows_pushlasterror(L); return windows_pushlasterror(L);
p->status = exitcode; p->status = exitcode;
} }
lua_pushnumber(L, p->status); lua_pushnumber(L, p->status);
return 1; return 1;
} }
/* proc -- string */ /* proc -- string */
int process_tostring(lua_State *L) int process_tostring(lua_State *L)
{ {
struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE);
char buf[40]; char buf[40];
lua_pushlstring(L, buf, lua_pushlstring(L, buf,
sprintf(buf, "process (%lu, %s)", (unsigned long)p->dwProcessId, sprintf(buf, "process (%lu, %s)", (unsigned long)p->dwProcessId,
p->status==-1 ? "running" : "terminated")); p->status==-1 ? "running" : "terminated"));
return 1; return 1;
} }

@ -17,7 +17,10 @@ struct spawn_params *spawn_param_init(lua_State *L);
void spawn_param_filename(struct spawn_params *p, const char *filename); void spawn_param_filename(struct spawn_params *p, const char *filename);
void spawn_param_args(struct spawn_params *p); void spawn_param_args(struct spawn_params *p);
void spawn_param_env(struct spawn_params *p); void spawn_param_env(struct spawn_params *p);
void spawn_param_redirect(struct spawn_params *p, const char *stdname, HANDLE h); void spawn_param_redirect(
struct spawn_params *p,
const char *stdname,
HANDLE h);
int spawn_param_execute(struct spawn_params *p); int spawn_param_execute(struct spawn_params *p);
int process_wait(lua_State *L); int process_wait(lua_State *L);

Loading…
Cancel
Save