diff --git a/posix/Makefile b/posix/Makefile index 06531d0..36ee544 100755 --- a/posix/Makefile +++ b/posix/Makefile @@ -1,13 +1,19 @@ CFLAGS = -std=c99 $(WARNINGS) $(DEFINES) $(INCLUDES) -DEFINES = -D_POSIX_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L -U__STRICT_ANSI__ -INCLUDES = -I$(LUA)/include -I. +DEFINES = -D_POSIX_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L -U__STRICT_ANSI__ -DMISSING_POSIX_SPAWN +INCLUDES = -I$(LUA)/include WARNINGS = -W -Wall #LUA = /home/mark/src/lang/lua/lua-5.1-rc2 LUA = /home/mark/src/lang/lua/lua51 -ex.so: ex.o; $(CC) -shared -o $@ ex.o +ex-OBJS = ex.o spawn.o + +ex.so: $(ex-OBJS); $(CC) -shared -o $@ $(ex-OBJS) #LIBS = -L$(LUA)/lib -llua51 spawn.a -ex.dll: ex.o $(LIBS); $(CC) -shared -L$(LUA)/bin/Cygwin -o $@ ex.o spawn.o -llua51 +EXTRA = posix_spawn.o +ex.dll: $(ex-OBJS) $(LIBS); $(CC) -shared -L$(LUA)/bin/Cygwin -o $@ $(ex-OBJS) $(EXTRA) -llua51 +ex.o: ex.c spawn.h spawn.o: spawn.c spawn.h + +posix_spawn.o: posix_spawn.c posix_spawn.h diff --git a/posix/ex.c b/posix/ex.c index 7df891f..f82b2cd 100755 --- a/posix/ex.c +++ b/posix/ex.c @@ -11,12 +11,11 @@ #include #include -#include -#include +#include "spawn.h" /* Generally useful function -- what luaL_checkudata() should do */ -static void *luaL_checkuserdata(lua_State *L, int idx, const char *tname) +void *checkuserdata(lua_State *L, int idx, const char *tname) { void *ud; luaL_argcheck(L, ud = luaL_checkudata(L, idx, tname), idx, tname); @@ -25,7 +24,7 @@ static void *luaL_checkuserdata(lua_State *L, int idx, const char *tname) /* -- nil error */ -static int push_error(lua_State *L) +int push_error(lua_State *L) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); @@ -148,7 +147,7 @@ static int file_lock(lua_State *L, FILE *f, const char *mode, long offset, long /* file mode [offset [length]] -- true/nil error */ static int ex_lock(lua_State *L) { - FILE **pf = luaL_checkuserdata(L, 1, LUA_FILEHANDLE); + FILE **pf = checkuserdata(L, 1, LUA_FILEHANDLE); const char *mode = luaL_checkstring(L, 2); long offset = luaL_optnumber(L, 3, 0); long length = luaL_optnumber(L, 4, 0); @@ -188,128 +187,6 @@ static int ex_pipe(lua_State *L) } -struct spawn_params { - lua_State *L; - const char *command, **argv, **envp; - posix_spawn_file_actions_t file_actions; - int has_actions; -}; - -/* filename ... */ -static void spawn_param_filename(struct spawn_params *p) -{ - /* XXX confusing */ - p->command = luaL_checkstring(p->L, 1); -} - -/* -- */ -static void spawn_param_defaults(struct spawn_params *p) -{ - p->argv = lua_newuserdata(p->L, 2 * sizeof *p->argv); - p->argv[0] = p->command; - p->argv[1] = 0; - p->envp = (const char **)environ; - p->has_actions = 0; -} - -/* Converts a Lua array of strings to a null-terminated array of char pointers. - * Pops a (0-based) Lua array and replaces it with a userdatum which is the - * null-terminated C array of char pointers. The elements of this array point - * to the strings in the Lua array. These strings should be associated with - * this userdatum via a weak table for GC purposes, but they are not here. - * Therefore, any function which calls this must make sure that these strings - * remain available until the userdatum is thrown away. - */ -/* ... array -- ... vector */ -static const char **make_vector(lua_State *L) -{ - size_t i, n = lua_objlen(L, -1); - const char **vec = lua_newuserdata(L, (n + 2) * sizeof *vec); - /* ... arr vec */ - for (i = 0; i <= n; i++) { - lua_rawgeti(L, -2, i); /* ... arr vec elem */ - vec[i] = lua_tostring(L, -1); - lua_pop(L, 1); /* ... arr vec */ - } - vec[n + 1] = 0; - lua_replace(L, -2); /* ... vector */ - return vec; -} - -/* ... argtab */ -static void spawn_param_args(struct spawn_params *p) -{ - const char **argv = make_vector(p->L); - if (!argv[0]) argv[0] = p->command; - p->argv = argv; -} - -/* ... envtab/nil */ -static void spawn_param_env(struct spawn_params *p) -{ - size_t i = 0; - luaL_Buffer estr; - if (lua_isnil(p->L, -1)) { - p->envp = (const char **)environ; - return; - } - luaL_buffinit(p->L, &estr); - lua_newtable(p->L); /* ... envtab arr */ - lua_pushnil(p->L); /* ... envtab arr nil */ - for (i = 0; lua_next(p->L, -3); i++) { /* ... envtab arr k v */ - luaL_prepbuffer(&estr); - lua_pushvalue(p->L, -2); /* ... envtab arr k v k */ - luaL_addvalue(&estr); - luaL_putchar(&estr, '='); - lua_pop(p->L, 1); /* ... envtab arr k v */ - luaL_addvalue(&estr); - lua_pop(p->L, 1); /* ... envtab arr k */ - luaL_pushresult(&estr); /* ... envtab arr k estr */ - lua_rawseti(p->L, -3, i); /* ... envtab arr[n]=estr k */ - } /* ... envtab arr */ - lua_replace(p->L, -2); /* ... arr */ - make_vector(p->L); /* ... arr */ -} - -/* _ opts ... */ -static int get_redirect(struct spawn_params *p, const char *stdname, int descriptor) -{ - int ret; - lua_getfield(p->L, 2, stdname); - if ((ret = !lua_isnil(p->L, -1))) - /* XXX luaL_checkuserdata is confusing here */ - posix_spawn_file_actions_adddup2(&p->file_actions, - fileno(luaL_checkuserdata(p->L, -1, LUA_FILEHANDLE)), descriptor); - lua_pop(p->L, 1); - return ret; -} - -/* _ opts ... */ -static void spawn_param_redirects(struct spawn_params *p) -{ - posix_spawn_file_actions_init(&p->file_actions); - p->has_actions = 1; - get_redirect(p, "stdin", STDIN_FILENO); - get_redirect(p, "stdout", STDOUT_FILENO); - get_redirect(p, "stderr", STDERR_FILENO); -} - -#define PROCESS_HANDLE "process" -struct process { - int status; - pid_t pid; -}; - -static int spawn_param_execute(struct spawn_params *p, struct process *proc) -{ - int ret; - proc->status = -1; - ret = posix_spawnp(&proc->pid, p->command, &p->file_actions, 0, (char *const *)p->argv, (char *const *)p->envp); - if (p->has_actions) - posix_spawn_file_actions_destroy(&p->file_actions); - return ret == 0; -} - /* filename [args-opts] -- true/nil,error */ /* args-opts -- true/nil,error */ static int ex_spawn(lua_State *L) @@ -381,20 +258,6 @@ static int ex_spawn(lua_State *L) return 1; /* ... proc */ } -/* proc -- exitcode/nil error */ -static int process_wait(lua_State *L) -{ - struct process *p = luaL_checkuserdata(L, 1, PROCESS_HANDLE); - if (p->status == -1) { - int status; - if (-1 == waitpid(p->pid, &status, 0)) - return push_error(L); - p->status = WEXITSTATUS(status); - } - lua_pushnumber(L, p->status); - return 1; -} - static const luaL_reg ex_iolib[] = { {"pipe", ex_pipe}, diff --git a/posix/spawn.c b/posix/spawn.c new file mode 100755 index 0000000..63eda45 --- /dev/null +++ b/posix/spawn.c @@ -0,0 +1,135 @@ +#include +#include +#include + +#include +#include + +#include "spawn.h" + +extern int push_error(lua_State *L); +extern void *checkuserdata(lua_State *L, int index, const char *name); + +/* filename ... */ +void spawn_param_filename(struct spawn_params *p) +{ + /* XXX confusing */ + p->command = luaL_checkstring(p->L, 1); +} + +/* -- */ +void spawn_param_defaults(struct spawn_params *p) +{ + p->argv = lua_newuserdata(p->L, 2 * sizeof *p->argv); + p->argv[0] = p->command; + p->argv[1] = 0; + p->envp = (const char **)environ; + p->has_actions = 0; +} + +/* Converts a Lua array of strings to a null-terminated array of char pointers. + * Pops a (0-based) Lua array and replaces it with a userdatum which is the + * null-terminated C array of char pointers. The elements of this array point + * to the strings in the Lua array. These strings should be associated with + * this userdatum via a weak table for GC purposes, but they are not here. + * Therefore, any function which calls this must make sure that these strings + * remain available until the userdatum is thrown away. + */ +/* ... array -- ... vector */ +static const char **make_vector(lua_State *L) +{ + size_t i, n = lua_objlen(L, -1); + const char **vec = lua_newuserdata(L, (n + 2) * sizeof *vec); + /* ... arr vec */ + for (i = 0; i <= n; i++) { + lua_rawgeti(L, -2, i); /* ... arr vec elem */ + vec[i] = lua_tostring(L, -1); + lua_pop(L, 1); /* ... arr vec */ + } + vec[n + 1] = 0; + lua_replace(L, -2); /* ... vector */ + return vec; +} + +/* ... argtab */ +void spawn_param_args(struct spawn_params *p) +{ + const char **argv = make_vector(p->L); + if (!argv[0]) argv[0] = p->command; + p->argv = argv; +} + +/* ... envtab/nil */ +void spawn_param_env(struct spawn_params *p) +{ + size_t i = 0; + luaL_Buffer estr; + if (lua_isnil(p->L, -1)) { + p->envp = (const char **)environ; + return; + } + luaL_buffinit(p->L, &estr); + lua_newtable(p->L); /* ... envtab arr */ + lua_pushnil(p->L); /* ... envtab arr nil */ + for (i = 0; lua_next(p->L, -3); i++) { /* ... envtab arr k v */ + luaL_prepbuffer(&estr); + lua_pushvalue(p->L, -2); /* ... envtab arr k v k */ + luaL_addvalue(&estr); + luaL_putchar(&estr, '='); + lua_pop(p->L, 1); /* ... envtab arr k v */ + luaL_addvalue(&estr); + lua_pop(p->L, 1); /* ... envtab arr k */ + luaL_pushresult(&estr); /* ... envtab arr k estr */ + lua_rawseti(p->L, -3, i); /* ... envtab arr[n]=estr k */ + } /* ... envtab arr */ + lua_replace(p->L, -2); /* ... arr */ + make_vector(p->L); /* ... arr */ +} + +/* _ opts ... */ +static int get_redirect(struct spawn_params *p, const char *stdname, int descriptor) +{ + int ret; + lua_getfield(p->L, 2, stdname); + if ((ret = !lua_isnil(p->L, -1))) + /* XXX checkuserdata is confusing here */ + posix_spawn_file_actions_adddup2(&p->file_actions, + fileno(checkuserdata(p->L, -1, LUA_FILEHANDLE)), descriptor); + lua_pop(p->L, 1); + return ret; +} + +/* _ opts ... */ +void spawn_param_redirects(struct spawn_params *p) +{ + posix_spawn_file_actions_init(&p->file_actions); + p->has_actions = 1; + get_redirect(p, "stdin", STDIN_FILENO); + get_redirect(p, "stdout", STDOUT_FILENO); + get_redirect(p, "stderr", STDERR_FILENO); +} + +int spawn_param_execute(struct spawn_params *p, struct process *proc) +{ + int ret; + proc->status = -1; + ret = posix_spawnp(&proc->pid, p->command, &p->file_actions, 0, (char *const *)p->argv, (char *const *)p->envp); + if (p->has_actions) + posix_spawn_file_actions_destroy(&p->file_actions); + return ret == 0; +} + + +/* proc -- exitcode/nil error */ +int process_wait(lua_State *L) +{ + struct process *p = checkuserdata(L, 1, PROCESS_HANDLE); + if (p->status == -1) { + int status; + if (-1 == waitpid(p->pid, &status, 0)) + return push_error(L); + p->status = WEXITSTATUS(status); + } + lua_pushnumber(L, p->status); + return 1; +} diff --git a/posix/spawn.h b/posix/spawn.h new file mode 100755 index 0000000..39167de --- /dev/null +++ b/posix/spawn.h @@ -0,0 +1,32 @@ +#ifndef SPAWN_H +#define SPAWN_H + +#ifdef MISSING_POSIX_SPAWN +#include "posix_spawn.h" +#endif + +#include +#include + +#define PROCESS_HANDLE "process" +struct process { + int status; + pid_t pid; +}; + +struct spawn_params { + lua_State *L; + const char *command, **argv, **envp; + posix_spawn_file_actions_t file_actions; + int has_actions; +}; + +void spawn_param_filename(struct spawn_params *p); +void spawn_param_defaults(struct spawn_params *p); +void spawn_param_args(struct spawn_params *p); +void spawn_param_env(struct spawn_params *p); +void spawn_param_redirects(struct spawn_params *p); +int spawn_param_execute(struct spawn_params *p, struct process *proc); +int process_wait(lua_State *L); + +#endif/*SPAWN_H*/ diff --git a/w32api/Makefile b/w32api/Makefile index aa65a4b..0ca1e60 100755 --- a/w32api/Makefile +++ b/w32api/Makefile @@ -7,5 +7,6 @@ WARNINGS = -W -Wall -Wno-missing-braces LUA = /home/mark/src/lang/lua/lua51 LUALIBS = -L$(LUA)/bin/mingw -llua51 -ex.dll: ex.o - $(CC) $(TARGET_ARCH) -shared -o $@ ex.o $(LUALIBS) /c/windows/system32/msvcrt.dll +ex.dll-OBJS = ex.o spawn.o +ex.dll: $(ex.dll-OBJS) + $(CC) $(TARGET_ARCH) -shared -o $@ $(ex.dll-OBJS) $(LUALIBS) /c/windows/system32/msvcrt.dll diff --git a/w32api/ex.c b/w32api/ex.c index 14b5dcd..aa6cdc7 100755 --- a/w32api/ex.c +++ b/w32api/ex.c @@ -12,15 +12,23 @@ #include #include +#include "spawn.h" + /* Generally useful function -- what luaL_checkudata() should do */ -static void *luaL_checkuserdata(lua_State *L, int idx, const char *tname) +extern void *checkuserdata(lua_State *L, int idx, const char *tname) { void *ud; luaL_argcheck(L, ud = luaL_checkudata(L, idx, tname), idx, tname); return ud; } +/* return HANDLE from a FILE */ +extern HANDLE get_handle(FILE *f) +{ + return (HANDLE)_get_osfhandle(fileno(f)); +} + /* -- nil error */ static int push_error(lua_State *L) @@ -135,11 +143,6 @@ static int ex_currentdir(lua_State *L) return 1; } -static HANDLE get_handle(FILE *f) -{ - return (HANDLE)_get_osfhandle(fileno(f)); -} - /* pathname -- iter state nil */ static int ex_dir(lua_State *L) { return luaL_error(L, "not yet implemented"); } @@ -174,7 +177,7 @@ static int file_lock(lua_State *L, FILE *f, const char *mode, long offset, long /* file mode [offset [length]] -- true/nil error */ static int ex_lock(lua_State *L) { - FILE **pf = luaL_checkuserdata(L, 1, LUA_FILEHANDLE); + FILE **pf = checkuserdata(L, 1, LUA_FILEHANDLE); const char *mode = luaL_checkstring(L, 2); long offset = luaL_optnumber(L, 3, 0); long length = luaL_optnumber(L, 4, 0); @@ -216,146 +219,6 @@ static int ex_pipe(lua_State *L) } -struct spawn_params { - lua_State *L; - const char *cmdline; - const char *environment; - STARTUPINFO si; -}; - -static int needs_quoting(const char *s) -{ - return s[0] != '"' && strchr(s, ' '); -} - -/* filename ... */ -static void spawn_param_filename(struct spawn_params *p) -{ - /* XXX luaL_checkstring is confusing here */ - p->cmdline = luaL_checkstring(p->L, 1); - if (needs_quoting(p->cmdline)) { - lua_pushliteral(p->L, "\""); /* cmd ... q */ - lua_pushvalue(p->L, 1); /* cmd ... q cmd */ - lua_pushvalue(p->L, -2); /* cmd ... q cmd q */ - lua_concat(p->L, 3); /* cmd ... "cmd" */ - lua_replace(p->L, 1); /* "cmd" ... */ - p->cmdline = lua_tostring(p->L, 1); - } -} - -/* -- */ -static void spawn_param_defaults(struct spawn_params *p) -{ - p->environment = 0; - memset(&p->si, 0, sizeof p->si); - p->si.cb = sizeof p->si; -} - -/* cmd opts ... argtab -- cmd opts ... cmdline */ -static void spawn_param_args(struct spawn_params *p) -{ - lua_State *L = p->L; - luaL_Buffer args; - luaL_buffinit(L, &args); - size_t i, n = lua_objlen(L, -1); - /* concatenate the arg array to a string */ - for (i = 1; i <= n; i++) { - int quote; - lua_rawgeti(L, -1, i); /* ... argtab arg */ - /* XXX checkstring is confusing here */ - quote = needs_quoting(luaL_checkstring(L, -1)); - luaL_putchar(&args, ' '); - if (quote) luaL_putchar(&args, '"'); - luaL_addvalue(&args); - if (quote) luaL_putchar(&args, '"'); - lua_pop(L, 1); /* ... argtab */ - } - luaL_pushresult(&args); /* ... argtab argstr */ - lua_pushvalue(L, 1); /* cmd opts ... argtab argstr cmd */ - lua_replace(L, -2); /* cmd opts ... argtab cmd argstr */ - lua_concat(L, 2); /* cmd opts ... argtab cmdline */ - lua_replace(L, -2); /* cmd opts ... cmdline */ - p->cmdline = lua_tostring(L, -1); -} - -/* ... envtab */ -static void spawn_param_env(struct spawn_params *p) -{ - lua_State *L = p->L; - luaL_Buffer env; - if (lua_isnil(L, -1)) { - p->environment = 0; - lua_pop(L, 1); - return; - } - /* convert env table to zstring list */ - /* {nam1=val1,nam2=val2} => "nam1=val1\0nam2=val2\0\0" */ - luaL_buffinit(L, &env); - lua_pushnil(L); /* ... envtab nil */ - while (lua_next(L, -2)) { /* ... envtab k v */ - /* XXX luaL_checktype is confusing here */ - luaL_checktype(L, -2, LUA_TSTRING); - luaL_checktype(L, -1, LUA_TSTRING); - lua_pushvalue(L, -2); /* ... envtab k v k */ - luaL_addvalue(&env); - luaL_putchar(&env, '='); - lua_pop(L, 1); /* ... envtab k v */ - luaL_addvalue(&env); - luaL_putchar(&env, '\0'); - lua_pop(L, 1); /* ... envtab k */ - } - luaL_putchar(&env, '\0'); - luaL_pushresult(&env); /* ... envtab envstr */ - lua_replace(L, -2); /* ... envtab envstr */ - p->environment = lua_tostring(L, -1); -} - -/* _ opts ... */ -static int get_redirect(struct spawn_params *p, const char *stdname, HANDLE *ph) -{ - int ret; - lua_getfield(p->L, 2, stdname); - if ((ret = !lua_isnil(p->L, -1))) { - /* XXX checkuserdata is confusing here */ - FILE **pf = luaL_checkuserdata(p->L, -1, LUA_FILEHANDLE); - *ph = get_handle(*pf); - } - lua_pop(p->L, 1); - return ret; -} - -/* _ opts ... */ -static void spawn_param_redirects(struct spawn_params *p) -{ - p->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - p->si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - p->si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - if (get_redirect(p, "stdin", &p->si.hStdInput) - | get_redirect(p, "stdout", &p->si.hStdOutput) - | get_redirect(p, "stderr", &p->si.hStdError)) - p->si.dwFlags = STARTF_USESTDHANDLES; -} - -#define PROCESS_HANDLE "process" -struct process { - int status; - HANDLE hProcess; -}; - -static int spawn_param_execute(struct spawn_params *p, struct process *proc) -{ - char *c = strdup(p->cmdline); - char *e = (char *)p->environment; // strdup(p->environment); - PROCESS_INFORMATION pi; - proc->status = -1; - /* XXX does CreateProcess modify its environment argument? */ - int ret = CreateProcess(0, c, 0, 0, 0, 0, e, 0, &p->si, &pi); - if (e) free(e); - free(c); - if (ret) proc->hProcess = pi.hProcess; - return ret; -} - /* filename [args-opts] -- true/nil,error */ /* args-opts -- true/nil,error */ static int ex_spawn(lua_State *L) @@ -427,20 +290,6 @@ static int ex_spawn(lua_State *L) return 1; /* ... proc */ } -/* proc -- exitcode/nil error */ -static int process_wait(lua_State *L) -{ - struct process *p = luaL_checkuserdata(L, 1, PROCESS_HANDLE); - if (p->status == -1) { - DWORD exitcode; - WaitForSingleObject(p->hProcess, INFINITE); - GetExitCodeProcess(p->hProcess, &exitcode); - p->status = exitcode; - } - lua_pushnumber(L, p->status); - return 1; -} - static const luaL_reg ex_iolib[] = { {"pipe", ex_pipe}, diff --git a/w32api/spawn.c b/w32api/spawn.c new file mode 100755 index 0000000..926a4ce --- /dev/null +++ b/w32api/spawn.c @@ -0,0 +1,153 @@ +#include + +#include +#include +#include + +#include + +#include "spawn.h" + +extern void *checkuserdata(lua_State *L, int index, const char *name); +extern HANDLE get_handle(FILE *f); + +static int needs_quoting(const char *s) +{ + return s[0] != '"' && strchr(s, ' '); +} + +/* filename ... */ +void spawn_param_filename(struct spawn_params *p) +{ + /* XXX luaL_checkstring is confusing here */ + p->cmdline = luaL_checkstring(p->L, 1); + if (needs_quoting(p->cmdline)) { + lua_pushliteral(p->L, "\""); /* cmd ... q */ + lua_pushvalue(p->L, 1); /* cmd ... q cmd */ + lua_pushvalue(p->L, -2); /* cmd ... q cmd q */ + lua_concat(p->L, 3); /* cmd ... "cmd" */ + lua_replace(p->L, 1); /* "cmd" ... */ + p->cmdline = lua_tostring(p->L, 1); + } +} + +/* -- */ +void spawn_param_defaults(struct spawn_params *p) +{ + p->environment = 0; + memset(&p->si, 0, sizeof p->si); + p->si.cb = sizeof p->si; +} + +/* cmd opts ... argtab -- cmd opts ... cmdline */ +void spawn_param_args(struct spawn_params *p) +{ + lua_State *L = p->L; + luaL_Buffer args; + luaL_buffinit(L, &args); + size_t i, n = lua_objlen(L, -1); + /* concatenate the arg array to a string */ + for (i = 1; i <= n; i++) { + int quote; + lua_rawgeti(L, -1, i); /* ... argtab arg */ + /* XXX checkstring is confusing here */ + quote = needs_quoting(luaL_checkstring(L, -1)); + luaL_putchar(&args, ' '); + if (quote) luaL_putchar(&args, '"'); + luaL_addvalue(&args); + if (quote) luaL_putchar(&args, '"'); + lua_pop(L, 1); /* ... argtab */ + } + luaL_pushresult(&args); /* ... argtab argstr */ + lua_pushvalue(L, 1); /* cmd opts ... argtab argstr cmd */ + lua_replace(L, -2); /* cmd opts ... argtab cmd argstr */ + lua_concat(L, 2); /* cmd opts ... argtab cmdline */ + lua_replace(L, -2); /* cmd opts ... cmdline */ + p->cmdline = lua_tostring(L, -1); +} + +/* ... envtab */ +void spawn_param_env(struct spawn_params *p) +{ + lua_State *L = p->L; + luaL_Buffer env; + if (lua_isnil(L, -1)) { + p->environment = 0; + lua_pop(L, 1); + return; + } + /* convert env table to zstring list */ + /* {nam1=val1,nam2=val2} => "nam1=val1\0nam2=val2\0\0" */ + luaL_buffinit(L, &env); + lua_pushnil(L); /* ... envtab nil */ + while (lua_next(L, -2)) { /* ... envtab k v */ + /* XXX luaL_checktype is confusing here */ + luaL_checktype(L, -2, LUA_TSTRING); + luaL_checktype(L, -1, LUA_TSTRING); + lua_pushvalue(L, -2); /* ... envtab k v k */ + luaL_addvalue(&env); + luaL_putchar(&env, '='); + lua_pop(L, 1); /* ... envtab k v */ + luaL_addvalue(&env); + luaL_putchar(&env, '\0'); + lua_pop(L, 1); /* ... envtab k */ + } + luaL_putchar(&env, '\0'); + luaL_pushresult(&env); /* ... envtab envstr */ + lua_replace(L, -2); /* ... envtab envstr */ + p->environment = lua_tostring(L, -1); +} + +/* _ opts ... */ +static int get_redirect(struct spawn_params *p, const char *stdname, HANDLE *ph) +{ + int ret; + lua_getfield(p->L, 2, stdname); + if ((ret = !lua_isnil(p->L, -1))) { + /* XXX checkuserdata is confusing here */ + FILE **pf = checkuserdata(p->L, -1, LUA_FILEHANDLE); + *ph = get_handle(*pf); + } + lua_pop(p->L, 1); + return ret; +} + +/* _ opts ... */ +void spawn_param_redirects(struct spawn_params *p) +{ + p->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + p->si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + p->si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + if (get_redirect(p, "stdin", &p->si.hStdInput) + | get_redirect(p, "stdout", &p->si.hStdOutput) + | get_redirect(p, "stderr", &p->si.hStdError)) + p->si.dwFlags = STARTF_USESTDHANDLES; +} + +int spawn_param_execute(struct spawn_params *p, struct process *proc) +{ + char *c = strdup(p->cmdline); + char *e = (char *)p->environment; // strdup(p->environment); + PROCESS_INFORMATION pi; + proc->status = -1; + /* XXX does CreateProcess modify its environment argument? */ + int ret = CreateProcess(0, c, 0, 0, 0, 0, e, 0, &p->si, &pi); + if (e) free(e); + free(c); + if (ret) proc->hProcess = pi.hProcess; + return ret; +} + +/* proc -- exitcode/nil error */ +int process_wait(lua_State *L) +{ + struct process *p = checkuserdata(L, 1, PROCESS_HANDLE); + if (p->status == -1) { + DWORD exitcode; + WaitForSingleObject(p->hProcess, INFINITE); + GetExitCodeProcess(p->hProcess, &exitcode); + p->status = exitcode; + } + lua_pushnumber(L, p->status); + return 1; +} diff --git a/w32api/spawn.h b/w32api/spawn.h new file mode 100755 index 0000000..1d91235 --- /dev/null +++ b/w32api/spawn.h @@ -0,0 +1,29 @@ +#ifndef SPAWN_H +#define SPAWN_H +#include + +#include + +struct spawn_params { + lua_State *L; + const char *cmdline; + const char *environment; + STARTUPINFO si; +}; + +#define PROCESS_HANDLE "process" +struct process { + int status; + HANDLE hProcess; +}; + +void spawn_param_filename(struct spawn_params *p); +void spawn_param_defaults(struct spawn_params *p); +void spawn_param_args(struct spawn_params *p); +void spawn_param_env(struct spawn_params *p); +void spawn_param_redirects(struct spawn_params *p); +int spawn_param_execute(struct spawn_params *p, struct process *proc); + +int process_wait(lua_State *L); + +#endif/*SPAWN_H*/