Implement shell builtins: cd, exec, umask (issue #15)

Add builtin dispatch in lcmd.c that checks __builtins table before
fork(), so cd/exec/umask operate on the shell process itself.
This commit is contained in:
Cormac Shannon
2026-03-04 19:20:42 +00:00
parent e8756d5d78
commit 4cc352cbec
7 changed files with 431 additions and 3 deletions

38
lcmd.c
View File

@@ -20,6 +20,7 @@
#include "lua.h"
#include "lauxlib.h"
#include "lcmd.h"
#include "lbuiltin.h"
/* ===== argv parser ===== */
@@ -555,6 +556,30 @@ static int exec_pipeline (lua_State *L, char **stages, int nstages,
}
/* ===== builtin dispatch ===== */
/*
** Check if argv[0] is a shell builtin. If so, call it and push the
** result table. Returns 1 if handled, 0 if not a builtin.
*/
static int try_builtin (lua_State *L, ParsedArgs *pa) {
int i;
if (lua_getglobal(L, "__builtins") != LUA_TTABLE) {
lua_pop(L, 1);
return 0;
}
if (lua_getfield(L, -1, pa->argv[0]) != LUA_TFUNCTION) {
lua_pop(L, 2);
return 0;
}
lua_remove(L, -2); /* remove __builtins, keep function */
for (i = 0; i < pa->argc; i++)
lua_pushstring(L, pa->argv[i]);
lua_call(L, pa->argc, 1);
return 1;
}
/* ===== luaB_command ===== */
int luaB_command (lua_State *L) {
@@ -599,6 +624,12 @@ int luaB_command (lua_State *L) {
return 1;
}
/* check for shell builtin */
if (try_builtin(L, &pa)) {
free_argv(&pa);
return 1;
}
/* create pipes */
if (pipe(out_pipe) != 0) {
free_argv(&pa);
@@ -731,6 +762,13 @@ int luaB_interactive (lua_State *L) {
return 0;
}
/* check for shell builtin */
if (try_builtin(L, &pa)) {
free_argv(&pa);
lua_setglobal(L, "_");
return 0;
}
/* ignore SIGINT, SIGQUIT, SIGPIPE in parent */
memset(&sa_new, 0, sizeof(sa_new));
sa_new.sa_handler = SIG_IGN;