diff --git a/Makefile b/Makefile index 426dfd2..37b6f14 100755 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ INCLUDES = -I$(LUA)/include LUA = /home/mark/src/lang/lua/lua-5.1-rc1 -mingw:; $(MAKE) "EX_LIB=ex.dll" +mingw:; $(MAKE) -C w32api ex.dll -cygwin:; $(MAKE) "EX_LIB=ex.so" "DEFINES=-D_XOPEN_SOURCE=600" +cygwin:; $(MAKE) -C posix ex.dll +#"EX_LIB=ex.so" "DEFINES=-D_XOPEN_SOURCE=600" diff --git a/posix/ex.c b/posix/ex.c index a5f4d71..7df891f 100755 --- a/posix/ex.c +++ b/posix/ex.c @@ -38,10 +38,9 @@ static int ex_getenv(lua_State *L) { const char *nam = luaL_checkstring(L, 1); char *val = getenv(nam); - if (val) - lua_pushstring(L, val); - else - lua_pushnil(L); + if (!val) + return push_error(L); + lua_pushstring(L, val); return 1; } @@ -189,22 +188,6 @@ static int ex_pipe(lua_State *L) } -/* ... argtab -- ... */ -static const char **build_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; -} - struct spawn_params { lua_State *L; const char *command, **argv, **envp; @@ -219,7 +202,7 @@ static void spawn_param_filename(struct spawn_params *p) 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); @@ -229,10 +212,34 @@ static void spawn_param_defaults(struct spawn_params *p) 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 = build_vector(p->L); + const char **argv = make_vector(p->L); if (!argv[0]) argv[0] = p->command; p->argv = argv; } @@ -261,19 +268,19 @@ static void spawn_param_env(struct spawn_params *p) lua_rawseti(p->L, -3, i); /* ... envtab arr[n]=estr k */ } /* ... envtab arr */ lua_replace(p->L, -2); /* ... arr */ - build_vector(p->L); /* ... arr */ + make_vector(p->L); /* ... arr */ } /* _ opts ... */ -static int get_redirect(lua_State *L, posix_spawn_file_actions_t *file_actions, - const char *stdname, int descriptor) +static int get_redirect(struct spawn_params *p, const char *stdname, int descriptor) { int ret; - lua_getfield(L, 2, stdname); - if ((ret = !lua_isnil(L, -1))) - posix_spawn_file_actions_adddup2(file_actions, - fileno(luaL_checkudata(L, -1, LUA_FILEHANDLE)), descriptor); - lua_pop(L, 1); + 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; } @@ -282,9 +289,9 @@ static void spawn_param_redirects(struct spawn_params *p) { posix_spawn_file_actions_init(&p->file_actions); p->has_actions = 1; - get_redirect(p->L, &p->file_actions, "stdin", STDIN_FILENO); - get_redirect(p->L, &p->file_actions, "stdout", STDOUT_FILENO); - get_redirect(p->L, &p->file_actions, "stderr", STDERR_FILENO); + get_redirect(p, "stdin", STDIN_FILENO); + get_redirect(p, "stdout", STDOUT_FILENO); + get_redirect(p, "stderr", STDERR_FILENO); } #define PROCESS_HANDLE "process" @@ -339,7 +346,7 @@ static int ex_spawn(lua_State *L) switch (lua_type(L, 2)) { default: luaL_argerror(L, 2, "expected options table"); break; case LUA_TNONE: - spawn_param_defaults(¶ms); + spawn_param_defaults(¶ms); /* cmd opts ... */ break; case LUA_TTABLE: lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ @@ -363,7 +370,8 @@ static int ex_spawn(lua_State *L) spawn_param_env(¶ms); /* cmd opts ... */ break; } - spawn_param_redirects(¶ms); + spawn_param_redirects(¶ms); /* cmd opts ... */ + break; } proc = lua_newuserdata(L, sizeof *proc); /* cmd opts ... proc */ luaL_getmetatable(L, PROCESS_HANDLE); /* cmd opts ... proc M */ @@ -376,7 +384,7 @@ static int ex_spawn(lua_State *L) /* proc -- exitcode/nil error */ static int process_wait(lua_State *L) { - struct process *p = luaL_checkudata(L, 1, PROCESS_HANDLE); + struct process *p = luaL_checkuserdata(L, 1, PROCESS_HANDLE); if (p->status == -1) { int status; if (-1 == waitpid(p->pid, &status, 0)) @@ -448,4 +456,3 @@ int luaopen_ex(lua_State *L) lua_pushboolean(L, 1); return 1; } - diff --git a/w32api/Makefile b/w32api/Makefile index 59fb85c..aa65a4b 100755 --- a/w32api/Makefile +++ b/w32api/Makefile @@ -5,7 +5,7 @@ INCLUDES = -I${LUA}/include WARNINGS = -W -Wall -Wno-missing-braces #LUA = /home/mark/src/lang/lua/lua-5.1-rc2 LUA = /home/mark/src/lang/lua/lua51 -LUALIBS = -L$(LUA)/lib -llua51 +LUALIBS = -L$(LUA)/bin/mingw -llua51 ex.dll: ex.o $(CC) $(TARGET_ARCH) -shared -o $@ ex.o $(LUALIBS) /c/windows/system32/msvcrt.dll diff --git a/w32api/ex.c b/w32api/ex.c index ff03835..14b5dcd 100755 --- a/w32api/ex.c +++ b/w32api/ex.c @@ -31,11 +31,8 @@ static int push_error(lua_State *L) size_t res = FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, 0, error, 0, buffer + len, sizeof buffer - len, 0); - if (res) { - len += res; - while (len > 0 && isspace(buffer[len - 1])) - len--; - } + if (res) /* trim spaces */ + for (len += res; len > 0 && isspace(buffer[len - 1]); len--) ; else len += sprintf(buffer + len, ""); lua_pushnil(L); @@ -53,10 +50,11 @@ static int ex_getenv(lua_State *L) len = GetEnvironmentVariable(nam, val, sizeof val); if (sizeof val < len) return push_error(L); - else if (len == 0) + else if (len == 0) { lua_pushnil(L); - else - lua_pushlstring(L, val, len); + return 1; + } + lua_pushlstring(L, val, len); return 1; } @@ -85,9 +83,8 @@ static int ex_unsetenv(lua_State *L) static int ex_environ(lua_State *L) { const char *nam, *val, *end; - const char *envs; - if (!(envs = GetEnvironmentStrings())) - return push_error(L); + const char *envs = GetEnvironmentStrings(); + if (!envs) return push_error(L); lua_newtable(L); for (nam = envs; *nam; nam = end + 1) { end = strchr(val = strchr(nam, '=') + 1, '\0'); @@ -132,9 +129,8 @@ static int ex_mkdir(lua_State *L) static int ex_currentdir(lua_State *L) { char pathname[PATH_MAX + 1]; - size_t len; - if (!(len = GetCurrentDirectory(sizeof pathname, pathname))) - return push_error(L); + size_t len = GetCurrentDirectory(sizeof pathname, pathname); + if (len == 0) return push_error(L); lua_pushlstring(L, pathname, len); return 1; } @@ -219,6 +215,7 @@ static int ex_pipe(lua_State *L) _fdopen(_open_osfhandle((long)ph[1], _O_WRONLY), "w")); } + struct spawn_params { lua_State *L; const char *cmdline; @@ -242,53 +239,57 @@ static void spawn_param_filename(struct spawn_params *p) 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_default(struct spawn_params *p) +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; } -/* {arg1,arg 2} => " arg1 \"arg2\"" */ -static const char *concat_args(lua_State *L) +/* 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 */ + 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 */ + lua_pop(L, 1); /* ... argtab */ } - lua_pop(L, 1); /* ... */ - luaL_pushresult(&args); /* ... argstr */ - return lua_tostring(L, -1); -} - -/* cmd opts ... argtab */ -static void spawn_param_args(struct spawn_params *p) -{ - concat_args(p->L); /* cmd opts ... argstr */ - lua_pushvalue(p->L, 1); /* cmd opts ... argstr cmd */ - lua_replace(p->L, -2); /* cmd opts ... cmd argstr */ - lua_concat(p->L, 2); /* cmd opts ... cmdline */ - p->cmdline = lua_tostring(p->L, -1); + 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); } -/* {nam1=val1,nam2=val2} => "nam1=val1\0nam2=val2\0" */ -static const char *concat_env(lua_State *L) +/* ... 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 */ @@ -304,46 +305,34 @@ static const char *concat_env(lua_State *L) lua_pop(L, 1); /* ... envtab k */ } luaL_putchar(&env, '\0'); - lua_pop(L, 1); /* ... */ - luaL_pushresult(&env); /* ... envstr */ - return lua_tostring(L, -1); -} - -/* ... envtab */ -static void spawn_param_env(struct spawn_params *p) -{ - if (lua_isnil(p->L, -1)) { - p->environment = 0; - lua_pop(p->L, 1); - } - else { - p->environment = concat_env(p->L); - } + luaL_pushresult(&env); /* ... envtab envstr */ + lua_replace(L, -2); /* ... envtab envstr */ + p->environment = lua_tostring(L, -1); } /* _ opts ... */ -static int get_redirect(lua_State *L, const char *stdname, HANDLE *ph) +static int get_redirect(struct spawn_params *p, const char *stdname, HANDLE *ph) { int ret; - lua_getfield(L, 2, stdname); - if ((ret = !lua_isnil(L, -1))) { + lua_getfield(p->L, 2, stdname); + if ((ret = !lua_isnil(p->L, -1))) { /* XXX checkuserdata is confusing here */ - FILE **pf = luaL_checkuserdata(L, -1, LUA_FILEHANDLE); + FILE **pf = luaL_checkuserdata(p->L, -1, LUA_FILEHANDLE); *ph = get_handle(*pf); } - lua_pop(L, 1); + lua_pop(p->L, 1); return ret; } /* _ opts ... */ -static void spawn_param_redirect(struct spawn_params *p) +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->L, "stdin", &p->si.hStdInput) - | get_redirect(p->L, "stdout", &p->si.hStdOutput) - | get_redirect(p->L, "stderr", &p->si.hStdError)) + 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; } @@ -396,14 +385,14 @@ static int ex_spawn(lua_State *L) } } - /* get command */ + /* get filename to execute */ spawn_param_filename(¶ms); /* get arguments, environment, and redirections */ switch (lua_type(L, 2)) { default: luaL_argerror(L, 2, "expected options table"); break; case LUA_TNONE: - spawn_param_default(¶ms); + spawn_param_defaults(¶ms); /* cmd opts ... */ break; case LUA_TTABLE: lua_getfield(L, 2, "args"); /* cmd opts ... argtab */ @@ -416,7 +405,7 @@ static int ex_spawn(lua_State *L) case LUA_TTABLE: if (lua_objlen(L, 2) > 0) luaL_error(L, "cannot specify both the args option and array values"); - spawn_param_args(¶ms); + spawn_param_args(¶ms); /* cmd opts ... */ break; } lua_getfield(L, 2, "env"); /* cmd opts ... envtab */ @@ -424,10 +413,10 @@ static int ex_spawn(lua_State *L) default: luaL_error(L, "env option must be a table"); break; case LUA_TNIL: case LUA_TTABLE: - spawn_param_env(¶ms); + spawn_param_env(¶ms); /* cmd opts ... */ break; } - spawn_param_redirect(¶ms); + spawn_param_redirects(¶ms); /* cmd opts ... */ break; } proc = lua_newuserdata(L, sizeof *proc); /* cmd opts ... proc */ @@ -435,7 +424,7 @@ static int ex_spawn(lua_State *L) lua_setmetatable(L, -2); /* cmd opts ... proc */ if (!spawn_param_execute(¶ms, proc)) return push_error(L); - return 1; /* ... proc */ + return 1; /* ... proc */ } /* proc -- exitcode/nil error */ @@ -449,7 +438,7 @@ static int process_wait(lua_State *L) p->status = exitcode; } lua_pushnumber(L, p->status); - return 1; /* exitcode */ + return 1; }