Hide shell functions from _G using upvalues on the main chunk

Store __command, __interactive, __getenv, __setenv as upvalues
populated by lua_load() from the registry, keeping them invisible
to user code while accessible to the parser's codegen.
This commit is contained in:
Cormac Shannon
2026-03-12 22:46:56 +00:00
parent 34bfabccbd
commit f88b17959f
8 changed files with 134 additions and 77 deletions

47
lcmd.c
View File

@@ -874,15 +874,21 @@ static int exec_pipeline (lua_State *L, char **stages, int nstages,
*/
static int try_builtin (lua_State *L, ParsedArgs *pa) {
int i;
if (lua_getglobal(L, "__builtins") != LUA_TTABLE) {
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_LUSH);
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 0;
}
if (lua_getfield(L, -1, pa->argv[0]) != LUA_TFUNCTION) {
if (lua_getfield(L, -1, "builtins") != LUA_TTABLE) {
lua_pop(L, 2);
return 0;
}
lua_remove(L, -2); /* remove __builtins, keep function */
if (lua_getfield(L, -1, pa->argv[0]) != LUA_TFUNCTION) {
lua_pop(L, 3);
return 0;
}
lua_remove(L, -2); /* remove builtins table */
lua_remove(L, -2); /* remove lush table */
for (i = 0; i < pa->argc; i++)
lua_pushstring(L, pa->argv[i]);
lua_call(L, pa->argc, 1);
@@ -890,9 +896,9 @@ static int try_builtin (lua_State *L, ParsedArgs *pa) {
}
/* ===== luaB_command ===== */
/* ===== lushCmd_command ===== */
int luaB_command (lua_State *L) {
int lushCmd_command (lua_State *L) {
const char *cmd = luaL_checkstring(L, 1);
char *stages[MAX_PIPELINE_STAGES];
int nstages;
@@ -1033,9 +1039,9 @@ int luaB_command (lua_State *L) {
}
/* ===== luaB_interactive ===== */
/* ===== lushCmd_interactive ===== */
int luaB_interactive (lua_State *L) {
int lushCmd_interactive (lua_State *L) {
const char *cmd = luaL_checkstring(L, 1);
char *stages[MAX_PIPELINE_STAGES];
int nstages;
@@ -1142,3 +1148,30 @@ int luaB_interactive (lua_State *L) {
return 0; /* void — no Lua return values */
}
/* ===== lushCmd_getenv ===== */
int lushCmd_getenv (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
const char *val = getenv(name);
if (val == NULL)
lua_pushnil(L);
else
lua_pushstring(L, val);
return 1;
}
/* ===== lushCmd_setenv ===== */
int lushCmd_setenv (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
if (lua_isnoneornil(L, 2))
unsetenv(name);
else {
const char *val = luaL_tolstring(L, 2, NULL);
setenv(name, val, 1);
}
return 0;
}