Add builtin dispatch in lcmd.c that checks __builtins table before fork(), so cd/exec/umask operate on the shell process itself.
2.5 KiB
15 — Shell builtins
Status: done
Some commands must run inside the shell process itself to have any effect. An external cd binary exists on most systems but it spawns a subprocess, changes directory there, and exits — leaving lush's own working directory unchanged.
Lush only needs builtins for operations that cannot be implemented in pure Lua or via external binaries — things that require modifying the shell process state via syscalls with no Lua API.
Assessment
Every builtin from man 1 builtin was evaluated against two questions:
- Can it be done in Lua? (e.g.
export→$VAR = "val",source→dofile(),exit→os.exit()) - Does the external binary work? (e.g.
/bin/pwd,/usr/bin/which,/usr/bin/kill)
If either answer is yes, it's not needed as a builtin. What remains:
| Builtin | Why it must be a builtin |
|---|---|
cd |
chdir(2) must happen in the lush process. No Lua API. |
exec |
execvp(2) replaces the current process. os.execute() spawns a subprocess. No Lua equivalent. |
umask |
umask(2) is per-process. The external /usr/bin/umask is a shell script that calls the shell's own builtin. No Lua API. |
Builtins
cd [dir]
Change working directory via chdir(2).
- No argument:
cd $HOME cd -: change to$OLDPWD- Updates
$PWDand$OLDPWDenvironment variables - Errors if directory doesn't exist or isn't accessible
exec command [args...]
Replace the shell process with the given command via execvp(2). Does not return on success. On failure, prints an error and the shell continues.
umask [mode]
Get or set the file creation mask via umask(2).
- No argument: print current umask (octal)
- With argument: set umask to the given octal value
Design considerations
- Dispatch order: When a command is entered (backtick or
!prefix), check builtins before searching$PATH. A builtin namedcdmust intercept the command before it reachesexecvp. - Implementation: Expose the syscalls to Lua (e.g.
os.chdir(),os.exec(),os.umask()), then implement the builtins as Lua functions registered at startup. This keeps the C side minimal and lets users compose with the primitives directly. - Interaction with backticks:
`cd /tmp`and!cd /tmpshould both trigger the builtin, not spawn an external process.
Open questions
- Should builtins be overridable by the user?
- Should there be a
builtincommand to bypass user overrides?