blob: 3315f61d77626de83d621de8ab5853c2f48d84e5 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
# ves.fish
#
# fish shell shim for sh-ves. Runs each ves command in a POSIX sh subshell
# against the real implementation, then diffs the subshell's resulting
# environment against fish's and replays the changes with set -gx / set -e.
# The activation save-state is carried in exported SHVES_SAVED_* variables,
# so it survives between subshell invocations.
#
# Install this file to ~/.config/fish/functions/ves.fish (make install does
# this automatically when fish is present).
function ves --description 'sh-ves: Bourne Shell Virtual Environment System'
if not set -q SHVES_SCRIPTS_DIR
set -gx SHVES_SCRIPTS_DIR $HOME/.local/bin/ves_scripts
end
if not test -f "$SHVES_SCRIPTS_DIR/ves-init.sh"
printf "ERROR: sh-ves scripts not found in [%s]. Is sh-ves installed?\n" "$SHVES_SCRIPTS_DIR" >&2
return 1
end
set -l dump (mktemp)
# Resolve env(1) to an absolute path up front: the command may rewrite
# PATH inside the subshell, which would otherwise make the dump fail.
set -l envbin (command -v env)
if test -z "$envbin"
set envbin /usr/bin/env
end
# Run the real command, then dump the subshell's final environment
# NUL-delimited (values may contain newlines) for replay into fish.
VES_ENV_DUMP=$dump VES_ENV_BIN=$envbin sh -c '
. "$SHVES_SCRIPTS_DIR"/ves-init.sh
ves "$@"
_shves_status=$?
"$VES_ENV_BIN" -0 > "$VES_ENV_DUMP"
exit $_shves_status
' ves $argv
set -l ret $status
# An empty dump means the subshell could not write its environment at
# all; replaying it would wrongly erase every exported variable.
if not test -s $dump
printf "ERROR: failed to capture subshell environment, shell state unchanged.\n" >&2
rm -f $dump
return 1
end
# Variables managed by the wrapper machinery itself, plus per-process
# bookkeeping that should never be replayed into the parent shell.
set -l skip VES_ENV_DUMP VES_ENV_BIN PWD OLDPWD SHLVL _
# fish list-style path variables: replay these split on ':' so fish
# keeps treating them as lists.
set -l pathvars PATH CDPATH MANPATH
set -l seen
for entry in (string split0 < $dump)
set -l kv (string split -m1 = -- $entry)
set -l name $kv[1]
set -l value $kv[2]
# entries that are not well-formed variable names (e.g. exported
# multiline values' continuation, or odd env entries) are skipped
if not string match -qr '^[a-zA-Z_][a-zA-Z0-9_]*$' -- $name
continue
end
if contains -- $name $skip
continue
end
set -a seen $name
if contains -- $name $pathvars
set -l new (string split : -- $value)
if not set -q $name
set -gx $name $new
else if test "$new" != "$$name"
set -gx $name $new
end
else
if not set -q $name
set -gx $name $value
else if test "$value" != "$$name"
set -gx $name $value
end
end
end
# Anything fish exports that vanished from the subshell environment
# was unset by the command (e.g. by ves deactivate).
for name in (set -nx)
if contains -- $name $skip
continue
end
if not contains -- $name $seen
set -e $name
end
end
rm -f $dump
return $ret
end
|