Skip to main content

Module shell_exec

Module shell_exec 

Source
Expand description

exec submodule. Faithful Rust ports of free functions and file-static globals from Src/exec.c. The wordcode-VM dispatch tree (execlist / execpline / execcmd / execsimple etc.) that drives execution in C zsh is NOT replicated here — zshrs runs the fusevm bytecode VM instead (see src/vm_helper.rs + src/fusevm_bridge.rs).

What lives here are the parts of Src/exec.c that ARE faithful ports and don’t depend on the C-side wordcode walker:

  • trap_state / trap_return / forklevel — file-static integer globals from Src/exec.c:134 / :155 / :1052, exposed as atomics shared between this module, Src/signals.c’s port at src/ported/signals.rs, and Src/params.c’s port at src/ported/params.rs.
  • gethere (Src/exec.c:4573) — turn a here-document into a here-string. Called from the lexer port (src/ported/lex.rs).
  • getoutput (Src/exec.c:4712) — command-substitution body runner. Called from the parameter-expansion port (src/ported/subst.rs).
  • loadautofn + getfpfunc (Src/exec.c:5050 / :5260) — $fpath walker + autoload file installer. Called from bin_autoload / bin_functions -c in src/ported/builtin.rs.
  • resolvebuiltin (Src/exec.c:2703) — module-autoload guard used by the dispatch walk in execcmd_exec.
  • execcmd_compile_head — fusevm-bytecode-time head resolver mirroring the head section (c:2904-3275) of C’s execcmd_exec. NOT a faithful port; the canonical 7-arg execcmd_exec port lives alongside it.
  • execcmd_exec (Src/exec.c:2900) — canonical 7-arg port of the C function (locals + dispatch walk through builtin/shfunc/external invocation). Used by future tree-walker callers; the fusevm bytecode flow goes through execcmd_compile_head instead.

Modules§

esub
Port of enum { ESUB_ASYNC, ESUB_PGRP, ... }; from Src/exec.c:1056. Flag bits for entersubsh(int flags, struct entersubsh_ret *retp).

Structs§

entersubsh_ret
Port of struct entersubsh_ret from Src/exec.c (forward decl). Out-arg used by entersubsh() to hand back the group-leader pid and the list-pipe job index the parent should track. Only filled in for ESUB_PGRP + non-async forks (synchronous pipeline child groups).
execcmd_dispatch
Dispatch decision returned by execcmd_compile_head — the fusevm-bytecode-time head resolver that mirrors the local-variable state the C execcmd_exec function carries through c:2913-2916 (is_builtin, is_shfunc, cflags, use_defpath) plus the precmd-modifier strip count. The fusevm bytecode compiler reads this to emit the correct dispatch opcode in src/extensions/compile_zsh.rs::compile_simple.

Constants§

ADDVAR_EXPORT
Port of the anonymous enum { ... } from Src/exec.c:35-40. Flag bits passed as the addflags argument to addvars / addvarsfromargs:
ADDVAR_RESTORE
ADDVAR_RESTORE constant.
ANONYMOUS_FUNCTION_NAME
Port of static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)"; from Src/exec.c:5289. Anonymous-function name marker used by is_anonymous_function_name, execfuncdef, and doshfunc for () { ... } anonymous function dispatch.
POUNDBANGLIMIT
POUNDBANGLIMIT from Src/exec.c:500 — max bytes read from the front of a script when probing for a #! shebang line.

Statics§

DONETRAP
Port of static int donetrap; from Src/exec.c:1351. Tracks whether the ZERR trap has already fired for the current sublist. C source resets to 0 at sublist start (c:1455) and sets to 1 after dotrap(SIGZERR) (c:1602). The check if (!this_noerrexit && !donetrap && !this_donetrap) at c:1598 suppresses re-firing within the same sublist AND, crucially, carries the “already fired” state across a function-call return boundary so the outer caller’s post-command check doesn’t fire ZERR a second time for the same logical error. Bug #303 in docs/BUGS.md.
FORKLEVEL
Port of int forklevel; from Src/exec.c:1052. Records the locallevel at the most recent fork point (set at c:1221: forklevel = locallevel; inside entersubsh()). Used by:
LIST_PIPE_TEXT
Port of mod_export int noerrs; from Src/exec.c:117. When non-zero, suppress zerr() output (lex error reporting during parse_string, parseopts etc.). Saved/restored by execsave/execrestore. Port of static char list_pipe_text[JOBTEXTSIZE] from Src/exec.c:463. Holds the textual rendering of the in-flight pipe list; saved across nested execlist invocations at exec.c:1372-1380 (zeroed on entry, restored from old_list_pipe_text at c:1634-1638) and round-tripped through execsave/execrestore (c:6448 / c:6484). zshrs models it as a length-bounded String guarded by a Mutex — the C char[80] cap is a buffer-overflow guard, but matching length matters for the jobs builtin’s pipe-list rendering.
STTYval
Port of static char *STTYval; from Src/exec.c:263. Pending stty argument string captured by addvars when the command’s inline env contains STTY=.... Applied by execute before fork
TRAP_RETURN
Port of int trap_return; from Src/exec.c:155. Carries the pending exit status from inside a trap; sentinel -2 means “running an EXIT/DEBUG-style trap at the current level” (signals.c:1166). Promoted to the user’s return N value by bin_return when POSIX-trap semantics apply (builtin.c:5852).
TRAP_STATE
Port of int trap_state; from Src/exec.c:134. Tracks whether a trap handler is currently being processed and, paired with TRAP_RETURN below, whether a return inside the trap should promote to TRAP_STATE_FORCE_RETURN to unwind the trap caller.
cmdoutpid
Port of pid_t cmdoutpid; from Src/exec.c:215. Pid of the most recent $(cmd) command-substitution child. Used by exit-status propagation: cmdoutval carries the exit; cmdoutpid carries the pid waitpid-d for it.
cmdoutval
Port of int cmdoutval; from Src/exec.c:225. Exit status of the most recent $(cmd). Drives $? when a varspc-only command runs alongside a substitution.
doneps4
Port of static int doneps4; from Src/exec.c:262. Set after printprompt4 has emitted the $PS4 prefix for the current xtrace command — prevents double-printing when an inner sub-eval also wants to xtrace.
esglob
esprefork
Port of static int esprefork, esglob = 1; from Src/exec.c:2680.
exstack
Port of struct execstack *exstack; from Src/exec.c:244. Head of the linked exec-context save stack — execsave pushes a frame before signal-handler / trap dispatch; execrestore pops it afterwards so the interrupted command resumes with its state intact.
list_pipe
Port of int list_pipe = 0; from Src/exec.c:457. Set when the currently-executing pipeline is the long-running pipe-into-loop shape (cat foo | while read a; do ... done) — drives the super/sub-job tracking documented in the famous Allen Edeln… comment block above this declaration in C.
list_pipe_child
Port of static int list_pipe_child = 0; from Src/exec.c:462. Set in the child after the list_pipe fork so the child knows to continue executing the loop body (vs the parent which records the pid + returns).
list_pipe_job
Port of static int list_pipe_job; from Src/exec.c:462. Job table index of the pipeline’s first-stage job (the cat in cat foo | while ...).
list_pipe_pid
Port of static pid_t list_pipe_pid; from Src/exec.c:459. PID of the sub-shell created to host the loop-after-pipe pattern; passed up the recursive execlist stack so the cat-job’s super- job entry can record it.
noerrexit
Port of int noerrexit; from Src/exec.c:72. Bit-flags that suppress ERREXIT triggering on the next command(s). Bits: NOERREXIT_EXIT (in if/while/until test contexts), NOERREXIT_RETURN (after return), NOERREXIT_UNTIL_EXEC (until next exec’d command). Bucket-1 — per-evaluator (each recursive eval has its own suppression frame).
noerrs
nohistsave
Port of int nohistsave; from Src/exec.c:122. When non-zero, addhistnode no-ops so trap firings / eval invocations don’t pollute $HISTCMD. Tracked alongside noerrs in the trap path.
nowait
Port of static int nowait; from Src/exec.c:461. When set, execpline doesn’t wait for the pipeline; used during the list_pipe sub-shell fork bookkeeping.
pline_level
Port of int pline_level = 0; from Src/exec.c:461. Recursive pipeline depth (counts nested pipelines within the current execlist call chain).
procsubstpid
Port of mod_export pid_t procsubstpid; from Src/exec.c:220. Pid of the most recent process-substitution child (<(cmd) / >(cmd)). Tracked separately from cmdoutpid because procsubst jobs aren’t wait-collected by the parent until the fd is closed.
retflag
Port of mod_export volatile int retflag; from Src/exec.c:165. Set by bin_return to unwind the function-call stack. Cleared by runshfunc on entry, checked by execlist’s main loop.
sfcontext
Port of mod_export int sfcontext; from Src/exec.c:239. Source context — one of SFC_NONE, SFC_DIRECT (user typed it), SFC_SIGNAL (trap firing), SFC_HOOK (precmd/preexec etc.), SFC_WIDGET (ZLE widget), SFC_COMPLETE (completion fn), SFC_CFUNC (compsys fn), SFC_SUBST ($(…) cmd-subst), SFC_EVAL (eval body). Read by zerr() / funcstack building.
simple_pline
Port of int simple_pline = 0; from Src/exec.c:457. Set during dispatch of a “simple” pipeline (single-stage / no shell-construct tail) so the list_pipe machinery short-circuits.
subsh
Port of int subsh; from Src/exec.c:160. Subshell depth — bumped every time entersubsh forks a sub-shell, used by signal handling (different SIGINT semantics in subshells) and by ${$$} ($$ stays at the top-level pid).
this_noerrexit
Port of int this_noerrexit; from Src/exec.c:109. When set, suppress ERREXIT for THIS one command only (consumed + cleared before the next command starts). Set by execcursh and the ((expr)) arith path so a 0-result doesn’t trigger errexit.
use_cmdoutval
Port of int use_cmdoutval; from Src/exec.c:234. When set, lastval is updated from cmdoutval after the command (i.e. the command had substitutions whose exit status matters).
zsh_eval_context
Port of char **zsh_eval_context; from Src/exec.c (zsh.export:355). Stack of "context" labels used by eval-style nested execution: bin_dot, bin_eval, execode, autoloads. Each execode(prog, ..., "context") pushes its label and pops on return.
zsh_subshell
Port of mod_export int zsh_subshell; from Src/init.c:67. Visible $ZSH_SUBSHELL parameter — incremented by entersubsh() each time the shell forks into a subshell (real or fake-exec). Distinct from subsh which records whether we ARE a subshell; zsh_subshell is the visible depth count.

Functions§

addfd
Port of static void addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag, char *varid) from Src/exec.c:2397.
cancd
Port of char *cancd(char *s) from Src/exec.c:6370.
cancd2
Port of static int cancd2(char *s) from Src/exec.c:6411.
checkclobberparam
Port of static int checkclobberparam(struct redir *f) from Src/exec.c:2178.
clobber_open
Port of static int clobber_open(struct redir *f) from Src/exec.c:2221.
closeallelse
Port of static void closeallelse(struct multio *mn) from Src/exec.c:2358.
closem
Port of mod_export void closem(int how, int all) from Src/exec.c:4546.
closemn
Port of static void closemn(struct multio **mfds, int fd, int type) from Src/exec.c:2273.
closemnodes
Port of static void closemnodes(struct multio **mfds) from Src/exec.c:2344.
commandnotfound
Port of int commandnotfound(char *arg0, LinkList args) from Src/exec.c:669.
doshfunc
Port of int doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) from Src/exec.c:5823-6158.
entersubsh
Port of static void entersubsh(int flags, struct entersubsh_ret *retp) from Src/exec.c:1083. Called by every child fork to switch the process into subshell mode: traps reset, monitor disabled, signals re-defaulted, pgrp + tty handed off, saved fds closed, jobtab cleared, ZSH_SUBSHELL bumped, forklevel = locallevel.
execarith
Port of execarith(Estate state, UNUSED(int do_exec)) from Src/exec.c:5237-5275. Run a (( ... )) arithmetic command; returns 0 when val != 0 (success), 1 when val == 0 (false), 2 on parse error.
execautofn
Port of static int execautofn(Estate state, UNUSED(int do_exec)) from Src/exec.c:5635-5644. The autoload-aware dispatch entry for WC_AUTOFN: fault the function body in via loadautofn, then hand off to execautofn_basic to actually run it.
execautofn_basic
Port of execautofn_basic(Estate state, UNUSED(int do_exec)) from Src/exec.c:5608-5630. Run a pre-loaded autoload function body via execode, snapshotting scriptname/scriptfilename around the call so %N / %x reflect the autoload target during execution.
execcase
Port of execcase(Estate state, int do_exec) from Src/loop.c:600-733. case word in pat) body ;; ... esac with ;;/;&/;| separators.
execcmd_analyse
Port of execcmd_analyse(Estate state, Execcmd_params eparams) from Src/exec.c:2733-2785. Pre-execcmd_exec analysis pass: walks the wordcode at state->pc, splits out redirs/varspc/args without expanding (no prefork, no globbing), and fills eparams so the caller (execcmd_exec at c:2901 or execpline2 at c:2013) can branch on the command type before the real work.
execcmd_compile_head
!!! NOT A PORT OF C execcmd_exec !!!
execcmd_exec
Port of execcmd_exec(Estate state, Execcmd_params eparams, int input, int output, int how, int last1, int close_if_forked) from Src/exec.c:2900-4404. Execute a command at the lowest level of the hierarchy.
execcmd_fork
Port of execcmd_fork(Estate state, int how, int type, Wordcode varspc, LinkList *filelistp, char *text, int oautocont, int close_if_forked) from Src/exec.c:2810-2893.
execcmd_getargs
Port of execcmd_getargs(LinkList preargs, LinkList args, int expand) from Src/exec.c:2791-2806. Transfer the first node of args to preargs, performing prefork (singleton-list expansion) on the way if expand is set. Used by execcmd_exec to pull the command head one word at a time so prefix-modifier walking (BINF_COMMAND, BINF_EXEC etc.) sees expanded names.
execcond
Port of execcond(Estate state, UNUSED(int do_exec)) from Src/exec.c:5204-5232. Run a [[ ... ]] cond expression.
execcursh
Port of execcursh(Estate state, int do_exec) from Src/exec.c:469-498. Execute a { ... } current-shell command group: skip the trailing try-only word, optionally drop a stale job slot, then run the inner list.
execfor
Port of execfor(Estate state, int do_exec) from Src/loop.c:50-202. for var in args; do body; done and the C-style for ((init;cond;adv)) variant. WC_FOR_TYPE distinguishes PPARAM (use $@) / LIST (explicit words) / COND (C-style).
execfuncdef
Port of execfuncdef(Estate state, Eprog redir_prog) from Src/exec.c:5309-5494. Define a shell function: extract name(s)+body from the wordcode payload, allocate the Shfunc, install into shfunctab (named), or execute immediately (anon).
execif
Port of execif(Estate state, int do_exec) from Src/loop.c:553-598. if cond; then body; elif ...; else ...; fi.
execlist
Port of execlist(Estate state, int dont_change_job, int exiting) from Src/exec.c:1349-1665. Walks WC_LIST entries, dispatches each sublist (WC_SUBLIST chain inlined per c:1525-1625, same as C — there’s no separate execsublist function), handles signal-trap dispatch + ERREXIT propagation.
execode
Port of void execode(Eprog p, int dont_change_job, int exiting, char *context) from Src/exec.c:1245-1282. Set up an estate around the given Eprog and run execlist. Maintains the zsh_eval_context stack so $ZSH_EVAL_CONTEXT reflects the call chain.
execpline
Port of execpline(Estate state, wordcode slcode, int how, int last1) from Src/exec.c:1668-1942. Walks the WC_PIPE chain, sets up pipes/fork between stages, handles Z_TIMED / Z_ASYNC.
execpline2
Port of execpline2(Estate state, wordcode pcode, int how, int input, int output, int last1) from Src/exec.c:1989-2040. Recursive multi-stage pipe walker: at each step, analyse the current command, fork-into-pipe (if mid-pipeline) or exec directly (if WC_PIPE_END), then recurse on the next stage with pipes[0] as its input fd.
execrepeat
Port of execrepeat(Estate state, UNUSED(int do_exec)) from Src/loop.c:499-551. repeat N; do body; done.
execrestore
Port of void execrestore(void) from Src/exec.c:6470.
execsave
Port of void execsave(void) from Src/exec.c:6438.
execselect
Port of execselect(Estate state, UNUSED(int do_exec)) from Src/loop.c:217-410. select var in words; do body; done REPL.
execshfunc
execshfunc(Shfunc shf, LinkList args)Src/exec.c:5540. Promoted to top-level pub fn so execcmd_exec at the shfunc dispatch site (c:4102-4105) can route through it. The real port owns queue_signals + cmdstack + sfcontext setup before calling doshfunc; doshfunc itself is unported, so we route the body through runshfunc (exec.rs:1700), which carries the wrapper-chain + zunderscore restore. Degraded vs C (no cmdstack push, no sfcontext flip, no XTRACE arg-trace) but the function body executes and lastval is updated.
execsimple
Port of execsimple(Estate state) from Src/exec.c:1290-1340. Fast-path for single-Simple commands that bypasses the full execcmd_exec machinery.
execstring
Port of void execstring(char *s, int dont_change_job, int exiting, char *context) from Src/exec.c:1228.
exectime
Port of exectime(Estate state, UNUSED(int do_exec)) from Src/exec.c:5279-5294. Run time pipeline: drives execpline with the Z_TIMED|Z_SYNC flags so it tracks wall/user/sys time.
exectry
Port of exectry(Estate state, int do_exec) from Src/loop.c:735-798. { try } always { finally }: capture errflag/retflag/breaks/contflag from the try-clause, reset them around the always-clause, then restore if always-clause didn’t override.
execute
Port of static void execute(LinkList args, int flags, int defpath) from Src/exec.c:723. The canonical “child runs the simple external command” path: STTY/ARGV0/BINF_DASH handling, makecline, closem(FDT_XTRACE) + child_unblock, slash-path direct exec, defpath (command -p) search, cmdnamtab + $PATH walk, with commandnotfound-handler fallback and the final exit-code escape (127 not-found / 126 noperm).
execwhile
Port of execwhile(Estate state, UNUSED(int do_exec)) from Src/loop.c:413-498. while/until cond; do body; done.
findcmd
Port of char *findcmd(char *arg0, int docopy, int default_path) from Src/exec.c:897. Walk $PATH (or DEFAULT_PATH under default_path=1) for arg0, returning the matching path on success. _docopy is the C source’s “duplicate the result” flag — Rust ownership covers it without an explicit copy step. default_path=1 forces /bin:/usr/bin:... search (used by command -p).
fixfds
Port of static void fixfds(int *save) from Src/exec.c:4523.
getfpfunc
Port of getfpfunc(char *s, int *ksh, char **fdir, char **alt_path, int test_only) from Src/exec.c:5260. Walks $fpath (or the supplied spec_path slice) for a file named name and writes the resolved directory through *dir_path_out (matching the C char **dir_path). Returns Some(file_contents_path) on success, None when not found.
gethere
Convert a here-document into a here-string. Line-by-line port of gethere() from Src/exec.c:4569-4652. Reads the body from the input stream via hgetc() until the terminator line is matched, returning the collected body as a string. strp is in/out: on entry the raw terminator (possibly with token markers + leading tabs); on return the munged terminator (after quotesubst + untokenize and, for REDIR_HEREDOCDASH, leading-tab strip).
getherestr
Port of int getherestr(struct redir *fn) from Src/exec.c:4655.
getoutput
Port of LinkList getoutput(char *cmd, int qt) from Src/exec.c:4712-4791. Runs a command-substitution body in the active executor, then routes the captured stdout through readoutput(pipe, qt, NULL) semantics at c:4855-4872.
getoutputfile
Port of char *getoutputfile(char *cmd, char **eptr) from Src/exec.c:4910=(cmd) process substitution.
getpipe
Port of static int getpipe(char *cmd, int nullexec) from Src/exec.c:5119.
getproc
Port of char *getproc(char *cmd, char **eptr) from Src/exec.c:5025<(cmd) / >(cmd) process substitution via /dev/fd/N (PATH_DEV_FD branch; modern Linux/macOS).
hashcmd
Port of Cmdnam hashcmd(char *arg0, char **pp) from Src/exec.c:1010.
is_anonymous_function_name
Port of int is_anonymous_function_name(const char *name) from Src/exec.c:5300.
iscom
Port of int iscom(char *s) from Src/exec.c:962.
isgooderr
Port of int isgooderr(int e, char *dir) from Src/exec.c:652.
isreallycom
Port of int isreallycom(Cmdnam cn) from Src/exec.c:972-987.
isrelative
Port of int isrelative(char *s) from Src/exec.c:996.
loadautofn
Direct port of Shfunc loadautofn(Shfunc shf, int ks, int test_only, int ignore_loaddir) from Src/exec.c:5050. Walks $fpath for a file named shf->node.nam, reads it, installs the text body on the corresponding shfunctab entry, and clears PM_UNDEFINED.
loadautofnsetfile
Port of void loadautofnsetfile(Shfunc shf, char *fdir) from Src/exec.c:5657.
makecline
Port of static char **makecline(LinkList list) from Src/exec.c:2046.
mpipe
Port of int mpipe(int *pp) from Src/exec.c:5160.
namedpipe
Port of char *namedpipe(void) from Src/exec.c:5001.
parse_string
Port of parse_string(char *s, int reset_lineno) from Src/exec.c:283.
parsecmd
Lex a <(...)/>(...)/=(...) body — the leading 2 chars are the marker pair (Inang+Inpar, Outang+Inpar, Equals+Inpar), remainder is the command up to the matching Outpar. Returns the parsed Eprog (and writes the post-) cursor through eptr).
quote_tokenized_output
Port of void quote_tokenized_output(char *str, FILE *file) from Src/exec.c:2114.
readoutput
Port of Eprog parsecmd(char *cmd, char **eptr) from Src/exec.c:4878.
resolvebuiltin
Port of resolvebuiltin(const char *cmdarg, HashNode hn) from Src/exec.c:2703. Ensures that an autoload-stub builtin has its module loaded before the caller invokes its handlerfunc. If the stub has no handler, ensurefeature is asked to load the module and re-lookup the builtin node. C body (abridged):
restore_params
Port of restore_params(LinkList restorelist, LinkList removelist) from Src/exec.c:4464-4528. After the builtin/shfunc returns, unset every name in removelist, then for each saved param in restorelist re-install its values (PM_SPECIAL go through gsu setfn; regular params re-enter paramtab as-is).
runshfunc
Port of void runshfunc(Eprog prog, FuncWrap wrap, char *name) from Src/exec.c:6166. The inner shell-function executor — fires module-registered wrapper handlers around the function body, with $_ (zunderscore) save/restore and a paramscope push/pop around the wordcode walk.
save_params
Port of save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) from Src/exec.c:4410-4458. Walk WC_ASSIGN chain at pc, snapshot each existing param into restore_p (so the builtin/shfunc can restore them on return) and enqueue every touched name in remove_p (so we know what to unset).
search_defpath
Port of static char *search_defpath(char *cmd, char *pbuf, int plen) from Src/exec.c:691.
setunderscore
Port of void setunderscore(char *str) from Src/exec.c:2652.
shfunc_set_sticky
Port of void shfunc_set_sticky(Shfunc shf) from Src/exec.c:5527.
simple_redir_name
Port of char *simple_redir_name(Eprog prog, int redir_type) from Src/exec.c:4689.
spawnpipes
Port of static void spawnpipes(LinkList l, int nullexec) from Src/exec.c:5184.
sticky_emulation_dup
Port of Emulation_options sticky_emulation_dup(Emulation_options src, int useheap) from Src/exec.c:5501.
stripkshdef
Port of Eprog stripkshdef(Eprog prog, char *name) from Src/exec.c:6286-6364. Given an Eprog read from an autoload file plus the function name being defined, check whether the file consists of exactly one function NAME { … } definition for that name. If so, return a new Eprog whose prog/strs/ pats slice out just the function body (so calling code can invoke the body directly instead of re-parsing). Otherwise return the input untouched.
zexecve
Port of static int zexecve(char *pth, char **argv, char **newenvp) from Src/exec.c:504. Wraps execve(2) with:
zfork
Port of static pid_t zfork(struct timespec *ts) from Src/exec.c:349.