1 ################################################################################
2 # Stash/unstash support for per-directory variables
4 # Adopted for zsh-autoenv.
6 # Copyright (c) 2009,2012 Dave Olszewski <cxreg@pobox.com>
7 # http://github.com/cxreg/smartcd
9 # This code is released under GPL v2 and the Artistic License, and
10 # may be redistributed under the terms of either.
13 # This library allows you to save the current value of a given environment
14 # variable in a temporary location, so that you can modify it, and then
15 # later restore its original value.
17 # Note that you will need to be in the same directory you were in when you
18 # stashed in order to successfully unstash. This is because the temporary
19 # variable is derived from your current working directory's path.
23 # export PATH=/something/else
27 # Note that this was written for use with, and works very well with,
28 # smartcd. See the documentation there for examples.
30 # An alternate usage is `autostash' which will trigger autounstash when
31 # leaving the directory, if combined with smartcd. This reduces the amount
32 # of explicit configuration you need to provide:
35 # export PATH=/something/else
37 # You may also do both operations on line line, leaving only the very succinct
39 # autostash PATH=/something/else
41 # If you attempt to stash the same value twice, a warning will be displayed
42 # and the second stash will not occur. To make it happen anyway, pass -f
43 # as the first argument to stash.
47 # You have already stashed FOO, please specify "-f" if you want to overwrite another stashed value
51 # This rule is a bit different if you are assigning a value and the variable
52 # has already been stashed. In that case, the new value will be assigned, but
53 # the stash will not be overwritten. This allows for non-conflicting chained
56 ################################################################################
59 # Library functions, from smartcd's lib/core/arrays. {{{
62 eval "$var=(\${$var[@]} \"\$@\")"
68 if [[ -n $var ]]; then
69 eval "echo \${#$var[@]}"
74 setopt localoptions && setopt ksharrays
77 if [[ -n $var ]] && (( $(eval "echo \${#$var[@]}") >= 1 )); then
78 eval "echo \"\${$var""[0]}\""
83 setopt localoptions && setopt ksharrays
88 if [[ -n $var ]] && (( $(eval "echo \${#$var[@]}") >= 1 )); then
89 eval "_ashift_return=\"\${$var""[0]}\""
92 echo "$_ashift_return"
99 if [[ $1 == "-f" ]]; then
103 while [[ -n $1 ]]; do
104 if [[ $1 == "alias" && $2 == *=* ]]; then
106 local _stashing_alias_assign=1
110 local stash_expression=$1
111 local stash_which=${stash_expression%%'='*}
112 local stash_name=$(_mangle_var $stash_which)
114 # Extract the value and make it double-quote safe
115 local stash_value=${stash_expression#*'='}
116 stash_value=${stash_value//\\/\\\\}
117 stash_value=${stash_value//\"/\\\"}
118 stash_value=${stash_value//\`/\\\`}
119 stash_value=${stash_value//\$/\\\$}
121 if [[ ( -n "$(eval echo '$__varstash_alias__'$stash_name)" ||
122 -n "$(eval echo '$__varstash_function__'$stash_name)" ||
123 -n "$(eval echo '$__varstash_array__'$stash_name)" ||
124 -n "$(eval echo '$__varstash_export__'$stash_name)" ||
125 -n "$(eval echo '$__varstash_variable__'$stash_name)" ||
126 -n "$(eval echo '$__varstash_nostash__'$stash_name)" )
127 && -z $force ]]; then
129 if [[ -z $already_stashed && ${already_stashed-_} == "_" ]]; then
130 local already_stashed=1
135 if [[ $stash_which == $stash_expression ]]; then
136 if [[ -z $run_from_smartcd ]]; then
137 echo "You have already stashed $stash_which, please specify \"-f\" if you want to overwrite another stashed value"
140 # Skip remaining work if we're not doing an assignment
146 # Handle any alias that may exist under this name
147 if [[ -z $already_stashed ]]; then
148 local alias_def="$(eval alias $stash_which 2>/dev/null)"
149 if [[ -n $alias_def ]]; then
150 alias_def=${alias_def#alias }
151 eval "__varstash_alias__$stash_name=\"$alias_def\""
155 if [[ $stash_which != $stash_expression && -n $_stashing_alias_assign ]]; then
156 eval "alias $stash_which=\"$stash_value\""
159 # Handle any function that may exist under this name
160 if [[ -z $already_stashed ]]; then
161 local function_def="$(declare -f $stash_which)"
162 if [[ -n $function_def ]]; then
163 # make function definition quote-safe. because we are going to evaluate the
164 # source with "echo -e", we need to double-escape the backslashes (so 1 -> 4)
165 function_def=${function_def//\\/\\\\\\\\}
166 function_def=${function_def//\"/\\\"}
167 function_def=${function_def//\`/\\\`}
168 function_def=${function_def//\$/\\\$}
169 eval "__varstash_function__$stash_name=\"$function_def\""
174 # Handle any variable that may exist under this name
175 local vartype="$(declare -p $stash_which 2>/dev/null)"
176 if [[ -n $vartype ]]; then
177 if [[ -n $ZSH_VERSION ]]; then
178 local pattern="typeset"
180 local pattern="declare"
182 if [[ $vartype == $pattern" -a"* ]]; then
183 # varible is an array
184 if [[ -z $already_stashed ]]; then
185 eval "__varstash_array__$stash_name=(\"\${$stash_which""[@]}\")"
188 elif [[ $vartype == $pattern" -x"* ]]; then
189 # variable is exported
190 if [[ -z $already_stashed ]]; then
191 eval "export __varstash_export__$stash_name=\"\$$stash_which\""
193 if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
194 eval "export $stash_which=\"$stash_value\""
198 if [[ -z $already_stashed ]]; then
199 eval "__varstash_variable__$stash_name=\"\$$stash_which\""
201 if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
202 eval "$stash_which=\"$stash_value\""
209 if [[ -z $stashed ]]; then
210 # Nothing in the variable we're stashing, but make a note that we stashed so we
211 # do the right thing when unstashing. Without this, we take no action on unstash
213 # Zsh bug sometimes caues
214 # (eval):1: command not found: __varstash_nostash___tmp__home_dolszewski_src_smartcd_RANDOM_VARIABLE=1
215 # fixed in zsh commit 724fd07a67f, version 4.3.14
216 if [[ -z $already_stashed ]]; then
217 eval "export __varstash_nostash__$stash_name=1"
220 # In the case of a previously unset variable that we're assigning too, export it
221 if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
222 eval "export $stash_which=\"$stash_value\""
227 unset -v _stashing_alias_assign
231 function get_autostash_array_name() {
232 local autostash_name=$(_mangle_var AUTOSTASH)
233 # Create a scalar variable linked to an array (for exporting).
234 local autostash_array_name=${(L)autostash_name}
235 typeset -xT $autostash_name $autostash_array_name
236 ret=$autostash_array_name
239 function autostash() {
240 local run_from_autostash=1
241 while [[ -n $1 ]]; do
242 if [[ $1 == "alias" && $2 == *=* ]]; then
244 local _stashing_alias_assign=1
247 local already_stashed=
249 if [[ -z $already_stashed ]]; then
250 local ret varname=${1%%'='*}
251 get_autostash_array_name
252 apush $ret "$varname"
255 unset -v _stashing_alias_assign
260 while [[ -n $1 ]]; do
261 local unstash_which=$1
262 if [[ -z $unstash_which ]]; then
266 local unstash_name=$(_mangle_var $unstash_which)
268 # This bit is a little tricky. Here are the rules:
269 # 1) unstash any alias, function, or variable which matches
270 # 2) if one or more matches, but not all, delete any that did not
271 # 3) if none match but nostash is found, delete all
272 # 4) if none match and nostash not found, do nothing
275 if [[ -n "$(eval echo \$__varstash_alias__$unstash_name)" ]]; then
276 eval "alias $(eval echo \$__varstash_alias__$unstash_name)"
277 unset __varstash_alias__$unstash_name
279 local unstashed_alias=1
282 # Unstash any function
283 if [[ -n "$(eval echo \$__varstash_function__$unstash_name)" ]]; then
284 eval "function $(eval echo -e \"\$__varstash_function__$unstash_name\")"
285 unset __varstash_function__$unstash_name
287 local unstashed_function=1
290 # Unstash any variable
291 if [[ -n "$(declare -p __varstash_array__$unstash_name 2>/dev/null)" ]]; then
292 eval "$unstash_which=(\"\${__varstash_array__$unstash_name""[@]}\")"
293 unset __varstash_array__$unstash_name
295 local unstashed_variable=1
296 elif [[ -n "$(declare -p __varstash_export__$unstash_name 2>/dev/null)" ]]; then
297 eval "export $unstash_which=\"\$__varstash_export__$unstash_name\""
298 unset __varstash_export__$unstash_name
300 local unstashed_variable=1
301 elif [[ -n "$(declare -p __varstash_variable__$unstash_name 2>/dev/null)" ]]; then
302 # Unset variable first to reset export
303 unset -v $unstash_which
304 eval "$unstash_which=\"\$__varstash_variable__$unstash_name\""
305 unset __varstash_variable__$unstash_name
307 local unstashed_variable=1
310 # Unset any values which did not exist at time of stash
311 local nostash="$(eval echo \$__varstash_nostash__$unstash_name)"
312 unset __varstash_nostash__$unstash_name
313 if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_alias" ) ]]; then
314 unalias $unstash_which 2>/dev/null
316 if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_function" ) ]]; then
317 unset -f $unstash_which 2>/dev/null
319 if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_variable" ) ]]; then
320 # Don't try to unset illegal variable names
321 # Using substitution to avoid using regex, which might fail to load on Zsh (minimal system).
322 if [[ ${unstash_which//[^a-zA-Z0-9_]/} == $unstash_which && $unstash_which != [0-9]* ]]; then
323 unset -v $unstash_which
331 function autounstash() {
332 # If there is anything in (mangled) variable AUTOSTASH, then unstash it
334 get_autostash_array_name
335 local autounstash_name=$ret
336 if (( $(alen $autounstash_name) > 0 )); then
337 local run_from_autounstash=1
338 while (( $(alen $autounstash_name) > 0 )); do
339 local autounstash_var=$(afirst $autounstash_name)
340 ashift $autounstash_name >/dev/null
341 unstash $autounstash_var
343 unset $autounstash_name
347 function _mangle_var() {
348 local mangle_var_where="${varstash_dir:-$PWD}"
349 mangle_var_where=${mangle_var_where//[^A-Za-z0-9]/_}
350 local mangled_name=${1//[^A-Za-z0-9]/_}
351 echo "_tmp_${mangle_var_where}_${mangled_name}"
354 # vim: filetype=zsh autoindent expandtab shiftwidth=4 softtabstop=4