-
Notifications
You must be signed in to change notification settings - Fork 55
/
varstash
408 lines (365 loc) · 15.3 KB
/
varstash
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
################################################################################
# Stash/unstash support for per-directory variables
#
# Copyright (c) 2009,2012 Dave Olszewski <cxreg@pobox.com>
# http://github.com/cxreg/smartcd
#
# This code is released under GPL v2 and the Artistic License, and
# may be redistributed under the terms of either.
#
#
# This library allows you to save the current value of a given environment
# variable in a temporary location, so that you can modify it, and then
# later restore its original value.
#
# Note that you will need to be in the same directory you were in when you
# stashed in order to successfully unstash. This is because the temporary
# variable is derived from your current working directory's path.
#
# Usage:
# stash PATH
# export PATH=/something/else
# [...]
# unstash PATH
#
# Note that this was written for use with, and works very well with,
# smartcd. See the documentation there for examples.
#
# An alternate usage is `autostash' which will trigger autounstash when
# leaving the directory, if combined with smartcd. This reduces the amount
# of explicit configuration you need to provide:
#
# autostash PATH
# export PATH=/something/else
#
# You may also do both operations on line line, leaving only the very succinct
#
# autostash PATH=/something/else
#
# If you run stash, unstash, or varstash interactively, they will instruct
# you on how to create smartcd scripts for performing those actions
# automatically. You can affect this behavior with several variables:
#
# VARSTASH_QUIET - Set if you'd rather not see these notices
#
# VARSTASH_AUTOCONFIG - Set if you want the suggested actions to be
# performed automatically
#
# VARSTASH_AUTOEDIT - Set if you want it to set the values, but also
# give you an opportunity to edit the file
#
# If you attempt to stash the same value twice, a warning will be displayed
# and the second stash will not occur. To make it happen anyway, pass -f
# as the first argument to stash.
#
# $ stash FOO
# $ stash FOO
# You have already stashed FOO, please specify "-f" if you want to overwrite another stashed value
# $ stash -f FOO
# $
#
# This rule is a bit different if you are assigning a value and the variable
# has already been stashed. In that case, the new value will be assigned, but
# the stash will not be overwritten. This allows for non-conflicting chained
# stash-assign rules.
#
################################################################################
function stash() {
if [[ $1 == "-f" ]]; then
local force=1; shift
fi
if [[ -n $1 ]] && [[ -z $run_from_smartcd ]] && [[ -z $run_from_autostash ]]; then
local working_dir="${varstash_dir:-$(pwd)}"
local smartcd_dir="$(_smartcd_base)/scripts$working_dir"
local help_action="stashing a variable"
local help_dir=$smartcd_dir
local help_cmd="echo stash $@ >> \"$smartcd_dir/bash_enter\""
local help_which="bash_enter"
_manual_stash_help
fi
while [[ -n $1 ]]; do
if [[ $1 == "alias" && $2 =~ "=" ]]; then
shift
local _stashing_alias_assign=1
continue
fi
local stash_expression=$1
local stash_which=${stash_expression%%'='*}
local stash_name=$(_mangle_var $stash_which)
# Extract the value and make it double-quote safe
local stash_value=${stash_expression#*'='}
stash_value=${stash_value//\\/\\\\}
stash_value=${stash_value//\"/\\\"}
stash_value=${stash_value//\`/\\\`}
stash_value=${stash_value//\$/\\\$}
if [[ ( -n "$(eval echo '$__varstash_alias__'$stash_name)" ||
-n "$(eval echo '$__varstash_function__'$stash_name)" ||
-n "$(eval echo '$__varstash_array__'$stash_name)" ||
-n "$(eval echo '$__varstash_export__'$stash_name)" ||
-n "$(eval echo '$__varstash_variable__'$stash_name)" ||
-n "$(eval echo '$__varstash_nostash__'$stash_name)" )
&& -z $force ]]; then
if [[ -z $already_stashed && ${already_stashed-_} == "_" ]]; then
local already_stashed=1
else
already_stashed=1
fi
if [[ $stash_which == $stash_expression ]]; then
if [[ -z $run_from_smartcd ]]; then
echo "You have already stashed $stash_which, please specify \"-f\" if you want to overwrite another stashed value"
fi
# Skip remaining work if we're not doing an assignment
shift
continue
fi
fi
# Handle any alias that may exist under this name
if [[ -z $already_stashed ]]; then
local alias_def="$(eval alias $stash_which 2>/dev/null)"
if [[ -n $alias_def ]]; then
alias_def=${alias_def#alias }
eval "__varstash_alias__$stash_name=\"$alias_def\""
local stashed=1
fi
fi
if [[ $stash_which != $stash_expression && -n $_stashing_alias_assign ]]; then
eval "alias $stash_which=\"$stash_value\""
fi
# Handle any function that may exist under this name
if [[ -z $already_stashed ]]; then
local function_def="$(declare -f $stash_which)"
if [[ -n $function_def ]]; then
# make function definition quote-safe. because we are going to evaluate the
# source with "echo -e", we need to double-escape the backslashes (so 1 -> 4)
function_def=${function_def//\\/\\\\\\\\}
function_def=${function_def//\"/\\\"}
function_def=${function_def//\`/\\\`}
function_def=${function_def//\$/\\\$}
eval "__varstash_function__$stash_name=\"$function_def\""
local stashed=1
fi
fi
# Handle any variable that may exist under this name
local vartype="$(declare -p $stash_which 2>/dev/null)"
if [[ -n $vartype ]]; then
if [[ -n $ZSH_VERSION ]]; then
local pattern="^typeset"
else
local pattern="^declare"
fi
if [[ $vartype =~ $pattern" -a" ]]; then
# varible is an array
if [[ -z $already_stashed ]]; then
eval "__varstash_array__$stash_name=(\"\${$stash_which""[@]}\")"
fi
elif [[ $vartype =~ $pattern" -x" ]]; then
# variable is exported
if [[ -z $already_stashed ]]; then
eval "__varstash_export__$stash_name=\"\$$stash_which\""
fi
if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
eval "export $stash_which=\"$stash_value\""
fi
else
# regular variable
if [[ -z $already_stashed ]]; then
eval "__varstash_variable__$stash_name=\"\$$stash_which\""
fi
if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
eval "$stash_which=\"$stash_value\""
fi
fi
local stashed=1
fi
if [[ -z $stashed ]]; then
# Nothing in the variable we're stashing, but make a note that we stashed so we
# do the right thing when unstashing. Without this, we take no action on unstash
# Zsh bug sometimes caues
# (eval):1: command not found: __varstash_nostash___tmp__home_dolszewski_src_smartcd_RANDOM_VARIABLE=1
# fixed in zsh commit 724fd07a67f, version 4.3.14
if [[ -z $already_stashed ]]; then
eval "__varstash_nostash__$stash_name=1"
fi
# In the case of a previously unset variable that we're assigning too, export it
if [[ $stash_which != $stash_expression && -z $_stashing_alias_assign ]]; then
eval "export $stash_which=\"$stash_value\""
fi
fi
shift
unset -v _stashing_alias_assign
done
}
function autostash() {
if [[ -n $1 ]] && [[ -z $run_from_smartcd ]]; then
local working_dir="${varstash_dir:-$(pwd)}"
local smartcd_dir="$(_smartcd_base)/scripts$working_dir"
local help_action="autostashing a variable"
local help_dir=$smartcd_dir
local help_cmd="echo autostash $@ >> \"$smartcd_dir/bash_enter\""
local help_which="bash_enter"
_manual_stash_help
fi
local run_from_autostash=1
while [[ -n $1 ]]; do
if [[ $1 == "alias" && $2 =~ "=" ]]; then
shift
local _stashing_alias_assign=1
fi
local already_stashed=
stash "$1"
if [[ -z $already_stashed ]]; then
local autostash_name=$(_mangle_var AUTOSTASH)
local varname=${1%%'='*}
apush $autostash_name "$varname"
fi
shift
unset -v _stashing_alias_assign
done
}
function unstash() {
if [[ -n $1 ]] && [[ -z $run_from_smartcd ]] && [[ -z $run_from_autounstash ]]; then
local working_dir=${varstash_dir:-$(pwd)}
local smartcd_dir="$(_smartcd_base)/scripts$working_dir"
local help_action="unstashing a variable"
local help_dir=$smartcd_dir
local help_cmd="echo unstash $@ >> \"$smartcd_dir/bash_leave\""
local help_which="bash_leave"
_manual_stash_help
fi
while [[ -n $1 ]]; do
local unstash_which=$1
if [[ -z $unstash_which ]]; then
continue
fi
local unstash_name=$(_mangle_var $unstash_which)
# This bit is a little tricky. Here are the rules:
# 1) unstash any alias, function, or variable which matches
# 2) if one or more matches, but not all, delete any that did not
# 3) if none match but nostash is found, delete all
# 4) if none match and nostash not found, do nothing
# Unstash any alias
if [[ -n "$(eval echo \$__varstash_alias__$unstash_name)" ]]; then
eval "alias $(eval echo \$__varstash_alias__$unstash_name)"
unset __varstash_alias__$unstash_name
local unstashed=1
local unstashed_alias=1
fi
# Unstash any function
if [[ -n "$(eval echo \$__varstash_function__$unstash_name)" ]]; then
eval "function $(eval echo -e \"\$__varstash_function__$unstash_name\")"
unset __varstash_function__$unstash_name
local unstashed=1
local unstashed_function=1
fi
# Unstash any variable
if [[ -n "$(declare -p __varstash_array__$unstash_name 2>/dev/null)" ]]; then
eval "$unstash_which=(\"\${__varstash_array__$unstash_name""[@]}\")"
unset __varstash_array__$unstash_name
local unstashed=1
local unstashed_variable=1
elif [[ -n "$(declare -p __varstash_export__$unstash_name 2>/dev/null)" ]]; then
eval "export $unstash_which=\"\$__varstash_export__$unstash_name\""
unset __varstash_export__$unstash_name
local unstashed=1
local unstashed_variable=1
elif [[ -n "$(declare -p __varstash_variable__$unstash_name 2>/dev/null)" ]]; then
# Unset variable first to reset export
unset -v $unstash_which
eval "$unstash_which=\"\$__varstash_variable__$unstash_name\""
unset __varstash_variable__$unstash_name
local unstashed=1
local unstashed_variable=1
fi
# Unset any values which did not exist at time of stash
local nostash="$(eval echo \$__varstash_nostash__$unstash_name)"
unset __varstash_nostash__$unstash_name
if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_alias" ) ]]; then
unalias $unstash_which 2>/dev/null
fi
if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_function" ) ]]; then
unset -f $unstash_which 2>/dev/null
fi
if [[ ( -n "$nostash" && -z "$unstashed" ) || ( -n "$unstashed" && -z "$unstashed_variable" ) ]]; then
# Don't try to unset illegal variable names
if ! [[ $unstash_which =~ [^a-zA-Z0-9_] || $unstash_which =~ ^[0-9] ]]; then
unset -v $unstash_which
fi
fi
shift
done
}
function autounstash() {
# If there is anything in (mangled) variable AUTOSTASH, then unstash it
local autounstash_name=$(_mangle_var AUTOSTASH)
if (( $(alen $autounstash_name) > 0 )); then
local run_from_autounstash=1
while (( $(alen $autounstash_name) > 0 )); do
local autounstash_var=$(afirst $autounstash_name)
ashift $autounstash_name >/dev/null
unstash $autounstash_var
done
unset $autounstash_name
fi
}
function _mangle_var() {
local mangle_var_where="${varstash_dir:-$(pwd)}"
mangle_var_where=${mangle_var_where//[^A-Za-z0-9]/_}
local mangled_name=${1//[^A-Za-z0-9]/_}
echo "_tmp_${mangle_var_where}_${mangled_name}"
}
function _manual_stash_help() {
# instruct user how to create bash_enter or bash_leave
if [[ -n $VARSTASH_AUTOEDIT || -n $VARSTASH_AUTOCONFIG ]]; then
if [[ -z $VARSTASH_QUIET ]]; then
echo "varstash: Automatically running $help_cmd"
fi
if [[ ! -d $help_dir ]]; then
mkdir -p "$help_dir"
fi
eval $help_cmd
if [[ -n $VARSTASH_AUTOEDIT ]]; then
varstash_edit $help_which
fi
elif [[ -z $VARSTASH_QUIET ]]; then
echo "############################################################################"
echo "# You are manually $help_action. To automatically perform this"
echo "# whenever you enter this directory, paste the following command(s):"
if [[ ! -d $help_dir ]]; then
echo "mkdir -p \"$help_dir\""
fi
echo "$help_cmd"
echo "############################################################################"
fi
}
# A couple convenient aliases for smartcd_edit
function autostash_edit() {
varstash_edit "$@"
}
function varstash_edit() {
local file="$1"
local dir="$2"
if [[ -n $ZSH_VERSION ]]; then
if [[ $(type smartcd_edit) == "smartcd_edit is a shell function" ]]; then
local can_run=1
fi
else
if [[ $(type -t smartcd_edit) == "function" ]]; then
local can_run=1
fi
fi
if [[ -n "$can_run" ]]; then
# XXX - no support for "--host" or "--system" with this (yet?)
_smartcd_file_check "$file" "global" "" edit "$dir"
else
echo "smartcd not loaded, cannot run smartcd_edit"
fi
}
# Run deferred smartcd if we're waiting for it, and arrays is also loaded
if [[ -n "$smartcd_initially_deferred" && -n "$(fn_exists apush)" && -z "$SMARTCD_NOINITIAL" ]]; then
smartcd_skip_action=1
smartcd_run_mainline=1
smartcd cd
unset smartcd_skip_action
unset smartcd_initially_deferred
fi
# vim: filetype=sh autoindent expandtab shiftwidth=4 softtabstop=4