Gentoo Archives: gentoo-dev

From: "Steven J. Long" <slong@××××××××××××××××××.uk>
To: gentoo-dev@l.g.o
Subject: [gentoo-dev] Re: evar_push/pop helpers
Date: Sun, 02 Jun 2013 17:39:18
Message-Id: 20130602173804.GA4280@rathaus.eclipse.co.uk
In Reply to: [gentoo-dev] evar_push/pop helpers by Mike Frysinger
1 On Sat, Jun 01, 2013 at 11:03:20PM -0400, Mike Frysinger wrote:
2 > simple set of helpers to save/restore a variable in a limited section of code
3 >
4 > you can see an example of it in action at the end of the file where i need to
5 > tweak epatch (and no, doing `LC_COLLATE=C set -- ....` does not work).
6 > -mike
7 >
8 > --- eutils.eclass 22 May 2013 05:10:29 -0000 1.421
9 > +++ eutils.eclass 2 Jun 2013 03:00:46 -0000
10 > @@ -146,6 +146,77 @@ estack_pop() {
11 > eval unset ${__estack_name}\[${__estack_i}\]
12 > }
13 Just in passing, one of the places you don't want nullglob messing things up.
14
15 > +# @FUNCTION: evar_push
16 > +# @USAGE: <variable to save> [more vars to save]
17 > +# @DESCRIPTION:
18 > +# This let's you temporarily modify a variable and then restore it (including
19 > +# set vs unset semantics). Arrays are not supported at this time.
20 > +#
21 > +# For example:
22 > +# @CODE
23 > +# evar_push LC_ALL
24 > +# export LC_ALL=C
25 > +# ... do some stuff that needs LC_ALL=C set ...
26 > +# evar_pop
27 > +#
28 > +# # You can also save/restore more than one var at a time
29 > +# evar_push BUTTERFLY IN THE SKY
30 > +# ... do stuff with the vars ...
31 > +# evar_pop # This restores just one var, SKY
32 > +# ... do more stuff ...
33 > +# evar_pop 3 # This pops the remaining 3 vars
34 > +# @CODE
35 > +evar_push() {
36 > + local var val
37 > + for var ; do
38 > + [[ ${!var+set} == "set" ]] \
39 > + && val=${!var} \
40 > + || val="${___ECLASS_ONCE_EUTILS}"
41 > + estack_push evar "${var}" "${val}"
42 > + done
43 > +}
44 > +
45 > +# @FUNCTION: evar_push_set
46 > +# @USAGE: <variable to save> [new value to store]
47 > +# @DESCRIPTION:
48 > +# This is a handy shortcut to save and temporarily set a variable. If a value
49 > +# is not specified, the var will be unset.
50 > +evar_push_set() {
51 > + local var=$1
52 > + evar_push ${var}
53 > + case $# in
54 > + 1) unset ${var} ;;
55 > + 2) eval ${var}=\$2 ;;
56
57 I wish you wouldn't use eval for this. I know it's technically okay here, or would be
58 if you verified the parameter, but bash has printf -v for this purpose:
59
60 printf -v "$1" '%s' "$2" 2>/dev/null || die "unable to set: '$1' to: '$2'"
61
62 Note you should verify the variable name, ime, irrespective of a die on the printf
63 (or eval in sh.) It's much better feedback to the developer using the routine.
64 Here's what we currently use in sh:
65 # lib_key_ok "$input_name"
66 # is the input name ok as a variable or key identifier
67 lib_key_ok(){
68 case $1 in
69 ''|[![:alpha:]_]*|*[![:alnum:]_]*) return 1
70 ;; *) return 0
71 esac
72 }
73
74 # lib_check_key foo bar..
75 # die if any param is not lib_key_ok
76 lib_check_key() {
77 local i
78 for i; do
79 lib_key_ok "$i" || die "bad key: '$i'"
80 done
81 }
82
83 So I'd probably just use: lib_key_ok "$1" || die "$FUNCNAME: bad varname: '$1'"
84 in this context, or amend the check_key function to use:
85 .. || die "${FUNCNAME[1]}: bad key '$i'"
86 - since you're working in bash by definition.
87
88 printf -v also works with array members btw, if you do decide to extend in that
89 direction:
90 $ set -- 'foo[2]' 'array madness'
91 $ printf -v "$1" %s "$2"
92 $ echo "'${foo[2]}'"
93 'array madness'
94
95 And yeah, you can compose the variable name, eg:
96 set -- foo 'array 1' 1
97 printf -v "$1${3:+[$3]}" %s "$2"
98 echo "'${foo[1]}'"
99
100 So long as the developer using the routines knows about quoting, that works fine.
101 (If they don't, you have bigger problems.)
102
103 > + *) die "${FUNCNAME}: incorrect # of args: $*" ;;
104 > + esac
105 > +}
106 > +
107 > +# @FUNCTION: evar_pop
108 > +# @USAGE: [number of vars to restore]
109 > +# @DESCRIPTION:
110 > +# Restore the variables to the state saved with the corresponding
111 > +# evar_push call. See that function for more details.
112 > +evar_pop() {
113 > + local cnt=$1
114 might as well use: cnt=${1:-bad}
115 > + case $# in
116 > + 0) cnt=1 ;;
117 > + 1)
118 and lose this line.
119 > + : ${cnt:=bad}
120 > + [[ -n ${cnt//[0-9]} ]] && die "${FUNCNAME}: first arg must be a number: $*"
121 > + ;;
122 Though a generic is_int function comes in much handier, ime.
123 is_int() {
124 [[ $1 && $1 != *[^0-9]* ]]
125 }
126 1) is_int "$1" || die ..
127 cnt=$1
128
129 (I tend to guard against 0? as well, as octal inputs tend to mess things up later. I'm
130 sure you can see the case for sh.)
131
132 > + *) die "${FUNCNAME}: only accepts one arg: $*" ;;
133 > + esac
134 > +
135 > + local var val
136 > + while (( cnt-- )) ; do
137 > + estack_pop evar val || die "${FUNCNAME}: unbalanced push"
138 > + estack_pop evar var || die "${FUNCNAME}: unbalanced push"
139 > + [[ ${val} == "${___ECLASS_ONCE_EUTILS}" ]] \
140 > + && unset ${var} \
141 > + || eval ${var}=\${val}
142
143 again: printf -v "$var" %s "$val"
144 Though I'd rather this were in an if, so you can test for error better:
145
146 if [[ $val = "$___ECLASS_ONCE_EUTILS" ]]; then
147 unset "$var" 2>/dev/null || die "unable to unset: '$var'"
148 else printf -v "$var" %s "$val" 2>/dev/null \
149 || die "unable to reset: '$var'"
150 fi
151
152 (Or you can use: fi 2>/dev/null || die "unable to pop: '$var'"
153 if you prefer.)
154
155 > + done
156 > +}
157 > +
158 > # @FUNCTION: eshopts_push
159 > # @USAGE: [options to `set` or `shopt`]
160 > # @DESCRIPTION:
161 > @@ -344,8 +415,11 @@ epatch() {
162 > local EPATCH_SUFFIX=$1
163 >
164 > elif [[ -d $1 ]] ; then
165 > - # Some people like to make dirs of patches w/out suffixes (vim)
166 > + # We have to force sorting to C so that the wildcard expansion is consistent #471666.
167 > + evar_push_set LC_COLLATE C
168 > + # Some people like to make dirs of patches w/out suffixes (vim).
169 > set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
170 > + evar_pop
171
172 Have to say I'd just do this in a function adding to a locally-scoped array, if it were me,
173 eg local args; foo "$1" "$EPATCH_SUFFIX"; set -- "${args[@]}"; unset args
174 foo() {
175 local LC_COLLATE=C
176 args=("$1"/*${2:+."$2"})
177 }
178 though I'd prefer it if EPATCH_SUFFIX were allowed to be a list, personally.
179 Something like this (untested) with foo "$1" $EPATCH_SUFFIX
180 foo() {
181 local LC_COLLATE=C
182 set_nullglob
183 case $# in
184 0) fnusage
185 ;; 1) args=("$1"/*)
186 ;; 2) args=("$1"/*${2+".$2"})
187 ;; *) local i n=0 d=$1
188 shift; args=()
189 for i; do
190 [[ $i ]] || { n=1; continue; }
191 args+=("$d"/*."$i")
192 done
193 # if you want to allow for empty args from an ebuild to mean sth:
194 ((n && ! ${#args[*]})) && args=("$d"/*)
195 esac
196 reset_nullglob
197 }
198 (I'm not discounting dealing with the input variable to check for array etc,
199 it's just not my concern here.)
200
201 HTH,
202 steveL.
203 --
204 #friendly-coders -- We're friendly, but we're not /that/ friendly ;-)

Replies

Subject Author
Re: [gentoo-dev] Re: evar_push/pop helpers Mike Frysinger <vapier@g.o>