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 00:19:25 +0200
User-agent: Mutt/1.5.18 (2008-05-17)

Here's another revised version.

It seems like a lot of bookkeeping (I wish we could transfer to bash?),
but I don't see another way if you want to pass "variables by reference"
in a bash library and prevent both yourself and public users from being
bitten by a conflict with a local variable - other than obfuscating your
library local variables.

I hope we can get it right, so it can be used with a revised version of
the `_get_cword' function of the bash-completion package (= blackbox).

There are still some questions:

I have to give local variables a value (append an equal sign) in order
to get them listed with 'local' in "blackbox()".  Is there a bash
builtin which lists all defined local variable names, even those not
having a value yet?

I also want to let 'blackbox' return array variables, which doesn't seem
to be possible with the 'printf/read' workarounds, so I have to use
'eval' and still need to sanitize the array variable names.  What's
considered good sanitizing: I'm now checkin for $' \n\t;:$' in
"_blackbox_var_sane()"?

Other changes are:

Called private function _after_ collission test.

Output error messages to stderr and return non-zero value.

Added check to enforce private function "_blackbox()" is ALWAYS called
via public "blackbox()".

I thought about whether private function "_blackbox()" should also have
a check for having local conflicts, but I figured this should be covered
by blackbox unit tests :-)

Added an input parameter and return value for the sake of completeness.

Here's the code:

    # Output error of variable by reference conflicting with local
    # Params: $1  Variable name causing conflict
    #         $2  Function in which conflict occurs
    _blackbox_var_conflict() {
        echo "ERROR: variable name conflicts with local variable:"\
            "'$1', in function: $2()" 1>&2
    }

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

    # Check whether variable is sane to be used as eval assignment
    # Param:  $1  Variable name
    # Return: False (1) if not sane
    _blackbox_var_sane() {
        if [[ ! $1 || ${1//[$' \n\t;:$']} != $1 ]]; then
            echo "ERROR: invalid identifier: '$1'"\
                "passed to function: ${FUNCNAME[1]}()" 1>&2
            return 1
        fi
    }

    # 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 arr=( foo "bar cee" )
        # ...
        # Lots of complicated library code here
        # ...
        [[ $2 ]] && printf -v $2 %s b            # Return value
        _blackbox_var_sane "$3" &&               # Return array value
            eval $3=\( \"\${arr[@]}\" \) || return 1
        return 0                                 # Return exit 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= __v= IFS=$'\n'
        # Check arguments conflicting with locals
        for __v in $(local); do
            case ${__v%=*} in $2|$3)
                _blackbox_var_conflict ${__v%=*} $FUNCNAME; return 1;;
            esac
        done
        _blackbox "$1" __2 __3                   # Call private function
        __x=$?                                   # Catch exit status
        [[ $2 ]] && printf -v $2 %s "$__2"       # Return value
        _blackbox_var_sane "$3" &&               # Return array value
            eval $3=\( \"\${__3[@]}\" \) || return 1
        return $__x                              # Return exit status
    }

    blackbox i a b; printf $'%s\n' $a "${b[@]}"  # Outputs vars all right
    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]