/* ** $Id: lbuiltin.c $ ** Shell builtins (cd, exec, umask) ** See Copyright Notice in lua.h */ #define lbuiltin_c #define LUA_LIB #include #include #include #include #include #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"); /* Store as __lush.builtins in registry */ lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_LUSH); lua_insert(L, -2); lua_setfield(L, -2, "builtins"); lua_pop(L, 1); }