1 # Copyright 1999-2017 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 # @ECLASS: estack.eclass
6 # base-system@gentoo.org
7 # @BLURB: stack-like value storage support
9 # Support for storing values on stack-like variables.
11 if [[ -z ${_ESTACK_ECLASS} ]]; then
13 # @FUNCTION: estack_push
14 # @USAGE: <stack> [items to push]
16 # Push any number of items onto the specified stack. Pick a name that
17 # is a valid variable (i.e. stick to alphanumerics), and push as many
18 # items as you like onto the stack at once.
20 # The following code snippet will echo 5, then 4, then 3, then ...
22 # estack_push mystack 1 2 3 4 5
23 # while estack_pop mystack i ; do
28 [[ $# -eq 0 ]] && die "estack_push: incorrect # of arguments"
29 local stack_name="_ESTACK_$1_" ; shift
30 eval ${stack_name}+=\( \"\$@\" \)
33 # @FUNCTION: estack_pop
34 # @USAGE: <stack> [variable]
36 # Pop a single item off the specified stack. If a variable is specified,
37 # the popped item is stored there. If no more items are available, return
38 # 1, else return 0. See estack_push for more info.
40 [[ $# -eq 0 || $# -gt 2 ]] && die "estack_pop: incorrect # of arguments"
42 # We use the fugly _estack_xxx var names to avoid collision with
43 # passing back the return value. If we used "local i" and the
44 # caller ran `estack_pop ... i`, we'd end up setting the local
45 # copy of "i" rather than the caller's copy. The _estack_xxx
46 # garbage is preferable to using $1/$2 everywhere as that is a
48 local _estack_name="_ESTACK_$1_" ; shift
49 local _estack_retvar=$1 ; shift
50 eval local _estack_i=\${#${_estack_name}\[@\]}
51 # Don't warn -- let the caller interpret this as a failure
52 # or as normal behavior (akin to `shift`)
53 [[ $(( --_estack_i )) -eq -1 ]] && return 1
55 if [[ -n ${_estack_retvar} ]] ; then
56 eval ${_estack_retvar}=\"\${${_estack_name}\[${_estack_i}\]}\"
58 eval unset \"${_estack_name}\[${_estack_i}\]\"
61 # @FUNCTION: evar_push
62 # @USAGE: <variable to save> [more vars to save]
64 # This let's you temporarily modify a variable and then restore it (including
65 # set vs unset semantics). Arrays are not supported at this time.
67 # This is meant for variables where using `local` does not work (such as
68 # exported variables, or only temporarily changing things in a func).
74 # ... do some stuff that needs LC_ALL=C set ...
77 # # You can also save/restore more than one var at a time
78 # evar_push BUTTERFLY IN THE SKY
79 # ... do stuff with the vars ...
80 # evar_pop # This restores just one var, SKY
81 # ... do more stuff ...
82 # evar_pop 3 # This pops the remaining 3 vars
87 [[ ${!var+set} == "set" ]] \
89 || val="unset_76fc3c462065bb4ca959f939e6793f94"
90 estack_push evar "${var}" "${val}"
94 # @FUNCTION: evar_push_set
95 # @USAGE: <variable to save> [new value to store]
97 # This is a handy shortcut to save and temporarily set a variable. If a value
98 # is not specified, the var will be unset.
104 2) printf -v "${var}" '%s' "$2" ;;
105 *) die "${FUNCNAME}: incorrect # of args: $*" ;;
109 # @FUNCTION: evar_pop
110 # @USAGE: [number of vars to restore]
112 # Restore the variables to the state saved with the corresponding
113 # evar_push call. See that function for more details.
118 1) isdigit "${cnt}" || die "${FUNCNAME}: first arg must be a number: $*" ;;
119 *) die "${FUNCNAME}: only accepts one arg: $*" ;;
123 while (( cnt-- )) ; do
124 estack_pop evar val || die "${FUNCNAME}: unbalanced push"
125 estack_pop evar var || die "${FUNCNAME}: unbalanced push"
126 [[ ${val} == "unset_76fc3c462065bb4ca959f939e6793f94" ]] \
128 || printf -v "${var}" '%s' "${val}"
132 # @FUNCTION: eshopts_push
133 # @USAGE: [options to `set` or `shopt`]
135 # Often times code will want to enable a shell option to change code behavior.
136 # Since changing shell options can easily break other pieces of code (which
137 # assume the default state), eshopts_push is used to (1) push the current shell
138 # options onto a stack and (2) pass the specified arguments to set.
140 # If the first argument is '-s' or '-u', we assume you want to call `shopt`
141 # rather than `set` as there are some options only available via that.
143 # A common example is to disable shell globbing so that special meaning/care
144 # may be used with variables/arguments to custom functions. That would be:
146 # eshopts_push -o noglob
147 # for x in ${foo} ; do
148 # if ...some check... ; then
156 if [[ $1 == -[su] ]] ; then
157 estack_push eshopts "$(shopt -p)"
158 [[ $# -eq 0 ]] && return 0
159 shopt "$@" || die "${FUNCNAME}: bad options to shopt: $*"
161 estack_push eshopts "$(shopt -p -o)"
162 [[ $# -eq 0 ]] && return 0
163 set "$@" || die "${FUNCNAME}: bad options to set: $*"
167 # @FUNCTION: eshopts_pop
170 # Restore the shell options to the state saved with the corresponding
171 # eshopts_push call. See that function for more details.
174 estack_pop eshopts s || die "${FUNCNAME}: unbalanced push"
175 eval "${s}" || die "${FUNCNAME}: sanity: invalid shopt options: ${s}"
178 # @FUNCTION: eumask_push
179 # @USAGE: <new umask>
181 # Set the umask to the new value specified while saving the previous
182 # value onto a stack. Useful for temporarily changing the umask.
184 estack_push eumask "$(umask)"
185 umask "$@" || die "${FUNCNAME}: bad options to umask: $*"
188 # @FUNCTION: eumask_pop
191 # Restore the previous umask state.
193 [[ $# -eq 0 ]] || die "${FUNCNAME}: we take no options"
195 estack_pop eumask s || die "${FUNCNAME}: unbalanced push"
196 umask ${s} || die "${FUNCNAME}: sanity: could not restore umask: ${s}"
200 # @USAGE: <number> [more numbers]
202 # Return true if all arguments are numbers.
206 [[ ${d:-bad} == *[!0-9]* ]] && return 1