Re: [gentoo-dev] Re: evar_push/pop helpers

2013-06-17 Thread Mike Frysinger
On Monday 17 June 2013 01:42:15 Mike Frysinger wrote:
 On Sunday 02 June 2013 13:38:04 Steven J. Long wrote:
  On Sat, Jun 01, 2013 at 11:03:20PM -0400, Mike Frysinger wrote:
   +# 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:
 
 interesting, i hadn't seen that before ... looks new to bash-3.1.  /me
 tucks that into his tool belt.
 
 although it doesn't quite work in the edge case where the value is an empty
 string.  consider:
   unset x
   printf -v x ''
   echo ${x+set}
 
 that should show set, but it does not.  i'll have to keep `eval ${var}=`
 when the value we're setting is empty.  or just keep the eval code since i
 have to do eval anyways at that point.
 
 i'll report it upstream to the bash guys.

looks like it can be worked around by doing:
printf -v x '%s' ''
which is arguably what we want anyways
-mike


signature.asc
Description: This is a digitally signed message part.


Re: [gentoo-dev] Re: evar_push/pop helpers

2013-06-16 Thread Mike Frysinger
On Sunday 02 June 2013 13:38:04 Steven J. Long wrote:
 On Sat, Jun 01, 2013 at 11:03:20PM -0400, Mike Frysinger wrote:
  --- eutils.eclass   22 May 2013 05:10:29 -  1.421
  +++ eutils.eclass   2 Jun 2013 03:00:46 -
  @@ -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.

not sure what you mean.  that escapes the [] so that bash doesn't accidentally 
glob things.  when the code actually executes, you don't need to escape 
things.

touch f i d x
unset arr
declare -A arr
arr[f]=1234
arr[idx]=4321
echo ${arr[f]} ${arr[idx]}
__estack_name=arr
__estack_i=f
eval unset ${__estack_name}\[${__estack_i}\]
echo ${#arr[@]}
__estack_i=idx
eval unset ${__estack_name}\[${__estack_i}\]
echo ${#arr[@]}

seems to work

  +# 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:

interesting, i hadn't seen that before ... looks new to bash-3.1.  /me tucks 
that into his tool belt.

although it doesn't quite work in the edge case where the value is an empty 
string.  consider:
unset x
printf -v x ''
echo ${x+set}

that should show set, but it does not.  i'll have to keep `eval ${var}=` 
when the value we're setting is empty.  or just keep the eval code since i 
have to do eval anyways at that point.

i'll report it upstream to the bash guys.

 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.

i don't think it's worth it.  if you screw up the name, it tends to be a one-
time cost, and the error shown is pretty clear as to where it's coming from.

 printf -v also works with array members btw, if you do decide to extend in
 that direction:

i don't think i will, but i probably can convert the other core stack code to 
use this ...

  +   : ${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.

yeah, and we have other code that wants this (a simple grep for 0-9 in eclass/ 
shows a bunch of hits)

  -   # 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})
 }

since i plan on using these funcs in other places, using the existing api 
keeps things simple

 though I'd prefer it if EPATCH_SUFFIX were allowed to be a list,
 personally.

wouldn't really make this any easier
-mike


signature.asc
Description: This is a digitally signed message part.


[gentoo-dev] Re: evar_push/pop helpers

2013-06-02 Thread Steven J. Long
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 -  1.421
 +++ eutils.eclass 2 Jun 2013 03:00:46 -
 @@ -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