dev-python/hypothesis: Bump to 5.8.3
[gentoo.git] / eclass / estack.eclass
1 # Copyright 1999-2019 Gentoo Authors
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: estack.eclass
5 # @MAINTAINER:
6 # base-system@gentoo.org
7 # @BLURB: stack-like value storage support
8 # @DESCRIPTION:
9 # Support for storing values on stack-like variables.
10
11 if [[ -z ${_ESTACK_ECLASS} ]]; then
12
13 # @FUNCTION: estack_push
14 # @USAGE: <stack> [items to push]
15 # @DESCRIPTION:
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.
19 #
20 # The following code snippet will echo 5, then 4, then 3, then ...
21 # @CODE
22 #               estack_push mystack 1 2 3 4 5
23 #               while estack_pop mystack i ; do
24 #                       echo "${i}"
25 #               done
26 # @CODE
27 estack_push() {
28         [[ $# -eq 0 ]] && die "estack_push: incorrect # of arguments"
29         local stack_name="_ESTACK_$1_" ; shift
30         eval ${stack_name}+=\( \"\$@\" \)
31 }
32
33 # @FUNCTION: estack_pop
34 # @USAGE: <stack> [variable]
35 # @DESCRIPTION:
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.
39 estack_pop() {
40         [[ $# -eq 0 || $# -gt 2 ]] && die "estack_pop: incorrect # of arguments"
41
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
47         # bit harder to read.
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
54
55         if [[ -n ${_estack_retvar} ]] ; then
56                 eval ${_estack_retvar}=\"\${${_estack_name}\[${_estack_i}\]}\"
57         fi
58         eval unset \"${_estack_name}\[${_estack_i}\]\"
59 }
60
61 # @FUNCTION: evar_push
62 # @USAGE: <variable to save> [more vars to save]
63 # @DESCRIPTION:
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.
66 #
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).
69 #
70 # For example:
71 # @CODE
72 #               evar_push LC_ALL
73 #               export LC_ALL=C
74 #               ... do some stuff that needs LC_ALL=C set ...
75 #               evar_pop
76 #
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
83 # @CODE
84 evar_push() {
85         local var val
86         for var ; do
87                 [[ ${!var+set} == "set" ]] \
88                         && val=${!var} \
89                         || val="unset_76fc3c462065bb4ca959f939e6793f94"
90                 estack_push evar "${var}" "${val}"
91         done
92 }
93
94 # @FUNCTION: evar_push_set
95 # @USAGE: <variable to save> [new value to store]
96 # @DESCRIPTION:
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.
99 evar_push_set() {
100         local var=$1
101         evar_push ${var}
102         case $# in
103         1) unset ${var} ;;
104         2) printf -v "${var}" '%s' "$2" ;;
105         *) die "${FUNCNAME}: incorrect # of args: $*" ;;
106         esac
107 }
108
109 # @FUNCTION: evar_pop
110 # @USAGE: [number of vars to restore]
111 # @DESCRIPTION:
112 # Restore the variables to the state saved with the corresponding
113 # evar_push call.  See that function for more details.
114 evar_pop() {
115         local cnt=${1:-bad}
116         case $# in
117         0) cnt=1 ;;
118         1) [[ -z ${cnt//[0-9]} ]] \
119                 || die "${FUNCNAME}: first arg must be a number: $*" ;;
120         *) die "${FUNCNAME}: only accepts one arg: $*" ;;
121         esac
122
123         local var val
124         while (( cnt-- )) ; do
125                 estack_pop evar val || die "${FUNCNAME}: unbalanced push"
126                 estack_pop evar var || die "${FUNCNAME}: unbalanced push"
127                 [[ ${val} == "unset_76fc3c462065bb4ca959f939e6793f94" ]] \
128                         && unset ${var} \
129                         || printf -v "${var}" '%s' "${val}"
130         done
131 }
132
133 # @FUNCTION: eshopts_push
134 # @USAGE: [options to `set` or `shopt`]
135 # @DESCRIPTION:
136 # Often times code will want to enable a shell option to change code behavior.
137 # Since changing shell options can easily break other pieces of code (which
138 # assume the default state), eshopts_push is used to (1) push the current shell
139 # options onto a stack and (2) pass the specified arguments to set.
140 #
141 # If the first argument is '-s' or '-u', we assume you want to call `shopt`
142 # rather than `set` as there are some options only available via that.
143 #
144 # A common example is to disable shell globbing so that special meaning/care
145 # may be used with variables/arguments to custom functions.  That would be:
146 # @CODE
147 #               eshopts_push -o noglob
148 #               for x in ${foo} ; do
149 #                       if ...some check... ; then
150 #                               eshopts_pop
151 #                               return 0
152 #                       fi
153 #               done
154 #               eshopts_pop
155 # @CODE
156 eshopts_push() {
157         # Save both "shopt" and "set -o" option sets, because otherwise
158         # restoring posix would disable expand_aliases by side effect. #662586
159         estack_push eshopts "$(shopt -p -o) $(shopt -p)"
160         if [[ $1 == -[su] ]] ; then
161                 [[ $# -le 1 ]] && return 0
162                 shopt "$@" || die "${FUNCNAME}: bad options to shopt: $*"
163         else
164                 [[ $# -eq 0 ]] && return 0
165                 set "$@" || die "${FUNCNAME}: bad options to set: $*"
166         fi
167 }
168
169 # @FUNCTION: eshopts_pop
170 # @USAGE:
171 # @DESCRIPTION:
172 # Restore the shell options to the state saved with the corresponding
173 # eshopts_push call.  See that function for more details.
174 eshopts_pop() {
175         local s
176         estack_pop eshopts s || die "${FUNCNAME}: unbalanced push"
177         eval "${s}" || die "${FUNCNAME}: sanity: invalid shopt options: ${s}"
178 }
179
180 # @FUNCTION: eumask_push
181 # @USAGE: <new umask>
182 # @DESCRIPTION:
183 # Set the umask to the new value specified while saving the previous
184 # value onto a stack.  Useful for temporarily changing the umask.
185 eumask_push() {
186         estack_push eumask "$(umask)"
187         umask "$@" || die "${FUNCNAME}: bad options to umask: $*"
188 }
189
190 # @FUNCTION: eumask_pop
191 # @USAGE:
192 # @DESCRIPTION:
193 # Restore the previous umask state.
194 eumask_pop() {
195         [[ $# -eq 0 ]] || die "${FUNCNAME}: we take no options"
196         local s
197         estack_pop eumask s || die "${FUNCNAME}: unbalanced push"
198         umask ${s} || die "${FUNCNAME}: sanity: could not restore umask: ${s}"
199 }
200
201 _ESTACK_ECLASS=1
202 fi #_ESTACK_ECLASS