Add user-defined commands via lush.commands table (issue #30)
User commands run in forked children like external commands, so they support piping, redirection, and capture seamlessly.
This commit is contained in:
57
lcmd.c
57
lcmd.c
@@ -607,6 +607,57 @@ static void read_pipes (int fd_out, int fd_err,
|
||||
}
|
||||
|
||||
|
||||
/* ===== user command dispatch (runs in forked child) ===== */
|
||||
|
||||
/*
|
||||
** Look up argv[0] in lush.commands. If found, call the function with
|
||||
** all argv strings, extract an exit code, and _exit(). This must only
|
||||
** be called inside a fork()ed child process.
|
||||
** Returns 0 if not a user command (caller should fall through to execvp).
|
||||
*/
|
||||
static int exec_user_command (lua_State *L, ParsedArgs *pa) {
|
||||
int i, code = 0;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_LUSH);
|
||||
if (!lua_istable(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return 0;
|
||||
}
|
||||
if (lua_getfield(L, -1, "commands") != LUA_TTABLE) {
|
||||
lua_pop(L, 2);
|
||||
return 0;
|
||||
}
|
||||
if (lua_getfield(L, -1, pa->argv[0]) != LUA_TFUNCTION) {
|
||||
lua_pop(L, 3);
|
||||
return 0;
|
||||
}
|
||||
lua_remove(L, -2); /* remove commands table */
|
||||
lua_remove(L, -2); /* remove lush table */
|
||||
for (i = 0; i < pa->argc; i++)
|
||||
lua_pushstring(L, pa->argv[i]);
|
||||
if (lua_pcall(L, pa->argc, 1, 0) != LUA_OK) {
|
||||
/* write error to stderr and exit with failure */
|
||||
const char *err = lua_tostring(L, -1);
|
||||
if (err) {
|
||||
(void)write(STDERR_FILENO, err, strlen(err));
|
||||
(void)write(STDERR_FILENO, "\n", 1);
|
||||
}
|
||||
_exit(1);
|
||||
}
|
||||
/* extract exit code from return value */
|
||||
if (lua_isinteger(L, -1)) {
|
||||
code = (int)lua_tointeger(L, -1);
|
||||
} else if (lua_istable(L, -1)) {
|
||||
if (lua_getfield(L, -1, "code") == LUA_TNUMBER)
|
||||
code = (int)lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
_exit(code);
|
||||
return 1; /* unreachable */
|
||||
}
|
||||
|
||||
|
||||
/* ===== pipeline execution ===== */
|
||||
|
||||
/*
|
||||
@@ -797,6 +848,7 @@ static int exec_pipeline (lua_State *L, char **stages, int nstages,
|
||||
close(err_pipe[0]); close(err_pipe[1]);
|
||||
}
|
||||
|
||||
exec_user_command(L, &pa[i]);
|
||||
execvp(pa[i].argv[0], pa[i].argv);
|
||||
|
||||
/* exec failed */
|
||||
@@ -1050,6 +1102,7 @@ int lushCmd_command (lua_State *L) {
|
||||
sa_new.sa_handler = SIG_DFL;
|
||||
sigaction(SIGPIPE, &sa_new, NULL);
|
||||
|
||||
exec_user_command(L, &pa);
|
||||
execvp(pa.argv[0], pa.argv);
|
||||
|
||||
/* exec failed — write error to stderr and exit */
|
||||
@@ -1201,6 +1254,7 @@ int lushCmd_interactive (lua_State *L) {
|
||||
sigaction(SIGINT, &sa_new, NULL);
|
||||
sigaction(SIGQUIT, &sa_new, NULL);
|
||||
|
||||
exec_user_command(L, &pa);
|
||||
execvp(pa.argv[0], pa.argv);
|
||||
|
||||
/* exec failed */
|
||||
@@ -1287,6 +1341,9 @@ LUAMOD_API int luaopen_lush (lua_State *L) {
|
||||
/* create aliases subtable */
|
||||
lua_createtable(L, 0, 4);
|
||||
lua_setfield(L, -2, "aliases");
|
||||
/* create commands subtable for user-defined commands */
|
||||
lua_createtable(L, 0, 4);
|
||||
lua_setfield(L, -2, "commands");
|
||||
/* intern function name strings for OP_LUSH VM access */
|
||||
lushname[LUSH_OP_COMMAND] = luaS_new(L, "command");
|
||||
lushname[LUSH_OP_INTERACTIVE] = luaS_new(L, "interactive");
|
||||
|
||||
Reference in New Issue
Block a user