diff --git a/posix/Makefile b/posix/Makefile index f7f7508..cf829cc 100755 --- a/posix/Makefile +++ b/posix/Makefile @@ -1,5 +1,8 @@ CFLAGS = -std=c99 $(WARNINGS) $(DEFINES) $(INCLUDES) -DEFINES = -D_POSIX_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L -U__STRICT_ANSI__ -DMISSING_POSIX_SPAWN +DEFINES = -D_POSIX_SOURCE -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L \ + -U__STRICT_ANSI__ \ + -D_GNU_SOURCE -DMISSING_POSIX_SPAWN \ + -D'debug(...)=fprintf(stderr,__VA_ARGS__)' INCLUDES = -I$(LUA)/include WARNINGS = -W -Wall #LUA = /home/mark/src/lang/lua/lua-5.1-rc2 diff --git a/posix/ex.c b/posix/ex.c index f9dc58d..9ebc181 100755 --- a/posix/ex.c +++ b/posix/ex.c @@ -17,14 +17,7 @@ #include "spawn.h" -/* Generally useful function -- what luaL_checkudata() should do */ -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; -} - +#define absindex(L,i) ((i)>0?(i):lua_gettop(L)+(i)+1) /* -- nil error */ extern int push_error(lua_State *L) @@ -123,9 +116,19 @@ static int ex_currentdir(lua_State *L) } -static FILE *check_file(lua_State *L, int idx) +FILE *check_file(lua_State *L, int idx, const char *argname) { - FILE **pf = checkuserdata(L, idx, LUA_FILEHANDLE); + FILE **pf; + if (idx > 0) pf = luaL_checkudata(L, idx, LUA_FILEHANDLE); + else { + idx = absindex(idx); + pf = lua_touserdata(L, idx); + luaL_getmetatable(L, LUA_FILEHANDLE); + if (!pf || !lua_getmetatable(L, idx) || !lua_rawequal(L, -1, -2)) + luaL_error(L, "%s option: expected %s, got %s", + argname, LUA_FILEHANDLE, luaL_typename(L, idx)); + lua_pop(L, 2); + } if (!*pf) return luaL_error(L, "attempt to use a closed file"), NULL; return *pf; } @@ -142,7 +145,7 @@ static int ex_dirent(lua_State *L) return push_error(L); } break; case LUA_TUSERDATA: { - FILE *f = check_file(L, 1); + FILE *f = check_file(L, 1, NULL); if (-1 == fstat(fileno(f), &st)) return push_error(L); } break; @@ -186,9 +189,9 @@ static int diriter_setpathname(lua_State *L, int index) lua_pushliteral(L, LUA_DIRSEP); lua_concat(L, 2); } - lua_pushvalue(L, index); /* ... pathname diriter */ - lua_insert(L, -2); /* ... diriter pathname */ - lua_settable(L, LUA_REGISTRYINDEX); /* ... */ + lua_pushvalue(L, index); /* ... pathname diriter */ + lua_insert(L, -2); /* ... diriter pathname */ + lua_settable(L, LUA_REGISTRYINDEX); /* ... */ return 0; } @@ -216,32 +219,32 @@ static int ex_dir(lua_State *L) default: return luaL_argerror(L, 1, "expected pathname"); case LUA_TSTRING: pathname = lua_tostring(L, 1); - lua_pushcfunction(L, ex_dir); /* pathname ... iter */ - pi = lua_newuserdata(L, sizeof *pi); /* pathname ... iter state */ + lua_pushcfunction(L, ex_dir); /* pathname ... iter */ + pi = lua_newuserdata(L, sizeof *pi);/* pathname ... iter state */ pi->dir = opendir(pathname); if (!pi->dir) return push_error(L); - luaL_getmetatable(L, DIR_HANDLE); /* pathname ... iter state M */ - lua_setmetatable(L, -2); /* pathname ... iter state */ - lua_pushvalue(L, 1); /* pathname ... iter state pathname */ - diriter_setpathname(L, -2); /* pathname ... iter state */ + luaL_getmetatable(L, DIR_HANDLE); /* pathname ... iter state M */ + lua_setmetatable(L, -2); /* pathname ... iter state */ + lua_pushvalue(L, 1); /* pathname ... iter state pathname */ + diriter_setpathname(L, -2); /* pathname ... iter state */ return 2; case LUA_TUSERDATA: - pi = checkuserdata(L, 1, DIR_HANDLE); + pi = luaL_checkudata(L, 1, DIR_HANDLE); d = readdir(pi->dir); if (!d) { closedir(pi->dir); pi->dir = 0; return push_error(L); } - lua_newtable(L); /* diriter ... entry */ - diriter_getpathname(L, 1); /* diriter ... entry dirpath */ - lua_pushstring(L, d->d_name); /* diriter ... entry dirpath name */ - lua_pushliteral(L, "name"); /* diriter ... entry dirpath name "name" */ - lua_pushvalue(L, -2); /* diriter ... entry dirpath name "name" name */ - lua_settable(L, -5); /* diriter ... entry dirpath name */ - lua_concat(L, 2); /* diriter ... entry fullpath */ - lua_replace(L, 1); /* fullpath ... entry */ - lua_replace(L, 2); /* fullpath entry ... */ + lua_newtable(L); /* diriter ... entry */ + diriter_getpathname(L, 1); /* diriter ... entry dirpath */ + lua_pushstring(L, d->d_name); /* diriter ... entry dirpath name */ + lua_pushliteral(L, "name"); /* diriter ... entry dirpath name "name" */ + lua_pushvalue(L, -2); /* diriter ... entry dirpath name "name" name */ + lua_settable(L, -5); /* diriter ... entry dirpath name */ + lua_concat(L, 2); /* diriter ... entry fullpath */ + lua_replace(L, 1); /* fullpath ... entry */ + lua_replace(L, 2); /* fullpath entry ... */ return ex_dirent(L); } /*NOTREACHED*/ @@ -269,7 +272,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 *f = check_file(L, 1); + FILE *f = check_file(L, 1, NULL); const char *mode = luaL_checkstring(L, 2); long offset = luaL_optnumber(L, 3, 0); long length = luaL_optnumber(L, 4, 0); @@ -310,86 +313,88 @@ static int ex_pipe(lua_State *L) FILE *i, *o, **pf; if (!make_pipe(&i, &o)) return push_error(L); - luaL_getmetatable(L, LUA_FILEHANDLE); - *(pf = lua_newuserdata(L, sizeof *pf)) = i; - lua_pushvalue(L, -2); - lua_setmetatable(L, -2); - *(pf = lua_newuserdata(L, sizeof *pf)) = o; - lua_pushvalue(L, -3); - lua_setmetatable(L, -2); + debug("make_pipe returns %p<-%p\n", i, o); + luaL_getmetatable(L, LUA_FILEHANDLE); /* M */ + *(pf = lua_newuserdata(L, sizeof *pf)) = i; /* M i */ + lua_pushvalue(L, -2); /* M i M */ + lua_setmetatable(L, -2); /* M i */ + *(pf = lua_newuserdata(L, sizeof *pf)) = o; /* M i o */ + lua_pushvalue(L, -3); /* M i o M */ + lua_setmetatable(L, -2); /* M i o */ return 2; } +static void get_redirect(lua_State *L, int idx, const char *stdname, struct spawn_params *p) +{ + lua_getfield(L, idx, stdname); + if (!lua_isnil(L, -1)) + spawn_param_redirect(p, stdname, check_file(L, -1, stdname)); + lua_pop(L, 1); +} + /* filename [args-opts] -- true/nil error */ /* args-opts -- true/nil error */ static int ex_spawn(lua_State *L) { - struct spawn_params params = {L}; - struct process *proc; + struct spawn_params *params = spawn_param_init(L); if (lua_type(L, 1) == LUA_TTABLE) { - lua_getfield(L, 1, "command"); /* opts ... cmd */ + lua_getfield(L, 1, "command"); /* opts ... cmd */ if (!lua_isnil(L, -1)) { /* convert {command=command,arg1,...} to command {arg1,...} */ - lua_insert(L, 1); /* cmd opts ... */ + lua_insert(L, 1); /* cmd opts ... */ } else { /* convert {arg0,arg1,...} to arg0 {arg1,...} */ size_t i, n = lua_objlen(L, 1); - lua_rawgeti(L, 1, 1); /* opts ... nil cmd */ - if (lua_isnil(L, -1)) - return luaL_error(L, "no command specified"); - /* XXX check LUA_TSTRING */ - lua_insert(L, 1); /* cmd opts ... nil */ + lua_rawgeti(L, 1, 1); /* opts ... nil cmd */ + lua_insert(L, 1); /* cmd opts ... nil */ for (i = 2; i <= n; i++) { - lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */ - lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */ + lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */ + lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */ } - lua_rawseti(L, 2, n); /* cmd opts ... */ + lua_rawseti(L, 2, n); /* cmd opts ... */ } } /* get filename to execute */ - spawn_param_filename(¶ms); + if (lua_type(L, 1) != LUA_TSTRING) + return luaL_error(L, "command option: expected string, got %s", luaL_typename(L, 1)); + spawn_param_filename(params, lua_tostring(L, 1)); /* get arguments, environment, and redirections */ switch (lua_type(L, 2)) { default: return luaL_argerror(L, 2, "expected options table"); - case LUA_TNONE: - spawn_param_defaults(¶ms); /* cmd opts ... */ - break; + case LUA_TNONE: break; case LUA_TTABLE: - lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ + lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ switch (lua_type(L, -1)) { default: return luaL_error(L, "args option must be an array"); case LUA_TNIL: - lua_pop(L, 1); /* cmd opts ... */ - lua_pushvalue(L, 2); /* cmd opts ... opts */ + lua_pop(L, 1); /* cmd opts ... */ + lua_pushvalue(L, 2); /* cmd opts ... opts */ if (0) /*FALLTHRU*/ case LUA_TTABLE: if (lua_objlen(L, 2) > 0) return luaL_error(L, "cannot specify both the args option and array values"); - spawn_param_args(¶ms); /* cmd opts ... */ + spawn_param_args(params); /* cmd opts ... */ break; } - lua_getfield(L, 2, "env"); /* cmd opts ... envtab */ + lua_getfield(L, 2, "env"); /* cmd opts ... envtab */ switch (lua_type(L, -1)) { default: return luaL_error(L, "env option must be a table"); case LUA_TNIL: case LUA_TTABLE: - spawn_param_env(¶ms); /* cmd opts ... */ + spawn_param_env(params); /* cmd opts ... */ break; } - spawn_param_redirects(¶ms); /* cmd opts ... */ + get_redirect(L, 2, "stdin", params); /* cmd opts ... */ + get_redirect(L, 2, "stdout", params); /* cmd opts ... */ + get_redirect(L, 2, "stderr", params); /* cmd opts ... */ break; } - proc = lua_newuserdata(L, sizeof *proc); /* cmd opts ... proc */ - luaL_getmetatable(L, PROCESS_HANDLE); /* cmd opts ... proc M */ - lua_setmetatable(L, -2); /* cmd opts ... proc */ - if (!spawn_param_execute(¶ms, proc)) - return push_error(L); - return 1; /* ... proc */ + return spawn_param_execute(params); /* proc/nil error */ } @@ -422,47 +427,69 @@ static const luaL_reg ex_oslib[] = { }; static const luaL_reg ex_diriter_methods[] = { {"__gc", diriter_close}, +/* {"__tostring", diriter_tostring}, */ {0,0} }; static const luaL_reg ex_process_methods[] = { - {"wait", process_wait}, {"__tostring", process_tostring}, + {"wait", process_wait}, {0,0} }; +/* copy the fields given in 'l' from one table to another; insert missing fields */ +static void copy_fields(lua_State *L, const luaL_reg *l, int from, int to) +{ + from = absindex(L, from); + to = absindex(L, to); + for (; l->name; l++) { + lua_getfield(L, from, l->name); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_pushcfunction(L, l->func); + } + lua_setfield(L, to, l->name); + } +} + int luaopen_ex(lua_State *L) { - /* extend the io table */ - lua_getglobal(L, "io"); - if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded"); - luaL_openlib(L, 0, ex_iolib, 0); + /* Make all functions available via ex. namespace */ + luaL_register(L, "ex", ex_iolib); /* . ex */ + luaL_register(L, 0, ex_oslib); + luaL_register(L, 0, ex_iofile_methods); + luaL_register(L, 0, ex_process_methods + 1); /* XXX don't insert __tostring */ + lua_replace(L, 1); /* ex . */ /* extend the os table */ - lua_getglobal(L, "os"); + lua_getglobal(L, "os"); /* ex . os */ if (lua_isnil(L, -1)) return luaL_error(L, "os not loaded"); - luaL_openlib(L, "os", ex_oslib, 0); + copy_fields(L, ex_oslib, 1, -1); /* ex . os */ + + /* extend the io table */ + lua_getglobal(L, "io"); /* ex . io */ + if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded"); + copy_fields(L, ex_iolib, 1, -1); /* ex . io */ + lua_getfield(L, 1, "pipe"); /* ex . io ex_pipe */ + lua_getfield(L, -2, "stderr"); /* ex . io ex_pipe io_stderr */ + lua_getfenv(L, -1); /* ex . io ex_pipe io_stderr E */ + lua_setfenv(L, -3); /* ex . io ex_pipe io_stderr */ /* extend the io.file metatable */ - luaL_getmetatable(L, LUA_FILEHANDLE); + luaL_getmetatable(L, LUA_FILEHANDLE); /* ex . F */ if (lua_isnil(L, -1)) return luaL_error(L, "can't find FILE* metatable"); - luaL_openlib(L, 0, ex_iofile_methods, 0); + copy_fields(L, ex_iofile_methods, 1, -1); /* ex . F */ /* diriter metatable */ - luaL_newmetatable(L, DIR_HANDLE); - luaL_openlib(L, 0, ex_diriter_methods, 0); + luaL_newmetatable(L, DIR_HANDLE); /* ex . D */ + luaL_register(L, 0, ex_diriter_methods); /* ex . D */ /* proc metatable */ - luaL_newmetatable(L, PROCESS_HANDLE); /* proc */ - luaL_openlib(L, 0, ex_process_methods, 0); /* proc */ - lua_pushliteral(L, "__index"); /* proc __index */ - lua_pushvalue(L, -2); /* proc __index proc */ - lua_settable(L, -3); /* proc */ - - /* Make all functions available via ex. namespace */ - luaL_openlib(L, "ex", ex_iolib, 0); - luaL_openlib(L, 0, ex_oslib, 0); - luaL_openlib(L, 0, ex_iofile_methods, 0); - luaL_openlib(L, 0, ex_process_methods, 0); + luaL_newmetatable(L, PROCESS_HANDLE); /* ex . P */ + copy_fields(L, ex_process_methods, 1, -1); /* ex . P */ + lua_pushliteral(L, "__index"); /* ex . P __index */ + lua_pushvalue(L, -2); /* ex . P __index P */ + lua_settable(L, -3); /* ex . P */ + lua_settop(L, 1); /* ex */ return 1; } diff --git a/posix/spawn.c b/posix/spawn.c index e06bcaa..d3a3028 100755 --- a/posix/spawn.c +++ b/posix/spawn.c @@ -4,27 +4,36 @@ #include #include +#ifndef MISSING_POSIX_SPAWN +#include +#else +#include "posix_spawn.h" +#endif #include "spawn.h" +struct spawn_params { + lua_State *L; + const char *command, **argv, **envp; + posix_spawn_file_actions_t redirect; +}; + extern int push_error(lua_State *L); -extern void *checkuserdata(lua_State *L, int index, const char *name); +extern FILE *check_file(lua_State *L, int idx, const char *argname); -/* filename ... */ -void spawn_param_filename(struct spawn_params *p) +struct spawn_params *spawn_param_init(lua_State *L) { - /* XXX confusing */ - p->command = luaL_checkstring(p->L, 1); + struct spawn_params *p = lua_newuserdata(L, sizeof *p); + p->L = L; + p->command = 0; + p->argv = p->envp = 0; + posix_spawn_file_actions_init(&p->redirect); + return p; } -/* -- */ -void spawn_param_defaults(struct spawn_params *p) +void spawn_param_filename(struct spawn_params *p, const char *filename) { - 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; + p->command = filename; } /* Converts a Lua array of strings to a null-terminated array of char pointers. @@ -86,44 +95,50 @@ void spawn_param_env(struct spawn_params *p) make_vector(p->L); /* ... arr */ } -/* _ opts ... */ -static int get_redirect(struct spawn_params *p, const char *stdname, int descriptor) +void spawn_param_redirect(struct spawn_params *p, const char *stdname, FILE *f) { - 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; + int d; + switch (stdname[3]) { + case 'i': d = STDIN_FILENO; break; + case 'o': d = STDOUT_FILENO; break; + case 'e': d = STDERR_FILENO; break; + } + debug("duplicating %d to %d\n", fileno(f), d); + posix_spawn_file_actions_adddup2(&p->redirect, fileno(f), d); } -/* _ 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); -} +struct process { + int status; + pid_t pid; +}; -int spawn_param_execute(struct spawn_params *p, struct process *proc) +int spawn_param_execute(struct spawn_params *p) { + lua_State *L = p->L; + struct process *proc = lua_newuserdata(L, sizeof *proc); int ret; + luaL_getmetatable(L, PROCESS_HANDLE); + lua_setmetatable(L, -2); 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; + if (!p->argv) { + p->argv = lua_newuserdata(L, 2 * sizeof *p->argv); + p->argv[0] = p->command; + p->argv[1] = 0; + } + if (!p->envp) + p->envp = (const char **)environ; + ret = posix_spawnp(&proc->pid, p->command, &p->redirect, 0, (char *const *)p->argv, (char *const *)p->envp); + posix_spawn_file_actions_destroy(&p->redirect); + return ret != 0 ? push_error(L) : 1; } +extern int push_error(lua_State *L); + /* proc -- exitcode/nil error */ int process_wait(lua_State *L) { - struct process *p = checkuserdata(L, 1, PROCESS_HANDLE); + struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); if (p->status == -1) { int status; if (-1 == waitpid(p->pid, &status, 0)) @@ -137,7 +152,7 @@ int process_wait(lua_State *L) /* proc -- string */ int process_tostring(lua_State *L) { - struct process *p = checkuserdata(L, 1, PROCESS_HANDLE); + struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); char buf[40]; lua_pushlstring(L, buf, sprintf(buf, "process (%lu, %s)", (unsigned long)p->pid, diff --git a/posix/spawn.h b/posix/spawn.h index 64fcee1..e1862ba 100755 --- a/posix/spawn.h +++ b/posix/spawn.h @@ -1,32 +1,18 @@ #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; -}; +struct process; +struct spawn_params; -void spawn_param_filename(struct spawn_params *p); -void spawn_param_defaults(struct spawn_params *p); +struct spawn_params *spawn_param_init(lua_State *L); +void spawn_param_filename(struct spawn_params *p, const char *filename); 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); +void spawn_param_redirect(struct spawn_params *p, const char *stdname, FILE *f); +int spawn_param_execute(struct spawn_params *p); int process_wait(lua_State *L); int process_tostring(lua_State *L); diff --git a/tests/rt5.lua b/tests/rt5.lua index de5dc9a..6648592 100755 --- a/tests/rt5.lua +++ b/tests/rt5.lua @@ -1,7 +1,7 @@ #!/usr/bin/env lua require "ex" -local proc -proc = assert(os.spawn(arg[1])) +assert(arg[1], "argument required") +local proc = assert(os.spawn(arg[1])) print(proc) print(assert(proc:wait())) diff --git a/tests/rt6.lua b/tests/rt6.lua new file mode 100755 index 0000000..b23dfec --- /dev/null +++ b/tests/rt6.lua @@ -0,0 +1,14 @@ +#!/usr/bin/env lua +require "ex" + +for e in assert(os.dir(".")) do + print(string.format("%.3s %9d %s", e.type or 'Unknown', e.size or -1, e.name)) +end +--[[ +local f,s,i = assert(os.dir("..")) +print(f,s,i) +i=assert(f(s,i)) +for e in f,s,i do + print(e.name,e.type,e.size) +end +--]] diff --git a/tests/rt7.lua b/tests/rt7.lua new file mode 100755 index 0000000..612cad6 --- /dev/null +++ b/tests/rt7.lua @@ -0,0 +1,18 @@ +#!/usr/bin/env lua +require "ex" +assert(arg[1], "need a command name") +print"io.pipe()" +local i, o = assert(io.pipe()) +print("got", i, o) +print"os.spawn()" +local t = {command = arg[1], stdin = i} +print(t.stdin) +local proc = assert(os.spawn(t)) +print"i:close()" +i:close() +print"o:write()" +o:write("Hello\nWorld\n") +print"o:close()" +o:close() +print"proc:wait()" +print("exit status:", assert(proc:wait())) diff --git a/w32api/ex.c b/w32api/ex.c index 7dd74f2..2c14e44 100755 --- a/w32api/ex.c +++ b/w32api/ex.c @@ -15,13 +15,7 @@ #include "spawn.h" -/* Generally useful function -- what luaL_checkudata() should do */ -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; -} +#define absindex(L,i) ((i)>0?(i):lua_gettop(L)+(i)+1) /* return HANDLE from a FILE */ extern HANDLE get_handle(FILE *f) @@ -144,9 +138,19 @@ static int ex_currentdir(lua_State *L) } -static FILE *check_file(lua_State *L, int idx) +FILE *check_file(lua_State *L, int idx, const char *argname) { - FILE **pf = checkuserdata(L, idx, LUA_FILEHANDLE); + FILE **pf; + if (idx > 0) pf = luaL_checkudata(L, idx, LUA_FILEHANDLE); + else { + idx = absindex(idx); + pf = lua_touserdata(L, idx); + luaL_getmetatable(L, LUA_FILEHANDLE); + if (!pf || !lua_getmetatable(L, idx - 1) || !lua_rawequal(L, -1, -2)) + luaL_error(L, "%s option: expected %s, got %s", + argname, LUA_FILEHANDLE, luaL_typename(L, idx - 2)); + lua_pop(L, 2); + } if (!*pf) return luaL_error(L, "attempt to use a closed file"), NULL; return *pf; } @@ -187,7 +191,7 @@ static int ex_dirent(lua_State *L) size = get_size(name); } break; case LUA_TUSERDATA: { - FILE *f = check_file(L, 1); + FILE *f = check_file(L, 1, NULL); BY_HANDLE_FILE_INFORMATION info; if (!GetFileInformationByHandle(get_handle(f), &info)) return push_error(L); @@ -235,9 +239,9 @@ static int diriter_setpathname(lua_State *L, int index) lua_pushliteral(L, LUA_DIRSEP); lua_concat(L, 2); } - lua_pushvalue(L, index); /* ... pathname diriter */ - lua_insert(L, -2); /* ... diriter pathname */ - lua_settable(L, LUA_REGISTRYINDEX); /* ... */ + lua_pushvalue(L, index); /* ... pathname diriter */ + lua_insert(L, -2); /* ... diriter pathname */ + lua_settable(L, LUA_REGISTRYINDEX); /* ... */ return 0; } @@ -263,37 +267,37 @@ static int ex_dir(lua_State *L) switch (lua_type(L, 1)) { default: return luaL_argerror(L, 1, "expected pathname"); case LUA_TSTRING: - lua_pushvalue(L, 1); /* pathname ... pathname */ - lua_pushliteral(L, "\\*"); /* pathname ... pathname "\\*" */ - lua_concat(L, 2); /* pathname ... pattern */ + lua_pushvalue(L, 1); /* pathname ... pathname */ + lua_pushliteral(L, "\\*"); /* pathname ... pathname "\\*" */ + lua_concat(L, 2); /* pathname ... pattern */ pathname = lua_tostring(L, -1); - lua_pushcfunction(L, ex_dir); /* pathname ... pat iter */ - pi = lua_newuserdata(L, sizeof *pi); /* pathname ... pat iter state */ + lua_pushcfunction(L, ex_dir); /* pathname ... pat iter */ + pi = lua_newuserdata(L, sizeof *pi);/* pathname ... pat iter state */ pi->hf = FindFirstFile(pathname, &pi->fd); if (pi->hf == INVALID_HANDLE_VALUE) return push_error(L); - luaL_getmetatable(L, DIR_HANDLE); /* pathname ... pat iter state M */ - lua_setmetatable(L, -2); /* pathname ... pat iter state */ - lua_pushvalue(L, 1); /* pathname ... pat iter state pathname */ - diriter_setpathname(L, -2); /* pathname ... pat iter state */ + luaL_getmetatable(L, DIR_HANDLE); /* pathname ... pat iter state M */ + lua_setmetatable(L, -2); /* pathname ... pat iter state */ + lua_pushvalue(L, 1); /* pathname ... pat iter state pathname */ + diriter_setpathname(L, -2); /* pathname ... pat iter state */ return 2; case LUA_TUSERDATA: - pi = checkuserdata(L, 1, DIR_HANDLE); + pi = luaL_checkudata(L, 1, DIR_HANDLE); if (pi->hf == INVALID_HANDLE_VALUE) return 0; - lua_newtable(L); /* diriter ... entry */ - diriter_getpathname(L, 1); /* diriter ... entry dirpath */ - lua_pushstring(L, pi->fd.cFileName); /* diriter ... entry dirpath name */ - lua_pushliteral(L, "name"); /* diriter ... entry dirpath name "name" */ - lua_pushvalue(L, -2); /* diriter ... entry dirpath name "name" name */ - lua_settable(L, -5); /* diriter ... entry dirpath name */ - lua_concat(L, 2); /* diriter ... entry fullpath */ + lua_newtable(L); /* diriter ... entry */ + diriter_getpathname(L, 1); /* diriter ... entry dirpath */ + lua_pushstring(L, pi->fd.cFileName);/* diriter ... entry dirpath name */ + lua_pushliteral(L, "name"); /* diriter ... entry dirpath name "name" */ + lua_pushvalue(L, -2); /* diriter ... entry dirpath name "name" name */ + lua_settable(L, -5); /* diriter ... entry dirpath name */ + lua_concat(L, 2); /* diriter ... entry fullpath */ if (!FindNextFile(pi->hf, &pi->fd)) { FindClose(pi->hf); pi->hf = INVALID_HANDLE_VALUE; } - lua_replace(L, 1); /* fullpath ... entry */ - lua_replace(L, 2); /* fullpath entry ... */ + lua_replace(L, 1); /* fullpath ... entry */ + lua_replace(L, 2); /* fullpath entry ... */ return ex_dirent(L); } /*NOTREACHED*/ @@ -304,8 +308,8 @@ static int file_lock(lua_State *L, FILE *f, const char *mode, long offset, long { HANDLE h = get_handle(f); DWORD flags; - LARGE_INTEGER len = {0}; - OVERLAPPED ov = {0}; + LARGE_INTEGER len = {0,0}; + OVERLAPPED ov = {.hEvent = INVALID_HANDLE_VALUE}; BOOL ret; if (length) len.LowPart = length; else len.LowPart = GetFileSize(h, &len.HighPart); @@ -327,7 +331,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 *f = check_file(L, 1); + FILE *f = check_file(L, 1, NULL); const char *mode = luaL_checkstring(L, 2); long offset = luaL_optnumber(L, 3, 0); long length = luaL_optnumber(L, 4, 0); @@ -362,86 +366,87 @@ static int ex_pipe(lua_State *L) FILE *i, *o, **pf; if (!make_pipe(&i, &o)) return push_error(L); - luaL_getmetatable(L, LUA_FILEHANDLE); - *(pf = lua_newuserdata(L, sizeof *pf)) = i; - lua_pushvalue(L, -2); - lua_setmetatable(L, -2); - *(pf = lua_newuserdata(L, sizeof *pf)) = o; - lua_pushvalue(L, -3); - lua_setmetatable(L, -2); + luaL_getmetatable(L, LUA_FILEHANDLE); /* M */ + *(pf = lua_newuserdata(L, sizeof *pf)) = i; /* M i */ + lua_pushvalue(L, -2); /* M i M */ + lua_setmetatable(L, -2); /* M i */ + *(pf = lua_newuserdata(L, sizeof *pf)) = o; /* M i o */ + lua_pushvalue(L, -3); /* M i o M */ + lua_setmetatable(L, -2); /* M i o */ return 2; } +static void get_redirect(lua_State *L, int idx, const char *stdname, struct spawn_params *p) +{ + lua_getfield(L, idx, stdname); + if (!lua_isnil(L, -1)) + spawn_param_redirect(p, stdname, check_file(L, -1, stdname)); + lua_pop(L, 1); +} + /* filename [args-opts] -- true/nil error */ /* args-opts -- true/nil error */ static int ex_spawn(lua_State *L) { - struct spawn_params params = {L}; - struct process *proc; + struct spawn_params *params = spawn_param_init(L); if (lua_type(L, 1) == LUA_TTABLE) { - lua_getfield(L, 1, "command"); /* opts ... cmd */ + lua_getfield(L, 1, "command"); /* opts ... cmd */ if (!lua_isnil(L, -1)) { /* convert {command=command,arg1,...} to command {arg1,...} */ - lua_insert(L, 1); /* cmd opts ... */ + lua_insert(L, 1); /* cmd opts ... */ } else { /* convert {arg0,arg1,...} to arg0 {arg1,...} */ size_t i, n = lua_objlen(L, 1); - lua_rawgeti(L, 1, 1); /* opts ... nil cmd */ - if (lua_isnil(L, -1)) - return luaL_error(L, "no command specified"); - /* XXX check LUA_TSTRING */ - lua_insert(L, 1); /* cmd opts ... nil */ + lua_rawgeti(L, 1, 1); /* opts ... nil cmd */ + lua_insert(L, 1); /* cmd opts ... nil */ for (i = 2; i <= n; i++) { - lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */ - lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */ + lua_rawgeti(L, 2, i); /* cmd opts ... nil argi */ + lua_rawseti(L, 2, i - 1); /* cmd opts ... nil */ } - lua_rawseti(L, 2, n); /* cmd opts ... */ + lua_rawseti(L, 2, n); /* cmd opts ... */ } } /* get filename to execute */ - spawn_param_filename(¶ms); + if (lua_type(L, 1) != LUA_TSTRING) + return luaL_error(L, "command option: expected string, got %s", luaL_typename(L, 1)); + spawn_param_filename(params, lua_tostring(L, 1)); /* get arguments, environment, and redirections */ switch (lua_type(L, 2)) { default: return luaL_argerror(L, 2, "expected options table"); - case LUA_TNONE: - spawn_param_defaults(¶ms); /* cmd opts ... */ - break; + case LUA_TNONE: break; case LUA_TTABLE: - lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ + lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ switch (lua_type(L, -1)) { default: return luaL_error(L, "args option must be an array"); case LUA_TNIL: - lua_pop(L, 1); /* cmd opts ... */ - lua_pushvalue(L, 2); /* cmd opts ... opts */ + lua_pop(L, 1); /* cmd opts ... */ + lua_pushvalue(L, 2); /* cmd opts ... opts */ if (0) /*FALLTHRU*/ case LUA_TTABLE: if (lua_objlen(L, 2) > 0) return luaL_error(L, "cannot specify both the args option and array values"); - spawn_param_args(¶ms); /* cmd opts ... */ + spawn_param_args(params); /* cmd opts ... */ break; } - lua_getfield(L, 2, "env"); /* cmd opts ... envtab */ + lua_getfield(L, 2, "env"); /* cmd opts ... envtab */ switch (lua_type(L, -1)) { default: return luaL_error(L, "env option must be a table"); case LUA_TNIL: case LUA_TTABLE: - spawn_param_env(¶ms); /* cmd opts ... */ + spawn_param_env(params); /* cmd opts ... */ break; } - spawn_param_redirects(¶ms); /* cmd opts ... */ + get_redirect(L, 2, "stdin", params); /* cmd opts ... */ + get_redirect(L, 2, "stdout", params); /* cmd opts ... */ + get_redirect(L, 2, "stderr", params); /* cmd opts ... */ break; } - proc = lua_newuserdata(L, sizeof *proc); /* cmd opts ... proc */ - luaL_getmetatable(L, PROCESS_HANDLE); /* cmd opts ... proc M */ - lua_setmetatable(L, -2); /* cmd opts ... proc */ - if (!spawn_param_execute(¶ms, proc)) - return push_error(L); - return 1; /* ... proc */ + return spawn_param_execute(params); /* proc/nil error */ } @@ -474,47 +479,69 @@ static const luaL_reg ex_oslib[] = { }; static const luaL_reg ex_diriter_methods[] = { {"__gc", diriter_close}, +/* {"__tostring", diriter_tostring}, */ {0,0} }; static const luaL_reg ex_process_methods[] = { - {"wait", process_wait}, {"__tostring", process_tostring}, + {"wait", process_wait}, {0,0} }; +/* copy the fields given in 'l' from one table to another; insert missing fields */ +static void copy_fields(lua_State *L, const luaL_reg *l, int from, int to) +{ + from = absindex(L, from); + to = absindex(L, to); + for (; l->name; l++) { + lua_getfield(L, from, l->name); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_pushcfunction(L, l->func); + } + lua_setfield(L, to, l->name); + } +} + int luaopen_ex(lua_State *L) { - /* extend the io table */ - lua_getglobal(L, "io"); - if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded"); - luaL_openlib(L, 0, ex_iolib, 0); + /* Make all functions available via ex. namespace */ + luaL_register(L, "ex", ex_iolib); /* . ex */ + luaL_register(L, 0, ex_oslib); + luaL_register(L, 0, ex_iofile_methods); + luaL_register(L, 0, ex_process_methods + 1); /* XXX don't insert __tostring */ + lua_replace(L, 1); /* ex . */ /* extend the os table */ - lua_getglobal(L, "os"); + lua_getglobal(L, "os"); /* ex . os */ if (lua_isnil(L, -1)) return luaL_error(L, "os not loaded"); - luaL_openlib(L, "os", ex_oslib, 0); + copy_fields(L, ex_oslib, 1, -1); /* ex . os */ + + /* extend the io table */ + lua_getglobal(L, "io"); /* ex . io */ + if (lua_isnil(L, -1)) return luaL_error(L, "io not loaded"); + copy_fields(L, ex_iolib, 1, -1); /* ex . io */ + lua_getfield(L, 1, "pipe"); /* ex . io ex_pipe */ + lua_getfield(L, -2, "stderr"); /* ex . io ex_pipe io_stderr */ + lua_getfenv(L, -1); /* ex . io ex_pipe io_stderr E */ + lua_setfenv(L, -3); /* ex . io ex_pipe io_stderr */ /* extend the io.file metatable */ - luaL_getmetatable(L, LUA_FILEHANDLE); + luaL_getmetatable(L, LUA_FILEHANDLE); /* ex . F */ if (lua_isnil(L, -1)) return luaL_error(L, "can't find FILE* metatable"); - luaL_openlib(L, 0, ex_iofile_methods, 0); + copy_fields(L, ex_iofile_methods, 1, -1); /* ex . F */ /* diriter metatable */ - luaL_newmetatable(L, DIR_HANDLE); - luaL_openlib(L, 0, ex_diriter_methods, 0); + luaL_newmetatable(L, DIR_HANDLE); /* ex . D */ + luaL_register(L, 0, ex_diriter_methods); /* ex . D */ /* proc metatable */ - luaL_newmetatable(L, PROCESS_HANDLE); /* proc */ - luaL_openlib(L, 0, ex_process_methods, 0); /* proc */ - lua_pushliteral(L, "__index"); /* proc __index */ - lua_pushvalue(L, -2); /* proc __index proc */ - lua_settable(L, -3); /* proc */ - - /* Make all functions available via ex. namespace */ - luaL_openlib(L, "ex", ex_iolib, 0); - luaL_openlib(L, 0, ex_oslib, 0); - luaL_openlib(L, 0, ex_iofile_methods, 0); - luaL_openlib(L, 0, ex_process_methods, 0); + luaL_newmetatable(L, PROCESS_HANDLE); /* ex . P */ + copy_fields(L, ex_process_methods, 1, -1); /* ex . P */ + lua_pushliteral(L, "__index"); /* ex . P __index */ + lua_pushvalue(L, -2); /* ex . P __index P */ + lua_settable(L, -3); /* ex . P */ + lua_settop(L, 1); /* ex */ return 1; } diff --git a/w32api/spawn.c b/w32api/spawn.c index dda5e76..7932f5a 100755 --- a/w32api/spawn.c +++ b/w32api/spawn.c @@ -8,7 +8,10 @@ #include "spawn.h" -extern void *checkuserdata(lua_State *L, int index, const char *name); +#include +#define debug(...) fprintf(stderr, __VA_ARGS__) +#include "../lds.c" + extern HANDLE get_handle(FILE *f); extern int push_error(lua_State *L); @@ -17,33 +20,45 @@ 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); - } -} +struct spawn_params { + lua_State *L; + const char *cmdline; + const char *environment; + STARTUPINFO si; +}; -/* -- */ -void spawn_param_defaults(struct spawn_params *p) +struct spawn_params *spawn_param_init(lua_State *L) { + struct spawn_params *p = lua_newuserdata(L, sizeof *p); + p->L = L; + p->cmdline = 0; p->environment = 0; memset(&p->si, 0, sizeof p->si); - p->si.cb = sizeof p->si; + p->si.cb = 0; + p->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + p->si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + p->si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + return p; +} + +/* filename ... */ +void spawn_param_filename(struct spawn_params *p, const char *filename) +{ + p->cmdline = filename; + if (needs_quoting(p->cmdline)) { + lua_pushliteral(p->L, "\""); /* cmd ... q */ + lua_pushstring(p->L, p->cmdline); /* cmd ... q cmd */ + lua_pushvalue(p->L, -2); /* cmd ... q cmd q */ + lua_concat(p->L, 3); /* cmd ... "cmd" */ + p->cmdline = lua_tostring(p->L, -1); + } } /* cmd opts ... argtab -- cmd opts ... cmdline */ void spawn_param_args(struct spawn_params *p) { lua_State *L = p->L; + debug("spawn_param_args:"); debug_stack(L); luaL_Buffer args; luaL_buffinit(L, &args); size_t i, n = lua_objlen(L, -1); @@ -61,7 +76,7 @@ void spawn_param_args(struct spawn_params *p) } luaL_pushresult(&args); /* ... argtab argstr */ lua_pushvalue(L, 1); /* cmd opts ... argtab argstr cmd */ - lua_replace(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_replace(L, -2); /* cmd opts ... cmdline */ p->cmdline = lua_tostring(L, -1); @@ -99,53 +114,52 @@ void spawn_param_env(struct spawn_params *p) p->environment = lua_tostring(L, -1); } -/* _ opts ... */ -static int get_redirect(struct spawn_params *p, const char *stdname, HANDLE *ph) +void spawn_param_redirect(struct spawn_params *p, const char *stdname, FILE *f) { - 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); + HANDLE h = get_handle(f); + switch (stdname[3]) { + case 'i': p->si.hStdInput = h; break; + case 'o': p->si.hStdOutput = h; break; + case 'e': p->si.hStdError = h; break; } - lua_pop(p->L, 1); - return ret; + p->si.dwFlags |= STARTF_USESTDHANDLES; } -/* _ 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; -} +struct process { + int status; + HANDLE hProcess; + DWORD dwProcessId; +}; -int spawn_param_execute(struct spawn_params *p, struct process *proc) +int spawn_param_execute(struct spawn_params *p) { - char *c = strdup(p->cmdline); - char *e = (char *)p->environment; // strdup(p->environment); + lua_State *L = p->L; + struct process *proc; + char *c, *e; PROCESS_INFORMATION pi; + BOOL ret; + + proc = lua_newuserdata(L, sizeof *proc); /* cmd opts ... proc */ + luaL_getmetatable(L, PROCESS_HANDLE); /* cmd opts ... proc M */ + lua_setmetatable(L, -2); /* cmd opts ... proc */ proc->status = -1; + c = strdup(p->cmdline); + e = (char *)p->environment; /* strdup(p->environment); */ /* 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); + 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; - proc->dwProcessId = pi.dwProcessId; - } - return ret; + if (!ret) + return push_error(L); + proc->hProcess = pi.hProcess; + proc->dwProcessId = pi.dwProcessId; + return 1; } /* proc -- exitcode/nil error */ int process_wait(lua_State *L) { - struct process *p = checkuserdata(L, 1, PROCESS_HANDLE); + struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); if (p->status == -1) { DWORD exitcode; if (WAIT_FAILED == WaitForSingleObject(p->hProcess, INFINITE) @@ -160,7 +174,7 @@ int process_wait(lua_State *L) /* proc -- string */ int process_tostring(lua_State *L) { - struct process *p = checkuserdata(L, 1, PROCESS_HANDLE); + struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); char buf[40]; lua_pushlstring(L, buf, sprintf(buf, "process (%lu, %s)", (unsigned long)p->dwProcessId, diff --git a/w32api/spawn.h b/w32api/spawn.h index 309348c..0c484b7 100755 --- a/w32api/spawn.h +++ b/w32api/spawn.h @@ -4,26 +4,16 @@ #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; - DWORD dwProcessId; -}; +struct process; +struct spawn_params; -void spawn_param_filename(struct spawn_params *p); -void spawn_param_defaults(struct spawn_params *p); +struct spawn_params *spawn_param_init(lua_State *L); +void spawn_param_filename(struct spawn_params *p, const char *filename); 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); +void spawn_param_redirect(struct spawn_params *p, const char *stdname, FILE *f); +int spawn_param_execute(struct spawn_params *p); int process_wait(lua_State *L); int process_tostring(lua_State *L);