i needed to temporarily modify the umask in some vcs eclasses. rather than open coding the umask saving/restoring, i decided to re-use the eshopts_{push,pop} logic so the umask can be pushed/popped easily. the resulting code was mostly copy & paste the same, and the stack maintenance ends up drowning out the meat of the stuff i care about -- screwing with the umask. so to that end, i added a set of generic stack helpers: estack_{push,pop}. then i rewrote eshopts_{push,pop} and based eumask_{push,pop} on top of that. what do people think ? good stuff, or am i trying too hard ? -mike --- eutils.eclass 14 Dec 2011 17:36:18 -0000 1.372 +++ eutils.eclass 14 Dec 2011 22:23:02 -0000 @@ -100,6 +100,51 @@ esvn_clean() { find "$@" -type d -name '.svn' -prune -print0 | xargs -0 rm -rf } +# @FUNCTION: estack_push +# @USAGE: [items to push] +# @DESCRIPTION: +# Push any number of items onto the specified stack. Pick a name that +# is a valid variable (i.e. stick to alphanumerics), and push as many +# items as you like onto the stack at once. +# +# The following code snippet will echo 5, then 4, then 3, then ... +# @CODE +# estask_push mystack 1 2 3 4 5 +# while i=$(estack_pop mystack) ; do +# echo ${i} +# done +# @CODE +estack_push() { + [[ $# -eq 0 ]] && die "estack_push: incorrect # of arguments" + local stack_name="__ESTACK_$1__" ; shift + eval ${stack_name}+=\( \"\$@\" \) +} + +# @FUNCTION: estack_pop +# @USAGE: +# @DESCRIPTION: +# Pop a single item off the specified stack and return 0. If no more +# items are available, return 1. See estack_push for more info. +estack_pop() { + if [[ $# -ne 1 ]] ; then + # Would like to call `die` here, but people will usually + # be calling this in a subshell; e.g. + # val=$(estack_pop foo) + eerror "estack_pop: incorrect # of arguments" + return 1 + fi + + local stack_name="__ESTACK_$1__" ; shift + eval local i=\${#${stack_name}[@]} + # Don't warn -- let the caller interpret this as a failure + # or as normal behavior (akin to `shift`) + [[ $(( --i )) -eq -1 ]] && return 1 + + eval local s=\"\${${stack_name}[${i}]}\" + eval unset ${stack_name}[${i}] + echo "${s}" +} + # @FUNCTION: eshopts_push # @USAGE: [options to `set` or `shopt`] # @DESCRIPTION: @@ -126,15 +171,14 @@ esvn_clean() { eshopts_push() { # have to assume __ESHOPTS_SAVE__ isn't screwed with # as a `declare -a` here will reset its value - local i=${#__ESHOPTS_SAVE__[@]} if [[ $1 == -[su] ]] ; then - __ESHOPTS_SAVE__[$i]=$(shopt -p) + estack_push eshopts "$(shopt -p)" [[ $# -eq 0 ]] && return 0 shopt "$@" || die "eshopts_push: bad options to shopt: $*" else - __ESHOPTS_SAVE__[$i]=$- + estack_push eshopts $- [[ $# -eq 0 ]] && return 0 set "$@" || die "eshopts_push: bad options to set: $*" fi @@ -144,19 +188,36 @@ eshopts_push() { # Restore the shell options to the state saved with the corresponding # eshopts_push call. See that function for more details. eshopts_pop() { - [[ $# -ne 0 ]] && die "eshopts_pop takes no arguments" - local i=$(( ${#__ESHOPTS_SAVE__[@]} - 1 )) - [[ ${i} -eq -1 ]] && die "eshopts_{push,pop}: unbalanced pair" - local s=${__ESHOPTS_SAVE__[$i]} - unset __ESHOPTS_SAVE__[$i] + local s + s=$(estack_pop eshopts) || die # do not merge with `local` above if [[ ${s} == "shopt -"* ]] ; then eval "${s}" || die "eshopts_pop: sanity: invalid shopt options: ${s}" else set +$- || die "eshopts_pop: sanity: invalid shell settings: $-" set -${s} || die "eshopts_pop: sanity: unable to restore saved shell settings: ${s}" fi } +# @FUNCTION: eumask_push +# @USAGE: +# @DESCRIPTION: +# Set the umask to the new value specified while saving the previous +# value onto a stack. Useful for temporarily changing the umask. +eumask_push() { + estack_push eumask "$(umask)" + umask "$@" || die "${FUNCNAME}: bad options to umask: $*" +} + +# @FUNCTION: eumask_pop +# @USAGE: +# @DESCRIPTION: +# Restore the previous umask state. +eumask_pop() { + local s + s=$(estack_pop eumask) || die # do not merge with `local` above + umask ${s} || die "${FUNCNAME}: sanity: could not restore umask: ${s}" +} + # @VARIABLE: EPATCH_SOURCE # @DESCRIPTION: # Default directory to search for patches.