pub struct SubshellSnapshot {Show 30 fields
pub paramtab: hashtable_nodes<Param>,
pub paramtab_hashed_storage: CowHashMap<String, IndexMap<String, String>>,
pub positional_params: Vec<String>,
pub env_vars: HashMap<String, String>,
pub special_globals: Vec<(String, String)>,
pub zstyles: style_table,
pub flock_fds: Vec<i32>,
pub loop_flags: (i32, i32, i32),
pub cwd: Option<PathBuf>,
pub umask: u32,
pub traps: HashMap<String, String>,
pub opts: HashMap<String, bool>,
pub aliases: Vec<(String, String, i32)>,
pub shfuncs: Arc<shfunc_table>,
pub functions_compiled: HashMap<String, Chunk>,
pub function_source: HashMap<String, String>,
pub modules: HashMap<String, i32>,
pub thingytab: HashMap<String, Thingy>,
pub keymapnamtab: hashtable_nodes<KeymapName>,
pub lastpid: i32,
pub jobtab: Vec<job>,
pub curjob: i32,
pub prevjob: i32,
pub maxjob: usize,
pub thisjob: i32,
pub saved_fds: Vec<(i32, i32)>,
pub sigtrapped: Vec<i32>,
pub subsh: i32,
pub builtins_disabled: HashSet<String>,
pub reswds_disabled: HashSet<String>,
}Expand description
Snapshot of subshell-isolated state. Captured at ( entry, restored at
) exit. zsh subshell semantics: assignments inside (…) don’t leak to
the outer scope — and that includes export. zsh forks a child for the
subshell so the child’s env::set_var dies with the child; without a fork
(zshrs runs subshells in-process for perf), we snapshot+restore the OS
env table around the subshell. Otherwise (export y=v) would leak y
to the parent shell, breaking every script that uses a subshell to
scope an env override.
Snapshot of mutable executor state across a subshell
boundary.
Port of the entersubsh() save/restore Src/exec.c does at
line 1084 — captures everything that must be replaced when a
(...) group fires.
Fields§
§paramtab: hashtable_nodes<Param>Snapshot of paramtab (the C-canonical parameter store) at
subshell entry. Step 1 of the unification mirrors writes to
paramtab, so subshell-scoped assignments now show up there
too — without this snapshot, restoring only variables /
arrays / assoc_arrays leaks the subshell’s writes to the
parent via paramtab (e.g. x=outer; (x=inner); echo $x returned
inner because paramsubst reads through paramtab).
Same node storage as the live table (Src/params.c:854
newparamtable(151, "paramtab")) so restoring a snapshot restores
C’s bucket-walk order too, not just the name→value mapping.
paramtab_hashed_storage: CowHashMap<String, IndexMap<String, String>>paramtab_hashed_storage field.
positional_params: Vec<String>positional_params field.
env_vars: HashMap<String, String>env_vars field.
special_globals: Vec<(String, String)>Values of the special parameters whose backing store is a process
GLOBAL rather than the parameter table — Src/params.c’s char *ifs
(IFS), wordchars, home, histsiz, … — each reached through a GSU
getfn/setfn pair (the dispatch list at params.rs:12548).
The paramtab snapshot above restores the param NODE, but the node only
carries the GSU pair; the value itself lives in the global, which a
paramtab restore doesn’t touch. C forks for (...), so a child’s writes
to those globals die with it. zshrs runs subshells in-process, so
(IFS=,; :) left the PARENT’s IFS as , — and every later word-split
in the parent silently used it. Same fork-copy reasoning as opts /
umask / aliases above.
zstyles: style_tableParent’s zstyletab at subshell entry (Src/Modules/zutil.c:106
static HashTable zstyletab). C forks for (...), so a
zstyle set inside the subshell dies with the child. zshrs runs
subshells in-process, so a subshell-scoped zstyle leaked into
the parent AND — because setstypat (c:388-396) inserts a
same-weight pattern AFTER the already-present ones — a second
subshell re-defining the same (context, style) pair only
REPLACED the leaked entry instead of establishing a fresh
definition order. Same fork-copy reasoning as aliases /
shfuncs / modules.
flock_fds: Vec<i32>Flock fds (Src/utils.c:2111 addlockfd) live at subshell
entry. zsystem flock FILE keeps the fd open for the life of
the shell; under C’s forked (...) the child’s fd — and hence
the lock — dies when the subshell exits. zshrs runs subshells
in-process, so the lock outlived the subshell and every later
zsystem flock on that file (from a real forked background job)
blocked forever. Recorded here so subshell_end can close the
fds the subshell itself opened.
loop_flags: (i32, i32, i32)loops / breaks / contflag at subshell entry
(c:Src/loop.c, c:Src/builtin.c bin_break). C forks for (...),
so a break executed inside dies with the child and the parent’s
loop runs on: for i in 1 2; do (break); print after; done prints
after twice. zshrs runs subshells in-process, so the three
counters have to be restored by hand at the boundary.
cwd: Option<PathBuf>Process working directory at subshell entry. cd inside the
subshell shouldn’t leak to the parent; we restore on End.
umask: u32File-creation mask at subshell entry. zsh forks for (...) so
umask set inside dies with the child; we run subshells in
process so we must restore the mask on End. Otherwise
umask 022; (umask 077); umask shows 077 in the parent.
traps: HashMap<String, String>Parent’s traps at subshell entry. zsh’s (trap "echo X" EXIT; true) runs the trap when the subshell exits — BEFORE the parent
continues. Without this snapshot, the trap inherited from parent
would fire, OR a trap set inside the subshell would leak to the
parent’s process exit. Restored on subshell_end after the
subshell’s own EXIT trap (if any) has fired. Stores a snapshot
of crate::ported::builtin::traps_table() (canonical).
opts: HashMap<String, bool>Parent’s shell options at subshell entry. (set -e) /
(setopt extendedglob) mustn’t leak; zsh forks the subshell
so child options die with the child. We run in-process, so we
must restore the option store on subshell_end.
aliases: Vec<(String, String, i32)>Parent’s alias entries at subshell entry. zsh forks for
(...) so (alias x=y) inside a subshell dies with the
child and doesn’t leak to the parent. zshrs runs subshells
in-process, so we must restore the alias table on
subshell_end. Bug #209 in docs/BUGS.md. Stored as a flat
Vec<(name, text, flags)> snapshot. The node FLAGS must
round-trip: ALIAS_GLOBAL / DISABLED distinguish global and
disabled aliases in the shared aliastab — the previous
(name, text) shape restored every entry with flags=0, so ANY
subshell ((true), zsh-z’s (zshz --add … &) precmd)
reflagged every global alias to REGULAR in the parent:
alias -g listed nothing and ${+galiases[x]} went 0 one
prompt after every define.
shfuncs: Arc<shfunc_table>Parent’s shell-function table at subshell entry. C zsh’s
entersubsh (Src/exec.c) forks before running the
subshell body so (f() { ... }) defining a function dies
with the child and never leaks to the parent. zshrs runs
subshells in-process, so we must clone shfunctab on entry
and restore on exit. Bug #208 in docs/BUGS.md. Stored as a
clone of the whole shfunc_table — the bucket layout is part
of the state, because ${(k)functions} / compadd -k functions
emit C’s bucket-walk order verbatim
(Src/Modules/parameter.c:480-481); rebuilding the table from
an unordered map on restore would reshuffle that order after
every ( … ) / $( … ).
functions_compiled: HashMap<String, Chunk>Parent’s compiled-function chunks at subshell entry. Companion
to shfuncs above — ShellExecutor.functions_compiled is the
runtime dispatch table that Op::CallFunction reads through;
without restoring it, a subshell (g() { override; }) leaves
the override bytecode chunk in place so the parent’s
g call still runs the override after subshell_end
restored shfunctab. Bug #208 in docs/BUGS.md.
function_source: HashMap<String, String>Parent’s function source map at subshell entry. Companion to
functions_compiled so typeset -f / whence show the
parent’s source after subshell exit, not the subshell’s
overridden body. Bug #208 in docs/BUGS.md.
modules: HashMap<String, i32>Parent’s modulestab modules map at subshell entry. zsh forks
for (...) so a (zmodload zsh/X) inside the subshell sets
MOD_INIT_B on the child’s modulestab; when the child exits the
flag dies with it and the parent’s modulestab is untouched.
zshrs runs subshells in-process, so a subshell zmodload
would otherwise flip the parent’s ${modules[zsh/X]} from
unset to “loaded”. Snapshot here and restore on subshell_end.
Bug #210 in docs/BUGS.md. Stored as (name → flags)
since module struct doesn’t derive Clone (LinkList/
Linkedmod) — and the only thing zmodload mutates that
affects introspection is the flags bitmask (MOD_INIT_B
for loaded, MOD_UNLOAD for unloaded).
thingytab: HashMap<String, Thingy>Parent’s THINGYTAB (ZLE widget registry) at subshell entry.
zsh forks for (...) so zle -N w f / zle -D w inside the
subshell flip widget bindings only in the child; when the
child exits the parent’s widget table is untouched. zshrs runs
subshells in-process so a subshell’s zle -D w would
otherwise unbind the parent’s widget. Bug #453 in docs/BUGS.md.
keymapnamtab: hashtable_nodes<KeymapName>Parent’s KEYMAPNAMTAB (named keymap registry) at subshell
entry. Same fork-copy semantics as THINGYTAB — a subshell’s
bindkey -N km / bindkey -D km mutates only the child’s
keymap registry in C zsh. Bug #454 in docs/BUGS.md.
lastpid: i32Parent’s $! (clone::lastpid) at subshell entry. C zsh forks
for (...), so a background job started INSIDE the subshell
sets the child’s lastpid only — ( : & ); echo $! prints 0
in zsh. zshrs runs subshells in-process, so restore on end.
jobtab: Vec<job>Job-control state at subshell entry: (JOBTAB clone, CURJOB,
PREVJOB, MAXJOB, THISJOB). C zsh forks for (...) so any
disown / wait / new & job inside the subshell mutates the
CHILD’s copy of jobtab and dies with it (Src/exec.c::entersubsh
fork semantics); the parent’s table is untouched. zshrs’s
in-process subshell must snapshot/restore to match — without
this, sleep 1 & (disown); jobs shows an empty table where
zsh still lists the job. Bug #462.
curjob: i32curjob at subshell entry (Src/jobs.c:75 global).
prevjob: i32prevjob at subshell entry (Src/jobs.c:80 global).
maxjob: usizemaxjob at subshell entry (Src/jobs.c:71 global).
thisjob: i32thisjob at subshell entry (Src/jobs.c:77 global).
saved_fds: Vec<(i32, i32)>User-range fds (0-9) at subshell entry: (fd, saved_dup)
pairs where saved_dup is an F_DUPFD >= 10 copy, or -1 when
the fd was closed at entry. C zsh forks for (...) so a bare
exec >file / exec 3<&- inside the child dies with it
(Src/exec.c entersubsh fork semantics); the in-process
subshell must restore the parent’s fd table on End. Without
this, (exec >t.log; ...); cat t.log left the PARENT’s fd 1
pointing at t.log and cat looped forever copying the file
into itself.
sigtrapped: Vec<i32>sigtrapped[] at subshell entry (Src/signals.c:39). C’s
entersubsh clears per-signal trap STATE via unsettrap(sig)
(c:Src/exec.c:1088-1092), which zeroes both the body and the
sigtrapped flags. zshrs cleared only the body table, so the flags
desynced: a subshell that dropped a trap body still reported the
signal as trapped. Snapshot the whole vector so subshell_end can
restore the parent’s exact state (including an inherited
ZSIG_IGNORED on SIGQUIT).
subsh: i32subsh at subshell entry (Src/exec.c:160 global). C’s
entersubsh sets subsh = 1 for a real (non-ESUB_FAKE)
subshell at c:Src/exec.c:1192-1193, and the forked child
carries it for the whole body. PRINT_EXIT_VALUE reads it
(c:4309 && !subsh), which is why zsh prints nothing for
setopt printexitvalue; (false) while still reporting a
bare false. zshrs runs ( … ) in-process, so the flag has
to be set on entry and restored by hand on End.
builtins_disabled: HashSet<String>Names of builtins carrying DISABLED in builtintab at
subshell entry (c:Src/builtin.c:541-547 enable/disable
flip node.flags & DISABLED; c:Src/hashtable.c:1097
builtintab). C forks for (...), so a (disable typeset)
marks the flag only in the child’s copy of builtintab and
the parent still sees the builtin. zshrs runs subshells
in-process against the process-global BUILTINS_DISABLED
set, so ( disable typeset ); typeset x=1 reported
command not found: typeset in the PARENT.
reswds_disabled: HashSet<String>Names of reserved words carrying DISABLED in reswdtab at
subshell entry (c:Src/builtin.c:541-547 disable -r;
c:Src/hashtable.c:1124 reswdtab = newhashtable(23, "reswdtab", NULL)). Same fork-copy reasoning as
builtins_disabled — (disable -r typeset) must not change
how the parent PARSES typeset foo=cmd``.
Auto Trait Implementations§
impl Freeze for SubshellSnapshot
impl RefUnwindSafe for SubshellSnapshot
impl Send for SubshellSnapshot
impl Sync for SubshellSnapshot
impl Unpin for SubshellSnapshot
impl UnsafeUnpin for SubshellSnapshot
impl UnwindSafe for SubshellSnapshot
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<F, W, T, D> Deserialize<With<T, W>, D> for F
impl<F, W, T, D> Deserialize<With<T, W>, D> for F
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more