FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Gentoo > Gentoo Development

 
 
LinkBack Thread Tools
 
Old 12-14-2011, 09:25 PM
Mike Frysinger
 
Default estack_{push,pop}: cool new helpers or over engineering?

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: <stack> [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: <stack>
+# @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: <new umask>
+# @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.
 
Old 12-14-2011, 10:49 PM
Mike Frysinger
 
Default estack_{push,pop}: cool new helpers or over engineering?

some people pointed out typos/bugs, so here's v2
-mike

--- eutils.eclass 14 Dec 2011 17:36:18 -0000 1.372
+++ eutils.eclass 14 Dec 2011 23:46:37 -0000
@@ -100,6 +100,54 @@ esvn_clean() {
find "$@" -type d -name '.svn' -prune -print0 | xargs -0 rm -rf
}

+# @FUNCTION: estack_push
+# @USAGE: <stack> [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
+# estack_push mystack 1 2 3 4 5
+# while estack_pop mystack i ; 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: <stack> [variable]
+# @DESCRIPTION:
+# Pop a single item off the specified stack. If a variable is specified,
+# the popped item is stored there. If no more items are available, return
+# 1, else return 0. See estack_push for more info.
+estack_pop() {
+ ( [[ $# -eq 0 ]] || [[ $# -gt 2 ]] ) && die "estack_pop: incorrect # of arguments"
+
+ # We use the fugly __estack_xxx var names to avoid collision with
+ # passing back the return value. If we used "local i" and the
+ # caller ran `estack_pop ... i`, we'd end up setting the local
+ # copy of "i" rather than the caller's copy. The __estack_xxx
+ # garbage is preferable to using $1/$2 everywhere as that is a
+ # bit harder to read.
+ local __estack_name="__ESTACK_$1__" ; shift
+ local __estack_retvar=$1 ; shift
+ eval local __estack_i=${#${__estack_name}[@]}
+ # Don't warn -- let the caller interpret this as a failure
+ # or as normal behavior (akin to `shift`)
+ [[ $(( --__estack_i )) -eq -1 ]] && return 1
+
+ if [[ -n ${__estack_retvar} ]] ; then
+ eval ${__estack_retvar}="${${__estack_name}[${__estack_i}]}"
+ fi
+ eval unset ${__estack_name}[${__estack_i}]
+}
+
# @FUNCTION: eshopts_push
# @USAGE: [options to `set` or `shopt`]
# @DESCRIPTION:
@@ -126,15 +174,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 +191,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
+ estack_pop eshopts s || die "${FUNCNAME}: unbalanced push"
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: <new umask>
+# @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
+ estack_pop eumask s || die "${FUNCNAME}: unbalanced push"
+ umask ${s} || die "${FUNCNAME}: sanity: could not restore umask: ${s}"
+}
+
# @VARIABLE: EPATCH_SOURCE
# @DESCRIPTION:
# Default directory to search for patches.
 
Old 12-16-2011, 06:29 AM
Steven J Long
 
Default estack_{push,pop}: cool new helpers or over engineering?

Just to point out that arithmetic context can be more efficient; no bugs,
except for a /minor/ possibility (second last comment.)

Mike Frysinger wrote:
> --- eutils.eclass 14 Dec 2011 17:36:18 -0000 1.372
> +++ eutils.eclass 14 Dec 2011 23:46:37 -0000
> @@ -100,6 +100,54 @@ esvn_clean() {
> find "$@" -type d -name '.svn' -prune -print0 | xargs -0 rm -rf
> }
>
> +# @FUNCTION: estack_push
> +# @USAGE: <stack> [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
> +# estack_push mystack 1 2 3 4 5
> +# while estack_pop mystack i ; do
> +# echo ${i}
A minor #bash point in passing: although these values of i are safe, for
tutorial code, I really would recommend quoting: echo "$i" (or "${i}"). It's
better to get people used to quoting by default, and only not quoting iff
they need field-splitting on parameter expansions (eg for a variable used
for command options.)

> +# done
> +# @CODE
> +estack_push() {
> + [[ $# -eq 0 ]] && die "estack_push: incorrect # of arguments"
> + local stack_name="__ESTACK_$1__" ; shift
> + eval ${stack_name}+=( "$@" )
> +}
((..)) is quicker than [[ .. ]] for arithmetic stuff, and usually easier to
grok swiftly.
(($#)) || die .. is how this would normally be done.

> +
> +# @FUNCTION: estack_pop
> +# @USAGE: <stack> [variable]
> +# @DESCRIPTION:
> +# Pop a single item off the specified stack. If a variable is specified,
> +# the popped item is stored there. If no more items are available,
> return
> +# 1, else return 0. See estack_push for more info.
> +estack_pop() {
> + ( [[ $# -eq 0 ]] || [[ $# -gt 2 ]] ) && die "estack_pop: incorrect
# of arguments"

(($# == 0 || $# > 2)) && die.. # does it in one command, with no subshell.
[[ $# -eq 0 || $# -gt 2 ]] && die .. would work too, but more slowly.
In general if you want to do complex chains without a subshell, you would
use: { } && .. instead of: ( ) && ..

TBH I would type (($#==0||$#>2)) in bash, though I space in C, where it
doesn't affect execution time. But it's not as clear, especially if you're
not in a highlighting editor.

> + # We use the fugly __estack_xxx var names to avoid collision with
> + # passing back the return value. If we used "local i" and the
> + # caller ran `estack_pop ... i`, we'd end up setting the local
> + # copy of "i" rather than the caller's copy. The __estack_xxx
> + # garbage is preferable to using $1/$2 everywhere as that is a
> + # bit harder to read.
> + local __estack_name="__ESTACK_$1__" ; shift
> + local __estack_retvar=$1 ; shift
> + eval local __estack_i=${#${__estack_name}[@]}
> + # Don't warn -- let the caller interpret this as a failure
> + # or as normal behavior (akin to `shift`)
> + [[ $(( --__estack_i )) -eq -1 ]] && return 1
((--__estack_i == -1)) && ..

> +
> + if [[ -n ${__estack_retvar} ]] ; then
> + eval ${__estack_retvar}="${${__estack_name}
[${__estack_i}]}"
> + fi
> + eval unset ${__estack_name}[${__estack_i}]
> +}
> +
> # @FUNCTION: eshopts_push
> # @USAGE: [options to `set` or `shopt`]
> # @DESCRIPTION:
> @@ -126,15 +174,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
I'm not sure how this will ever match, given that $1 has been checked above?
(($#==1)) && return 0 # if that applies (might be a 'bug'.)

> shopt "$@" || die "eshopts_push: bad options to shopt: $*"
> else
> - __ESHOPTS_SAVE__[$i]=$-
> + estack_push eshopts $-
> [[ $# -eq 0 ]] && return 0
(($#)) || return 0

> set "$@" || die "eshopts_push: bad options to set: $*"
> fi
> }
>

HTH,
Steve.
--
#friendly-coders -- We're friendly, but we're not /that/ friendly ;-)
 
Old 12-16-2011, 10:20 PM
Mike Frysinger
 
Default estack_{push,pop}: cool new helpers or over engineering?

On Friday 16 December 2011 02:29:25 Steven J Long wrote:
> Mike Frysinger wrote:
> > + [[ $# -eq 0 ]] && die "estack_push: incorrect # of arguments"
>
> ((..)) is quicker than [[ .. ]] for arithmetic stuff, and usually easier to
> grok swiftly.

i'm not used to using this style, so for now i think i'll keep the existing
(and it's more common in Gentoo atm, or at least in the code base i sample,
ignoring the obvious selection bias). i'll noodle on this though and see if i
can't convince myself to start using this and live migrating code in the tree.

> (($#)) || die .. is how this would normally be done.

i think this is a little less clear, but considering some of the
[advanced/complicated] bash code i've written elsewhere in Gentoo, maybe
that's a specious argument ...

> > eshopts_push() {
> > if [[ $1 == -[su] ]] ; then
> > - __ESHOPTS_SAVE__[$i]=$(shopt -p)
> > + estack_push eshopts "$(shopt -p)"
> > [[ $# -eq 0 ]] && return 0
>
> I'm not sure how this will ever match, given that $1 has been checked
> above? (($#==1)) && return 0 # if that applies (might be a 'bug'.)

the larger idea when i first wrote eshopts_{push,pop} was to not do any arg
parsing at all, but then i hit the issue that `shopt` and `set` options are
like a venn diagram -- a lot of common stuff, but each also has unique options.
so i had to introduce a little arg parsing to make it actually work. and the
duplicated/forked code in the sub-branches were kept as similar as possible to
(hopefully) make things simpler to read at a glance. but that means sharper
eyes notice a code branch which can never be hit as you've highlighted here.

as for the $#==1 check at the top, i don't want these helpers to require
arguments as it would prevent more creative uses. such as:
eshopts_push
. ./some-script-from-a-package
eshopts_pop
but maybe that's not a big deal ...
-mike
 

Thread Tools




All times are GMT. The time now is 04:28 PM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright 2007 - 2008, www.linux-archive.org