we are checking whether value is null, so we can have different
error messages for ${x:?} and ${x?}. Report and fix from
don fong <dfong@dfong.com>
+
+ 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
+ <roy.ivy.iii@gmail.com>
+
+ 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 <spbnick@gmail.com> back
+ on 2/11/2018
+
+
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
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
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
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
${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)
@( 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
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)
}
/* 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))
{
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));
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 */
}
extern int lastpipe_opt;
extern int inherit_errexit;
extern int localvar_inherit;
+extern int localvar_unset;
#if defined (EXTENDED_GLOB)
extern int extended_glob;
{ "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)
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.
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} $@
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;
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 \
-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
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" )
${THIS_SH} ./array22.sub
${THIS_SH} ./array23.sub
${THIS_SH} ./array24.sub
+${THIS_SH} ./array25.sub
--- /dev/null
+# 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
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
${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
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
--- /dev/null
+# 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
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
# 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
--- /dev/null
+#!/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
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;
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);