From: Chet Ramey Date: Mon, 12 Mar 2018 12:10:29 +0000 (-0400) Subject: commit bash-20180309 snapshot X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bf5b8103d466fdbc3bfcdaa5e21a0d0c0dce7cba;p=thirdparty%2Fbash.git commit bash-20180309 snapshot --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index fdb6ee256..6b074002d 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -15047,3 +15047,25 @@ subst.c we are checking whether value is null, so we can have different error messages for ${x:?} and ${x?}. Report and fix from don fong + + 3/5 + --- +lib/readline/bind.c + - _rl_read_file: instead of calling stat/open on the passed filename, + use open/fstat to avoid one possible filename translation and close + a small (benign) race condition. Report and fix from Roy Ivy + + + 3/11 + ---- +variables.c + - makunbound: if new variable localvar_unset is non-zero, mark local + vars in previous scopes as invisible and unset so they will show + up as unset until that previous scope returns (similar to how local + variables in the current local scope are handled). localvar_unset + is currently set to 0 with no way for a script to change its value. + Eventually there will be an option to modify it. From a bug-bash + discussion started by Nikolai Kondrashov back + on 2/11/2018 + + diff --git a/MANIFEST b/MANIFEST index 5f6358120..93d7c0f28 100644 --- a/MANIFEST +++ b/MANIFEST @@ -864,6 +864,7 @@ tests/array21.sub f tests/array22.sub f tests/array23.sub f tests/array24.sub f +tests/array25.sub f tests/array-at-star f tests/array2.right f tests/assoc.tests f @@ -1125,6 +1126,7 @@ tests/nameref15.sub f tests/nameref16.sub f tests/nameref17.sub f tests/nameref18.sub f +tests/nameref19.sub f tests/nameref.right f tests/new-exp.tests f tests/new-exp1.sub f @@ -1348,6 +1350,7 @@ tests/varenv6.sub f tests/varenv7.sub f tests/varenv8.sub f tests/varenv9.sub f +tests/varenv10.sub f tests/version f tests/version.mini f tests/vredir.tests f diff --git a/Makefile.in b/Makefile.in index f86ebc1e0..80dd91662 100644 --- a/Makefile.in +++ b/Makefile.in @@ -148,17 +148,20 @@ SYSTEM_FLAGS = -DPROGRAM='"$(Program)"' -DCONF_HOSTTYPE='"$(Machine)"' -DCONF_OS BASE_CCFLAGS = $(SYSTEM_FLAGS) $(LOCAL_DEFS) \ $(DEFS) $(LOCAL_CFLAGS) $(INCLUDES) -CCFLAGS = $(ASAN_CFLAGS) $(BASE_CCFLAGS) ${PROFILE_FLAGS} $(CPPFLAGS) $(CFLAGS) +CCFLAGS = $(ADDON_CFLAGS) $(BASE_CCFLAGS) ${PROFILE_FLAGS} $(CPPFLAGS) $(CFLAGS) CCFLAGS_FOR_BUILD = $(BASE_CCFLAGS) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) BASE_LDFLAGS = @LDFLAGS@ $(LOCAL_LDFLAGS) $(CFLAGS) -LDFLAGS = ${ASAN_LDFLAGS} ${BASE_LDFLAGS} ${PROFILE_FLAGS} ${STATIC_LD} +LDFLAGS = ${ADDON_LDFLAGS} ${BASE_LDFLAGS} ${PROFILE_FLAGS} ${STATIC_LD} LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ $(LOCAL_LDFLAGS) $(CFLAGS_FOR_BUILD) ASAN_XCFLAGS = -fsanitize=address -fno-omit-frame-pointer ASAN_XLDFLAGS = -fsanitize=address +GCOV_XCFLAGS = -fprofile-arcs -ftest-coverage +GCOV_XLDFLAGS = -fprofile-arcs -ftest-coverage + INCLUDES = -I. @RL_INCLUDE@ -I$(srcdir) -I$(BASHINCDIR) -I$(LIBSRC) $(INTL_INC) # Maybe add: -Wextra @@ -602,7 +605,12 @@ lint: ${MAKE} ${MFLAGS} CFLAGS='${GCC_LINT_FLAGS}' .made asan: - ${MAKE} ${MFLAGS} ASAN_CFLAGS='${ASAN_XCFLAGS}' ASAN_LDFLAGS='${ASAN_XLDFLAGS}' .made + ${MAKE} ${MFLAGS} ADDON_CFLAGS='${ASAN_XCFLAGS}' ADDON_LDFLAGS='${ASAN_XLDFLAGS}' .made + +# cheating +gcov: + ${MAKE} ${MFLAGS} ADDON_CFLAGS='${GCOV_XCFLAGS}' ADDON_LDFLAGS='${GCOV_XLDFLAGS}' .made + # have to make this separate because making tests depend on $(PROGRAM) asan-tests: asan $(TESTS_SUPPORT) @@ -611,6 +619,10 @@ asan-tests: asan $(TESTS_SUPPORT) @( cd $(srcdir)/tests && \ PATH=$(BUILD_DIR)/tests:$$PATH THIS_SH=$(THIS_SH) $(SHELL) ${TESTSCRIPT} ) +profiling-tests: ${PROGRAM} + @test "X$$PROFILE_FLAGS" == "X" && { echo "profiling-tests: must be built with profiling enabled" >&2; exit 1; } + @${MAKE} ${MFLAGS} tests TESTSCRIPT=run-gprof + version.h: $(SOURCES) config.h Makefile patchlevel.h $(SHELL) $(SUPPORT_SRC)mkversion.sh -b -S ${topdir} -s $(RELSTATUS) -d $(Version) -o newversion.h \ && mv newversion.h version.h diff --git a/builtins/Makefile.in b/builtins/Makefile.in index d86a69b50..388ca4eb0 100644 --- a/builtins/Makefile.in +++ b/builtins/Makefile.in @@ -94,7 +94,7 @@ INCLUDES = -I. -I.. @RL_INCLUDE@ -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib -I$ BASE_CCFLAGS = ${PROFILE_FLAGS} $(DEFS) $(LOCAL_DEFS) $(SYSTEM_FLAGS) \ ${INCLUDES} $(LOCAL_CFLAGS) -CCFLAGS = ${ASAN_CFLAGS} $(BASE_CCFLAGS) $(CPPFLAGS) $(CFLAGS) +CCFLAGS = ${ADDON_CFLAGS} $(BASE_CCFLAGS) $(CPPFLAGS) $(CFLAGS) CCFLAGS_FOR_BUILD = $(BASE_CCFLAGS) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) diff --git a/builtins/declare.def b/builtins/declare.def index b56f24881..2574eba4d 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -573,8 +573,9 @@ restart_new_var_name: } /* However, if we're turning off the nameref attribute on an existing nameref variable, we first follow the nameref chain to the end, - modify the value of the variable this nameref variable references, - *CHANGING ITS VALUE AS A SIDE EFFECT* then turn off the nameref + modify the value of the variable this nameref variable references + if there is an assignment statement argument, + *CHANGING ITS VALUE AS A SIDE EFFECT*, then turn off the nameref flag *LEAVING THE NAMEREF VARIABLE'S VALUE UNCHANGED* */ else if (var == 0 && (flags_off & att_nameref)) { @@ -592,6 +593,16 @@ restart_new_var_name: any_failed++; NEXT_VARIABLE (); } + + /* If all we're doing is turning off the nameref attribute, don't + bother with VAR at all, whether it exists or not. Just turn it + off and go on. */ + if (refvar && flags_on == 0 && offset == 0 && (flags_off & ~att_nameref) == 0) + { + VUNSETATTR (refvar, att_nameref); + NEXT_VARIABLE (); + } + if (refvar) /* XXX - use declare_find_variable here? */ var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar)); @@ -671,6 +682,12 @@ restart_new_var_name: value = name + namelen; } free (oldname); + + /* OK, let's turn off the nameref attribute. + Now everything else applies to VAR. */ + if (flags_off & att_nameref) + VUNSETATTR (refvar, att_nameref); + goto restart_new_var_name; /* NOTREACHED */ } diff --git a/builtins/shopt.def b/builtins/shopt.def index e51c50012..467dc5344 100644 --- a/builtins/shopt.def +++ b/builtins/shopt.def @@ -92,6 +92,7 @@ extern int glob_asciirange; extern int lastpipe_opt; extern int inherit_errexit; extern int localvar_inherit; +extern int localvar_unset; #if defined (EXTENDED_GLOB) extern int extended_glob; @@ -214,6 +215,9 @@ static struct { { "lithist", &literal_history, (shopt_set_func_t *)NULL }, #endif { "localvar_inherit", &localvar_inherit, (shopt_set_func_t *)NULL }, +#if 0 + { "localvar_unset", &localvar_unset, (shopt_set_func_t *)NULL }, +#endif { "login_shell", &shopt_login_shell, set_login_shell }, { "mailwarn", &mail_warning, (shopt_set_func_t *)NULL }, #if defined (READLINE) diff --git a/lib/glob/Makefile.in b/lib/glob/Makefile.in index da28505ba..314622f13 100644 --- a/lib/glob/Makefile.in +++ b/lib/glob/Makefile.in @@ -53,7 +53,7 @@ BASHINCDIR = ${topdir}/include INCLUDES = -I. -I../.. -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib CCFLAGS = $(PROFILE_FLAGS) $(DEFS) $(LOCAL_DEFS) ${INCLUDES} $(CPPFLAGS) \ - $(LOCAL_CFLAGS) $(CFLAGS) ${ASAN_CFLAGS} + $(LOCAL_CFLAGS) $(CFLAGS) ${ADDON_CFLAGS} # Here is a rule for making .o files from .c files that doesn't force # the type of the machine (like -sun3) into the flags. diff --git a/lib/readline/Makefile.in b/lib/readline/Makefile.in index 08441a0de..b2358c67a 100644 --- a/lib/readline/Makefile.in +++ b/lib/readline/Makefile.in @@ -65,7 +65,7 @@ LOCAL_DEFS = @LOCAL_DEFS@ INCLUDES = -I. -I$(BUILD_DIR) -I$(topdir) -I$(topdir)/lib CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(APP_CFLAGS) $(CPPFLAGS) ${INCLUDES} \ - $(LOCAL_CFLAGS) $(CFLAGS) ${ASAN_CFLAGS} + $(LOCAL_CFLAGS) $(CFLAGS) ${ADDON_CFLAGS} .c.o: ${RM} $@ diff --git a/lib/readline/bind.c b/lib/readline/bind.c index 307799668..ef3331b80 100644 --- a/lib/readline/bind.c +++ b/lib/readline/bind.c @@ -839,8 +839,13 @@ _rl_read_file (char *filename, size_t *sizep) char *buffer; int i, file; - if ((stat (filename, &finfo) < 0) || (file = open (filename, O_RDONLY, 0666)) < 0) - return ((char *)NULL); + file = -1; + if (((file = open (filename, O_RDONLY, 0666)) < 0) || (fstat (file, &finfo) < 0)) + { + if (file >= 0) + close (file); + return ((char *)NULL); + } file_size = (size_t)finfo.st_size; diff --git a/lib/sh/Makefile.in b/lib/sh/Makefile.in index 9870dcd61..b1a086d6f 100644 --- a/lib/sh/Makefile.in +++ b/lib/sh/Makefile.in @@ -67,7 +67,7 @@ LOCAL_DEFS = @LOCAL_DEFS@ INCLUDES = -I. -I../.. -I$(topdir) -I$(topdir)/lib -I$(BASHINCDIR) -I$(srcdir) $(INTL_INC) -CCFLAGS = ${ASAN_CFLAGS} ${PROFILE_FLAGS} ${INCLUDES} $(DEFS) $(LOCAL_DEFS) \ +CCFLAGS = ${ADDON_CFLAGS} ${PROFILE_FLAGS} ${INCLUDES} $(DEFS) $(LOCAL_DEFS) \ $(LOCAL_CFLAGS) $(CFLAGS) $(CPPFLAGS) GCC_LINT_FLAGS = -Wall -Wshadow -Wpointer-arith -Wcast-qual \ diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 58c375b70..554f3d6ec 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/chet/bash/bash-current +BUILD_DIR=/usr/local/build/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR diff --git a/tests/array.right b/tests/array.right index 3aab592d9..ef3d49e2d 100644 --- a/tests/array.right +++ b/tests/array.right @@ -577,3 +577,47 @@ jkl abc def ghi jkl +1. indexed: +reference: +./array25.sub: line 10: ${a[ ]}: bad substitution +./array25.sub: line 11: ' ': syntax error: operand expected (error token is "' '") +./array25.sub: line 12: ${a[ ]}: bad substitution +4. 0 +5. 0 +6. 0 +assignment: +1.declare -a a=([0]="10" [1]="1") +2.declare -a a=([0]="11" [1]="1") +3.declare -a a=([0]="12" [1]="1") +4.declare -a a=([0]="13" [1]="1") +arithmetic: +1.declare -a a=([0]="0" [1]="1") +2.declare -a a=([0]="0" [1]="1") +3.declare -a a=([0]="0" [1]="1") +4.declare -a a=([0]="0" [1]="1") +5.declare -a a=([0]="0" [1]="1") +6.declare -a a=([0]="11" [1]="1") +7.declare -a a=([0]="0" [1]="1") +8.declare -a a=([0]="13" [1]="1") +2. associative: +reference: +./array25.sub: line 47: ${a[ ]}: bad substitution +2. +./array25.sub: line 49: ${a[ ]}: bad substitution +4. +5. +6. +assignment: +1.declare -A a=([" "]="10" [0]="0" [1]="1" ) +2.declare -A a=([" "]="11" [0]="0" [1]="1" ) +3.declare -A a=([" "]="12" [0]="0" [1]="1" ) +4.declare -A a=([" "]="13" [0]="0" [1]="1" ) +arithmetic: +1.declare -A a=([" "]="13" [0]="0" [1]="1" ) +2.declare -A a=([" "]="13" [0]="0" [1]="1" ) +3.declare -A a=([" "]="13" [0]="0" [1]="1" ) +4.declare -A a=([" "]="13" [0]="0" [1]="1" ) +5.declare -A a=([" "]="13" [0]="0" [1]="1" ) +6.declare -A a=([" "]="13" [0]="0" [1]="1" ["\" \""]="11" ) +7.declare -A a=([" "]="13" [0]="0" [1]="1" ["\" \""]="11" ) +8.declare -A a=([" "]="13" [0]="0" [1]="1" ["\" \""]="13" ) diff --git a/tests/array.tests b/tests/array.tests index 9f5dd6a33..c909a4f0b 100644 --- a/tests/array.tests +++ b/tests/array.tests @@ -400,3 +400,4 @@ ${THIS_SH} ./array21.sub ${THIS_SH} ./array22.sub ${THIS_SH} ./array23.sub ${THIS_SH} ./array24.sub +${THIS_SH} ./array25.sub diff --git a/tests/array25.sub b/tests/array25.sub new file mode 100644 index 000000000..b550d9d5e --- /dev/null +++ b/tests/array25.sub @@ -0,0 +1,70 @@ +# tests with blank subscripts, indexed and associative + +echo 1. indexed: +a[0]=0 a[1]=1 + +v=" " + +echo reference: + +echo 1. ${a[ ]} +echo 2. ${a[' ']} +echo 3. "${a[ ]}" +echo 4. ${a[$v]} +echo 5. ${a["$v"]} +echo 6. "${a[$v]}" + +echo assignment: + +echo -n 1. ; a[ ]=10 ; typeset -p a ; a[0]=0 +echo -n 2. ; a[" "]=11 ; typeset -p a ; a[0]=0 +echo -n 3. ; a[$v]=12 ; typeset -p a ; a[0]=0 +echo -n 4. ; a["$v"]=13 ; typeset -p a ; a[0]=0 + +echo arithmetic: + +echo -n 1. ; (( a[ ]=10 )); typeset -p a ; a[0]=0 +echo -n 2. ; (( a[" "]=11 )); typeset -p a ; a[0]=0 +echo -n 3. ; (( a[$v]=12 )); typeset -p a ; a[0]=0 +echo -n 4. ; (( a["$v"]=13 )); typeset -p a ; a[0]=0 +echo -n 5. ; let "a[ ]=10" ; typeset -p a ; a[0]=0 +echo -n 6. ; let "a[\" \"]=11" ; typeset -p a ; a[0]=0 +echo -n 7. ; let "a[$v]=12" ; typeset -p a ; a[0]=0 +echo -n 8. ; let "a[\"$v\"]=13" ; typeset -p a ; a[0]=0 + +unset -v a v + +echo 2. associative: +shopt -s assoc_expand_once + +typeset -A a +a[0]=0 a[1]=1 + +v=" " + +echo reference: + +echo 1. ${a[ ]} +echo 2. ${a[' ']} +echo 3. "${a[ ]}" +echo 4. ${a[$v]} +echo 5. ${a["$v"]} +echo 6. "${a[$v]}" + +echo assignment: + +echo -n 1. ; a[ ]=10 ; typeset -p a ; a[0]=0 +echo -n 2. ; a[" "]=11 ; typeset -p a ; a[0]=0 +echo -n 3. ; a[$v]=12 ; typeset -p a ; a[0]=0 +echo -n 4. ; a["$v"]=13 ; typeset -p a ; a[0]=0 + +echo arithmetic: + +echo -n 1. ; (( a[ ]=10 )); typeset -p a ; a[0]=0 +echo -n 2. ; (( a[" "]=11 )); typeset -p a ; a[0]=0 +echo -n 3. ; (( a[$v]=12 )); typeset -p a ; a[0]=0 +echo -n 4. ; (( a["$v"]=13 )); typeset -p a ; a[0]=0 +echo -n 5. ; let "a[ ]=10" ; typeset -p a ; a[0]=0 +echo -n 6. ; let "a[\" \"]=11" ; typeset -p a ; a[0]=0 +echo -n 7. ; let "a[$v]=12" ; typeset -p a ; a[0]=0 +echo -n 8. ; let "a[\"$v\"]=13" ; typeset -p a ; a[0]=0 diff --git a/tests/errors.right b/tests/errors.right index c968c3132..403450eb3 100644 --- a/tests/errors.right +++ b/tests/errors.right @@ -127,10 +127,22 @@ after 2: 1 after 3: 1 array after 1: 1 array after 2: 1 -./errors6.sub: line 18: ${-3}: bad substitution -./errors6.sub: line 19: -3: invalid variable name +./errors6.sub: uvar: parameter not set +./errors6.sub: uvar: parameter null or not set + +./errors6.sub: uvar: parameter null or not set +./errors6.sub: line 25: ${-3:-${-3}}: bad substitution +./errors6.sub: line 26: ${-3}: bad substitution +./errors6.sub: line 27: -3: invalid variable name after indir: 1 -./errors6.sub: line 18: ${-3}: bad substitution -./errors6.sub: line 19: -3: invalid variable name +./errors6.sub: line 30: -3: invalid variable name +./errors6.sub: uvar: parameter not set +./errors6.sub: uvar: parameter null or not set + +./errors6.sub: uvar: parameter null or not set +./errors6.sub: line 25: ${-3:-${-3}}: bad substitution +./errors6.sub: line 26: ${-3}: bad substitution +./errors6.sub: line 27: -3: invalid variable name after indir: 1 +./errors6.sub: line 30: -3: invalid variable name ./errors.tests: line 278: `!!': not a valid identifier diff --git a/tests/errors6.sub b/tests/errors6.sub index 3c05625a4..c08d41f1b 100644 --- a/tests/errors6.sub +++ b/tests/errors6.sub @@ -15,6 +15,17 @@ echo array after 1: $?' 2>/dev/null ${THIS_SH} -c 'typeset -A v ; v["0"]=one ; echo ${v[ ]} echo array after 2: $?' 2>/dev/null +${THIS_SH} -c 'echo ${uvar?}' ./errors6.sub +${THIS_SH} -c 'echo ${uvar:?}' ./errors6.sub +export uvar= +${THIS_SH} -c 'echo ${uvar?}' ./errors6.sub +${THIS_SH} -c 'echo ${uvar:?}' ./errors6.sub +unset uvar + +echo "${-3:-${-3}}" echo ${-3} x=-3; echo ${!x} echo after indir: $? + +function ivar() { echo -n "${!1:-${1}}"; } +ivar -3 diff --git a/tests/nameref.right b/tests/nameref.right index 220b1b509..0ff122239 100644 --- a/tests/nameref.right +++ b/tests/nameref.right @@ -417,3 +417,19 @@ declare -a var=([123]="") declare -n ref="var[123]" ./nameref18.sub: line 54: declare: var[123]: not found declare -a var=([123]="X") +declare -n foo="bar" +declare -- foo="bar" +./nameref19.sub: line 9: declare: bar: not found +declare -n foo="bar" +declare -- foo="bar" +declare -i bar="11" +declare -inx foo6 +declare -ix foo6 +declare -n foo="bar" +declare -- bar="Hello World!" +declare -- foo="bar" +declare -- bar="Hello World!" +declare -n foo="bar" +declare -- bar +declare -- foo="bar" +declare -- bar diff --git a/tests/nameref19.sub b/tests/nameref19.sub new file mode 100644 index 000000000..d46c003f1 --- /dev/null +++ b/tests/nameref19.sub @@ -0,0 +1,51 @@ +# can we unset the nameref attribute on variables with values that reference +# unset variables? + +unset bar +declare -n foo="bar" +declare -p foo + +declare +n foo +declare -p foo bar + +declare -n foo +declare -p foo + +# let's try removing the nameref attribute -- other attributes and assignments +# apply to the nameref target + +declare +n -i foo=7+4 +declare -p foo bar + +unset foo bar + +# but if the nameref variable doesn't have a value, the attributes apply to +# the nameref variable itself. thanks ksh93 + +declare -n foo6 +declare -xi foo6 +declare -p foo6 + +# and when we remove the nameref attribute, the other attributes remain + +declare +n foo6 +declare -p foo6 + +unset foo6 + +# make sure these cases continue to work + +# nameref referencing an existing, set variable +declare -n foo=bar +bar='Hello World!' +declare -p foo bar +declare +n foo +declare -p foo bar +unset foo bar + +# nameref referencing an existing, unset variable +declare -n foo=bar +declare bar +declare -p foo bar +declare +n foo +declare -p foo bar diff --git a/tests/varenv.right b/tests/varenv.right index 1d75239f0..c0ff664fd 100644 --- a/tests/varenv.right +++ b/tests/varenv.right @@ -119,6 +119,15 @@ declare -ar i4=([0]="a" [1]="b" [2]="c") declare -ar j4=([0]="1" [1]="2" [2]="3") ./varenv9.sub: line 66: unset: i4: cannot unset: readonly variable ./varenv9.sub: line 66: unset: j4: cannot unset: readonly variable +main: unset +inner: res unset +outer: res: X Y +main: after first call: X +inner: X +outer: res: X Y +main: after second call: X +func: null or unset +after func: x = outside a=z a=b a=z diff --git a/tests/varenv.sh b/tests/varenv.sh index 180ee1f67..cbfbed09e 100644 --- a/tests/varenv.sh +++ b/tests/varenv.sh @@ -225,5 +225,8 @@ ${THIS_SH} ./varenv8.sub # if executed in shell functions, like they modify local scalar variables ${THIS_SH} ./varenv9.sub +# more tests of unset and local variables with dynamic scoping +${THIS_SH} ./varenv10.sub + # make sure variable scoping is done right tt() { typeset a=b;echo a=$a; };a=z;echo a=$a;tt;echo a=$a diff --git a/tests/varenv10.sub b/tests/varenv10.sub new file mode 100644 index 000000000..887cfd2c2 --- /dev/null +++ b/tests/varenv10.sub @@ -0,0 +1,47 @@ +#!/bin/bash +# +# various tests of unset when applied to variables at different local scopes + +# function unsetting variable at previous local scope, uncovering global + +inner() +{ + unset res + echo ${FUNCNAME}: ${res-res unset} + if [[ $1 == "set" ]]; then + res[0]="X" + res[1]="Y" + fi +} + +outer() +{ + local res= + inner "$1" + echo ${FUNCNAME}: "res: ${res[@]}" +} + +echo main: ${res-unset} +outer set +echo main: after first call: ${res-unset} +outer dontset +echo main: after second call: ${res-unset} + +unset -f outer inner +unset res + +# local scope, unset variable at the same scope as local declaration +func() +{ + typeset x=4 + + unset x + echo ${FUNCNAME}: ${x:-null or unset} +} + +x=outside +func +echo after func: x = $x + +unset -f func +unset x diff --git a/variables.c b/variables.c index 0c35105f8..1c7b09ac4 100644 --- a/variables.c +++ b/variables.c @@ -124,6 +124,11 @@ int variable_context = 0; with the same name at a previous scope. */ int localvar_inherit = 0; +/* If non-zero, calling `unset' on local variables in previous scopes marks + them as invisible so lookups find them unset. This is the same behavior + as local variables in the current local scope. */ +int localvar_unset = 0; + /* The set of shell assignments which are made only in the environment for a single command. */ HASH_TABLE *temporary_env = (HASH_TABLE *)NULL; @@ -3706,7 +3711,8 @@ makunbound (name, vc) must be done so that if the variable is subsequently assigned a new value inside the function, the `local' attribute is still present. We also need to add it back into the correct hash table. */ - if (old_var && local_p (old_var) && variable_context == old_var->context) + if (old_var && local_p (old_var) && + (old_var->context == variable_context || (localvar_unset && old_var->context < variable_context))) { if (nofree_p (old_var)) var_setvalue (old_var, (char *)NULL);