bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Passing variables by reference conflicts with local


From: Freddy Vulto
Subject: Re: Passing variables by reference conflicts with local
Date: Sun, 2 May 2010 13:35:59 +0200
User-agent: Mutt/1.5.18 (2008-05-17)

Here's another revised version.

I figured I could use bash internal `printf -v' just before the `eval'
to check if the variable name is a valid identifier.  This should be an
exceptional case, so I don't mind doing this at the end of the function
- checking just before the eval.  Now it's also easy to see the eval is
safe (provided you don't make typos on the right hand side, see
http://mywiki.wooledge.org/BashFAQ/006, thanks).

I moved conflict checking to a central reusable function
"_blackbox_safe_vars()" because one needs to do this for *every* library
function which is returning variables by reference, in my opinion, unless
you're willing to obfuscate all your local variables and still wait for a
conflict to happen?

The code below works around the following bash caveats?:
- local variables conflict with variables to be returned by reference
- `local' only lists variables having a value
- `printf -v' can't be used to assign to an array
 
Here's the code:

    # Check whether variable name(s) conflicts with local(s)
    # Params: $1  Local var names (output of 'local')
    #         $*  Variables to check
    # Return: False (1) if error
    _blackbox_safe_vars() {
        local IFS=$'\n' l v
        for l in $1; do
            for v in "${@:2}"; do
                [[ ${l%=*} == $v ]] && echo "ERROR: ${FUNCNAME[1]}:"\
                    "\`$v': conflicts with local" 1>&2 && return 1
            done
        done
        return 0
    }

    # Check whether private function is being called by public interface
    # Params: $1  Public function
    # Return: False (1) if error
    _blackbox_called_by() {
        [[ ${FUNCNAME[2]} != $1 ]] && { echo "ERROR: ${FUNCNAME[1]}:"\
            "MUST be called by $1()" 1>&2; return 1; } || return 0
    }

    # Private library function. Do not call directly. See blackbox()
    _blackbox() {
        _blackbox_called_by blackbox || return 1
        local a b c d e f g h i j k=( foo "bar cee" )
        # ...
        # Lots of library code here, not more than one screen though ;-)
        # ...
        printf -v"$2" %s b                          # Return value
        printf -v"$3" x && eval $3=\(\"\${k[@]}\"\) # Return array
        return 0                                    # Return status
    }

    # Param: $1  input argument
    # Param: $2  variable name to return value to
    # Param: $3  variable name to return array value to
    # Public library function
    blackbox() {
        # NOTE: Give all locals a value so they're listed with 'local'
        local __2= __3= __x=
        _blackbox_safe_vars "$(local)" "$2" "$3" || return 1
        _blackbox "$1" __2 __3                        # Call private
        __x=$?                                        # Catch status
        printf -v"$2" %s "$__2"                       # Return value
        printf -v"$3" x && eval $3=\(\"\${__3[@]}\"\) # Return array
        return $__x                                   # Return status
    }

    blackbox i a b; printf $'%s\n' $a "${b[@]}"  # Outputs vars ok
    blackbox i __2 __3                           # Outputs error
    d='ls /;true'; blackbox i "$d" "$d"          # No oops
    _blackbox a                                  # Force public access


Freddy Vulto
http://fvue.nl/wiki/Bash:_passing_variables_by_reference




reply via email to

[Prev in Thread] Current Thread [Next in Thread]