Add builtin dispatch in lcmd.c that checks __builtins table before fork(), so cd/exec/umask operate on the shell process itself.
160 lines
3.7 KiB
C
160 lines
3.7 KiB
C
/*
|
|
** $Id: lbuiltin.c $
|
|
** Shell builtins (cd, exec, umask)
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
#define lbuiltin_c
|
|
#define LUA_LIB
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "lua.h"
|
|
#include "lauxlib.h"
|
|
#include "lbuiltin.h"
|
|
|
|
|
|
static void push_builtin_result (lua_State *L, int code,
|
|
const char *out, const char *err) {
|
|
lua_createtable(L, 0, 3);
|
|
lua_pushinteger(L, code);
|
|
lua_setfield(L, -2, "code");
|
|
lua_pushstring(L, out ? out : "");
|
|
lua_setfield(L, -2, "stdout");
|
|
lua_pushstring(L, err ? err : "");
|
|
lua_setfield(L, -2, "stderr");
|
|
}
|
|
|
|
|
|
/*
|
|
** builtin_cd(cmd, [dir])
|
|
** Change working directory. No arg → $HOME, "-" → $OLDPWD.
|
|
** Updates $PWD and $OLDPWD.
|
|
*/
|
|
static int builtin_cd (lua_State *L) {
|
|
const char *dir;
|
|
char oldpwd[4096];
|
|
char newpwd[4096];
|
|
|
|
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
|
|
/* no dir argument: use $HOME */
|
|
dir = getenv("HOME");
|
|
if (dir == NULL) {
|
|
push_builtin_result(L, 1, NULL, "cd: HOME not set");
|
|
return 1;
|
|
}
|
|
}
|
|
else {
|
|
dir = luaL_checkstring(L, 2);
|
|
if (strcmp(dir, "-") == 0) {
|
|
dir = getenv("OLDPWD");
|
|
if (dir == NULL) {
|
|
push_builtin_result(L, 1, NULL, "cd: OLDPWD not set");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* save current directory */
|
|
if (getcwd(oldpwd, sizeof(oldpwd)) == NULL)
|
|
oldpwd[0] = '\0';
|
|
|
|
if (chdir(dir) != 0) {
|
|
char errbuf[4200];
|
|
snprintf(errbuf, sizeof(errbuf), "cd: %s: %s", dir, strerror(errno));
|
|
push_builtin_result(L, 1, NULL, errbuf);
|
|
return 1;
|
|
}
|
|
|
|
/* get resolved path */
|
|
if (getcwd(newpwd, sizeof(newpwd)) == NULL)
|
|
newpwd[0] = '\0';
|
|
|
|
setenv("OLDPWD", oldpwd, 1);
|
|
setenv("PWD", newpwd, 1);
|
|
|
|
push_builtin_result(L, 0, NULL, NULL);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** builtin_exec(cmd, program, [args...])
|
|
** Replace the shell process via execvp(2).
|
|
*/
|
|
static int builtin_exec (lua_State *L) {
|
|
int nargs = lua_gettop(L);
|
|
int i;
|
|
char **argv;
|
|
char errbuf[4200];
|
|
|
|
if (nargs < 2) {
|
|
push_builtin_result(L, 1, NULL, "exec: command required");
|
|
return 1;
|
|
}
|
|
|
|
/* build argv from args 2..nargs (skip "exec" at arg 1) */
|
|
argv = (char **)malloc((size_t)(nargs) * sizeof(char *));
|
|
if (argv == NULL)
|
|
return luaL_error(L, "out of memory");
|
|
|
|
for (i = 2; i <= nargs; i++)
|
|
argv[i - 2] = (char *)luaL_checkstring(L, i);
|
|
argv[nargs - 1] = NULL;
|
|
|
|
execvp(argv[0], argv);
|
|
|
|
/* execvp only returns on failure */
|
|
snprintf(errbuf, sizeof(errbuf), "exec: %s: %s", argv[0], strerror(errno));
|
|
free(argv);
|
|
push_builtin_result(L, 1, NULL, errbuf);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** builtin_umask(cmd, [mode])
|
|
** No arg: query and print current umask. With arg: set umask from octal string.
|
|
*/
|
|
static int builtin_umask (lua_State *L) {
|
|
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
|
|
/* query mode */
|
|
mode_t old = umask(0);
|
|
char buf[16];
|
|
umask(old); /* restore */
|
|
snprintf(buf, sizeof(buf), "%04o\n", (unsigned)old);
|
|
push_builtin_result(L, 0, buf, NULL);
|
|
return 1;
|
|
}
|
|
else {
|
|
const char *modestr = luaL_checkstring(L, 2);
|
|
char *endp;
|
|
unsigned long val;
|
|
errno = 0;
|
|
val = strtoul(modestr, &endp, 8);
|
|
if (errno != 0 || *endp != '\0' || endp == modestr || val > 0777) {
|
|
push_builtin_result(L, 1, NULL, "umask: invalid mode");
|
|
return 1;
|
|
}
|
|
umask((mode_t)val);
|
|
push_builtin_result(L, 0, NULL, NULL);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
void luaopen_builtins (lua_State *L) {
|
|
lua_createtable(L, 0, 3);
|
|
lua_pushcfunction(L, builtin_cd);
|
|
lua_setfield(L, -2, "cd");
|
|
lua_pushcfunction(L, builtin_exec);
|
|
lua_setfield(L, -2, "exec");
|
|
lua_pushcfunction(L, builtin_umask);
|
|
lua_setfield(L, -2, "umask");
|
|
lua_setglobal(L, "__builtins");
|
|
}
|