On Sat, Jun 01, 2013 at 11:03:20PM -0400, Mike Frysinger wrote:
> simple set of helpers to save/restore a variable in a limited section of code
>
> you can see an example of it in action at the end of the file where i need to
> tweak epatch (and no, doing `LC_COLLATE=C set -- ....` does not work).
> -mike
>
> --- eutils.eclass 22 May 2013 05:10:29 -0000 1.421
> +++ eutils.eclass 2 Jun 2013 03:00:46 -0000
> @@ -146,6 +146,77 @@ estack_pop() {
> eval unset ${__estack_name}\[${__estack_i}\]
> }
Just in passing, one of the places you don't want nullglob messing things up.
> +# @FUNCTION: evar_push
> +# @USAGE: <variable to save> [more vars to save]
> +# @DESCRIPTION:
> +# This let's you temporarily modify a variable and then restore it (including
> +# set vs unset semantics). Arrays are not supported at this time.
> +#
> +# For example:
> +# @CODE
> +# evar_push LC_ALL
> +# export LC_ALL=C
> +# ... do some stuff that needs LC_ALL=C set ...
> +# evar_pop
> +#
> +# # You can also save/restore more than one var at a time
> +# evar_push BUTTERFLY IN THE SKY
> +# ... do stuff with the vars ...
> +# evar_pop # This restores just one var, SKY
> +# ... do more stuff ...
> +# evar_pop 3 # This pops the remaining 3 vars
> +# @CODE
> +evar_push() {
> + local var val
> + for var ; do
> + [[ ${!var+set} == "set" ]] \
> + && val=${!var} \
> + || val="${___ECLASS_ONCE_EUTILS}"
> + estack_push evar "${var}" "${val}"
> + done
> +}
> +
> +# @FUNCTION: evar_push_set
> +# @USAGE: <variable to save> [new value to store]
> +# @DESCRIPTION:
> +# This is a handy shortcut to save and temporarily set a variable. If a
> value
> +# is not specified, the var will be unset.
> +evar_push_set() {
> + local var=$1
> + evar_push ${var}
> + case $# in
> + 1) unset ${var} ;;
> + 2) eval ${var}=\$2 ;;
I wish you wouldn't use eval for this. I know it's technically okay here, or
would be
if you verified the parameter, but bash has printf -v for this purpose:
printf -v "$1" '%s' "$2" 2>/dev/null || die "unable to set: '$1' to: '$2'"
Note you should verify the variable name, ime, irrespective of a die on the
printf
(or eval in sh.) It's much better feedback to the developer using the routine.
Here's what we currently use in sh:
# lib_key_ok "$input_name"
# is the input name ok as a variable or key identifier
lib_key_ok(){
case $1 in
''|[![:alpha:]_]*|*[![:alnum:]_]*) return 1
;; *) return 0
esac
}
# lib_check_key foo bar..
# die if any param is not lib_key_ok
lib_check_key() {
local i
for i; do
lib_key_ok "$i" || die "bad key: '$i'"
done
}
So I'd probably just use: lib_key_ok "$1" || die "$FUNCNAME: bad varname: '$1'"
in this context, or amend the check_key function to use:
.. || die "${FUNCNAME[1]}: bad key '$i'"
- since you're working in bash by definition.
printf -v also works with array members btw, if you do decide to extend in that
direction:
$ set -- 'foo[2]' 'array madness'
$ printf -v "$1" %s "$2"
$ echo "'${foo[2]}'"
'array madness'
And yeah, you can compose the variable name, eg:
set -- foo 'array 1' 1
printf -v "$1${3:+[$3]}" %s "$2"
echo "'${foo[1]}'"
So long as the developer using the routines knows about quoting, that works
fine.
(If they don't, you have bigger problems.)
> + *) die "${FUNCNAME}: incorrect # of args: $*" ;;
> + esac
> +}
> +
> +# @FUNCTION: evar_pop
> +# @USAGE: [number of vars to restore]
> +# @DESCRIPTION:
> +# Restore the variables to the state saved with the corresponding
> +# evar_push call. See that function for more details.
> +evar_pop() {
> + local cnt=$1
might as well use: cnt=${1:-bad}
> + case $# in
> + 0) cnt=1 ;;
> + 1)
and lose this line.
> + : ${cnt:=bad}
> + [[ -n ${cnt//[0-9]} ]] && die "${FUNCNAME}: first arg must be a
> number: $*"
> + ;;
Though a generic is_int function comes in much handier, ime.
is_int() {
[[ $1 && $1 != *[^0-9]* ]]
}
1) is_int "$1" || die ..
cnt=$1
(I tend to guard against 0? as well, as octal inputs tend to mess things up
later. I'm
sure you can see the case for sh.)
> + *) die "${FUNCNAME}: only accepts one arg: $*" ;;
> + esac
> +
> + local var val
> + while (( cnt-- )) ; do
> + estack_pop evar val || die "${FUNCNAME}: unbalanced push"
> + estack_pop evar var || die "${FUNCNAME}: unbalanced push"
> + [[ ${val} == "${___ECLASS_ONCE_EUTILS}" ]] \
> + && unset ${var} \
> + || eval ${var}=\${val}
again: printf -v "$var" %s "$val"
Though I'd rather this were in an if, so you can test for error better:
if [[ $val = "$___ECLASS_ONCE_EUTILS" ]]; then
unset "$var" 2>/dev/null || die "unable to unset: '$var'"
else printf -v "$var" %s "$val" 2>/dev/null \
|| die "unable to reset: '$var'"
fi
(Or you can use: fi 2>/dev/null || die "unable to pop: '$var'"
if you prefer.)
> + done
> +}
> +
> # @FUNCTION: eshopts_push
> # @USAGE: [options to `set` or `shopt`]
> # @DESCRIPTION:
> @@ -344,8 +415,11 @@ epatch() {
> local EPATCH_SUFFIX=$1
>
> elif [[ -d $1 ]] ; then
> - # Some people like to make dirs of patches w/out suffixes (vim)
> + # We have to force sorting to C so that the wildcard expansion
> is consistent #471666.
> + evar_push_set LC_COLLATE C
> + # Some people like to make dirs of patches w/out suffixes (vim).
> set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
> + evar_pop
Have to say I'd just do this in a function adding to a locally-scoped array, if
it were me,
eg local args; foo "$1" "$EPATCH_SUFFIX"; set -- "${args[@]}"; unset args
foo() {
local LC_COLLATE=C
args=("$1"/*${2:+."$2"})
}
though I'd prefer it if EPATCH_SUFFIX were allowed to be a list, personally.
Something like this (untested) with foo "$1" $EPATCH_SUFFIX
foo() {
local LC_COLLATE=C
set_nullglob
case $# in
0) fnusage
;; 1) args=("$1"/*)
;; 2) args=("$1"/*${2+".$2"})
;; *) local i n=0 d=$1
shift; args=()
for i; do
[[ $i ]] || { n=1; continue; }
args+=("$d"/*."$i")
done
# if you want to allow for empty args from an ebuild to mean
sth:
((n && ! ${#args[*]})) && args=("$d"/*)
esac
reset_nullglob
}
(I'm not discounting dealing with the input variable to check for array etc,
it's just not my concern here.)
HTH,
steveL.
--
#friendly-coders -- We're friendly, but we're not /that/ friendly ;-)