From b348b871b45ca354f58c8c965e53866903b666ea Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Mon, 23 Oct 2023 09:50:02 -0400 Subject: [PATCH] small change to brace expansion to inhibit some error messages; changes to printf builtin for precision and field widths --- CWRU/CWRU.chlog | 46 ++++++++++++++++++++++++++++++ MANIFEST | 2 ++ braces.c | 39 ++++++++++++++++++++++++-- builtins/printf.def | 66 +++++++++++++++++++++++++++++++++---------- parse.y | 7 +++-- tests/array.right | 1 + tests/array.tests | 4 +++ tests/braces.right | 5 ++++ tests/braces.tests | 11 ++++++++ tests/exp.right | 14 ++++++++++ tests/exp1.sub | 20 +++++++++++++ tests/printf.right | 63 +++++++++++++++++++++++++++++++++++------ tests/printf.tests | 19 +++++++++++-- tests/printf6.sub | 24 ++++++++++++++++ tests/printf7.sub | 68 +++++++++++++++++++++++++++++++++++++++++++++ tests/read.right | 2 ++ tests/read.tests | 9 ++++++ tests/run-braces | 2 +- variables.c | 18 ++++-------- 19 files changed, 376 insertions(+), 44 deletions(-) create mode 100644 tests/printf7.sub diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index a9f2dfe49..686c4c126 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -7864,3 +7864,49 @@ builtins/read.def - read_builtin: return EX_MISCERROR (2) if there is an error trying to assign to one of the variables. This is what the newest POSIX draft specifies. + +variables.c + - dispose_variable_value: do the right thing for att_nofree vars + - makunbound: call dispose_variable_value instead of using inline code + +braces.c + - brace_gobbler: make sure to set no_longjmp_on_fatal_error around + calls to extract_dollar_brace_string + + 10/17 + ----- +braces.c + - brace_gobbler: set SX_NOLONGJMP|SX_NOERROR in the flags passed + to extract_command_subst; make sure no_longjmp_on_fatal_error + is set before that call + - brace_gobbler: revert 10/13 change to use extract_dollar_brace_string + + 10/20 + ----- +parse.y + + - xparse_dolparen,parse_string_to_command: if SX_NOERROR is set in + FLAGS, add PST_NOERROR to parser_state. Not much effect yet. + - parse_matched_pair: if PST_NOERROR is set in parser_state, don't + print an error message if we hit EOF, just return an error. A start + at using PST_NOERROR to suppress error messages, not just duplicate + ones. We'll see how this goes before adding more + +builtins/printf.def + - decodeprec: decode the precision into an intmax_t; clamp the return + value at INT_MAX + - printf_builtin: update to posix interp 1647 (even though it's about + fprintf(3)) and output a NUL byte if %lc is supplied a null argument. + - printf_builtin: fix case where %Q is supplied with a precision in + the format string + - printf_builtin: fix case where %Q is supplied with a precision + greater than INT_MAX + - getwidestr,getwidechar: handle case where there is no argument + supplied; return NULL or NUL + - convwidestr: allow a precedence of 0 for %ls + - getint: don't call getintmax any more, just use the same code style + inline; getintmax will consume an extra argument on an error + Report and patches from Grisha Levit + - printf_builtin: handle field width and precision overflow from + getint() by ignoring the argument (fieldwidth = 0, precision = -1) + diff --git a/MANIFEST b/MANIFEST index 4c738ccea..3fb5147af 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1405,6 +1405,8 @@ tests/printf2.sub f tests/printf3.sub f tests/printf4.sub f tests/printf5.sub f +tests/printf6.sub f +tests/printf7.sub f tests/procsub.tests f tests/procsub.right f tests/procsub1.sub f diff --git a/braces.c b/braces.c index 3d096db75..fbc9b6f19 100644 --- a/braces.c +++ b/braces.c @@ -619,9 +619,30 @@ brace_gobbler (char *text, size_t tlen, int *indx, int satisfy) /* If compiling for the shell, treat ${...} like \{...} */ if (c == '$' && text[i+1] == '{' && quoted != '\'') /* } */ { +#if 0 + /* If we want to inhibit brace expansion during parameter expansions, + we need to skip over parameter expansions here. This is easier + than teaching brace expansion about the idiosyncracies of shell + word expansion. */ + int o, f; + o = no_longjmp_on_fatal_error; + no_longjmp_on_fatal_error = 1; + f = (quoted == '"') ? Q_DOUBLE_QUOTES : 0; si = i + 2; - t = extract_dollar_brace_string (text, &si, 0, SX_NOALLOC); + t = extract_dollar_brace_string (text, &si, f, SX_NOALLOC|SX_NOLONGJMP|SX_COMPLETE|SX_NOERROR); i = si + 1; + no_longjmp_on_fatal_error = o; + if (i > tlen) + { + i = tlen; + break; + } +#else + pass_next = 1; + i++; + if (quoted == 0) + level++; +#endif continue; } #endif @@ -654,10 +675,24 @@ brace_gobbler (char *text, size_t tlen, int *indx, int satisfy) /* Pass new-style command and process substitutions through unchanged. */ if ((c == '$' || c == '<' || c == '>') && text[i+1] == '(') /* ) */ { + int o; + comsub: + o = no_longjmp_on_fatal_error; + no_longjmp_on_fatal_error = 1; si = i + 2; - t = extract_command_subst (text, &si, SX_NOALLOC); +#if 0 + t = extract_command_subst (text, &si, SX_NOALLOC|SX_NOLONGJMP|SX_NOERROR|SX_COMPLETE); +#else + t = extract_command_subst (text, &si, SX_NOALLOC|SX_NOLONGJMP|SX_NOERROR); +#endif i = si + 1; + no_longjmp_on_fatal_error = o; + if (i > tlen) + { + i = tlen; + break; + } continue; } #endif diff --git a/builtins/printf.def b/builtins/printf.def index ae6c75c58..abff9c647 100644 --- a/builtins/printf.def +++ b/builtins/printf.def @@ -252,12 +252,12 @@ static size_t conv_bufsize; static inline int decodeprec (char *ps) { - int mpr; + intmax_t mpr; mpr = *ps++ - '0'; while (DIGIT (*ps)) mpr = (mpr * 10) + (*ps++ - '0'); - return mpr; + return (mpr < 0 || mpr > INT_MAX) ? INT_MAX : mpr; } int @@ -416,6 +416,9 @@ printf_builtin (WORD_LIST *list) fmt++; have_fieldwidth = 1; fieldwidth = getint (); + /* Handle field with overflow by ignoring it. */ + if (fieldwidth == INT_MAX || fieldwidth == INT_MIN) + fieldwidth = 0; } else while (DIGIT (*fmt)) @@ -430,6 +433,10 @@ printf_builtin (WORD_LIST *list) fmt++; have_precision = 1; precision = getint (); + /* Handle precision overflow by ignoring it. "A negative + precision is treated as if it were missing." */ + if (precision == INT_MAX || precision == INT_MIN) + precision = -1; } else { @@ -490,7 +497,12 @@ printf_builtin (WORD_LIST *list) ws[0] = wc; ws[1] = L'\0'; - r = printwidestr (start, ws, 1, fieldwidth, precision); + /* If %lc is supplied a null argument, posix interp 1647 + says it should produce a single null byte. */ + if (wc == L'\0') + r = printstr (start, "", 1, fieldwidth, precision); + else + r = printwidestr (start, ws, 1, fieldwidth, precision); if (r < 0) PRETURN (EXECUTION_FAILURE); break; @@ -514,7 +526,7 @@ printf_builtin (WORD_LIST *list) wp = getwidestr (&slen); r = printwidestr (start, wp, slen, fieldwidth, precision); - free (wp); + FREE (wp); if (r < 0) PRETURN (EXECUTION_FAILURE); break; @@ -647,20 +659,19 @@ printf_builtin (WORD_LIST *list) case 'Q': { char *p, *xp; - int r, mpr; + int r; size_t slen; r = 0; p = getstr (); /* Decode precision and apply it to the unquoted string. */ - if (convch == 'Q' && precstart) + if (convch == 'Q' && (have_precision || precstart)) { - mpr = decodeprec (precstart); - /* Error if precision > INT_MAX here? */ - precision = (mpr < 0 || mpr > INT_MAX) ? INT_MAX : mpr; + if (precstart) + precision = decodeprec (precstart); slen = strlen (p); /* printf precision works in bytes. */ - if (precision < slen) + if (precision >= 0 && precision < slen) p[precision] = '\0'; } if (p && *p == 0) /* XXX - getstr never returns null */ @@ -1336,17 +1347,31 @@ getstr (void) return ret; } +/* Don't call getintmax here because it may consume an argument on error, and + we call this to get field width/precision arguments. */ static int getint (void) { intmax_t ret; - - ret = getintmax (); + char *ep; if (garglist == 0) - return ret; + return (0); + + if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"') + return asciicode (); + + errno = 0; + ret = strtoimax (garglist->word->word, &ep, 0); - if (ret > INT_MAX) + if (*ep) + { + sh_invalidnum (garglist->word->word); + conversion_error = 1; + } + else if (errno == ERANGE) + printf_erange (garglist->word->word); + else if (ret > INT_MAX) { printf_erange (garglist->word->word); ret = INT_MAX; @@ -1357,6 +1382,7 @@ getint (void) ret = INT_MIN; } + garglist = garglist->next; return ((int)ret); } @@ -1519,6 +1545,13 @@ getwidestr (size_t *lenp) size_t slen, mblength; DECLARE_MBSTATE; + if (garglist == 0) + { + if (lenp) + *lenp = 0; + return NULL; + } + mbs = garglist->word->word; slen = strlen (mbs); ws = (wchar_t *)xmalloc ((slen + 1) * sizeof (wchar_t)); @@ -1547,6 +1580,9 @@ getwidechar (void) size_t slen, mblength; DECLARE_MBSTATE; + if (garglist == 0) + return L'\0'; + wc = 0; mblength = mbrtowc (&wc, garglist->word->word, locale_mb_cur_max, &state); if (MB_INVALIDCH (mblength)) @@ -1571,7 +1607,7 @@ convwidestr (wchar_t *ws, int prec) ts = (const wchar_t *)ws; - if (prec > 0) + if (prec >= 0) { rsize = prec * MB_CUR_MAX; ret = (char *)xmalloc (rsize + 1); diff --git a/parse.y b/parse.y index dd35ea768..0c8dc3f51 100644 --- a/parse.y +++ b/parse.y @@ -3793,7 +3793,8 @@ parse_matched_pair (int qc, int open, int close, size_t *lenp, int flags) if (ch == EOF) { free (ret); - parser_error (start_lineno, _("unexpected EOF while looking for matching `%c'"), close); + if ((parser_state & PST_NOERROR) == 0) + parser_error (start_lineno, _("unexpected EOF while looking for matching `%c'"), close); EOF_Reached = 1; /* XXX */ parser_state |= PST_NOERROR; /* avoid redundant error message */ return (&matched_pair_error); @@ -4602,7 +4603,7 @@ xparse_dolparen (const char *base, char *string, size_t *indp, int flags) /*(*/ parser_state |= PST_CMDSUBST|PST_EOFTOKEN; /* allow instant ')' */ /*{(*/ closer = shell_eof_token = funsub ? '}' : ')'; - if (flags & SX_COMPLETE) + if (flags & (SX_COMPLETE|SX_NOERROR)) parser_state |= PST_NOERROR; if (funsub) parser_state |= PST_FUNSUBST; @@ -4733,7 +4734,7 @@ parse_string_to_command (char *string, int flags) #if defined (ALIAS) || defined (DPAREN_ARITHMETIC) pushed_string_list = (STRING_SAVER *)NULL; #endif - if (flags & SX_COMPLETE) + if (flags & (SX_COMPLETE|SX_NOERROR)) parser_state |= PST_NOERROR; parser_state |= PST_STRING; diff --git a/tests/array.right b/tests/array.right index fb08a5c1e..346557a18 100644 --- a/tests/array.right +++ b/tests/array.right @@ -210,6 +210,7 @@ e b c $0 declare -a A=([0]="X=a" [1]="b") +2 3 4 5 6 FIN1:0 FIN2:0 FIN3:0 diff --git a/tests/array.tests b/tests/array.tests index bd2ee5660..b6d6c9cc6 100644 --- a/tests/array.tests +++ b/tests/array.tests @@ -407,6 +407,10 @@ Z='a b' A=( X=$Z ) declare -p A +# combine with brace expansion +letters=( {0..9} ) +echo "${letters["{2..6}"]}" + # tests for assigning to noassign array variables BASH_ARGC=(xxx) ; echo FIN1:$? BASH_ARGC=foio ; echo FIN2:$? diff --git a/tests/braces.right b/tests/braces.right index 75be33e81..7bcbc3880 100644 --- a/tests/braces.right +++ b/tests/braces.right @@ -22,6 +22,11 @@ foobar foobaz bazx bazy vx vy bazx bazy +4 +4 +./braces.tests: command substitution: line 59: unexpected EOF while looking for matching `)' +4 +4 1 2 3 4 5 6 7 8 9 10 0..10 braces 0 1 2 3 4 5 6 7 8 9 10 braces diff --git a/tests/braces.tests b/tests/braces.tests index 56a08166a..e1920f29a 100644 --- a/tests/braces.tests +++ b/tests/braces.tests @@ -49,6 +49,17 @@ echo ${var}{x,y} unset var varx vary +# make sure ${ is parsed as a word expansion, since it may contain other +# expansions +a=4 +echo "${a#'$('\'}" +echo "${a-'$('\'}" +echo "${a+'$('\'}" + +echo "${a#aaaa'$(aaaa'aaa)aaa\'}" +echo "${a#aaaa'$(aaaa)'aaaa\'}" +unset -v a + # new sequence brace operators echo {1..10} diff --git a/tests/exp.right b/tests/exp.right index 6ec11598a..bc2dae5e5 100644 --- a/tests/exp.right +++ b/tests/exp.right @@ -166,6 +166,20 @@ argv[2] = <^?> argv[1] = <^A^?> argv[1] = <^A^?^A^?> argv[1] = <^A^A^?> +argv[1] = <\^A> +argv[1] = <^A> +argv[1] = <\^A> +argv[1] = <\^A> +argv[1] = <^A> +argv[1] = <^A> +argv[1] = <^A^A> +argv[1] = <^A^A> +argv[1] = <\^A> +argv[1] = <\^A> +argv[1] = <\^A^?> +argv[1] = <\^A^?> +argv[1] = <^\^A ^\^?> +argv[1] = <^A ^_> 0.net 0.net0 diff --git a/tests/exp1.sub b/tests/exp1.sub index 3dbe3d90f..234705e9e 100644 --- a/tests/exp1.sub +++ b/tests/exp1.sub @@ -32,3 +32,23 @@ recho $FOO recho '' recho '' recho '' + +# tests of backslash-escaped CTLESC + +recho "${_+\}" +recho ${_+\} +recho "${_+\\}" +recho ${_+\\} +recho "${_+}" +recho ${_+} +recho "${_+}" +recho ${_+} + +recho $'\\\1' +recho "\\" + +recho "\\" +recho \\ + +recho $'\c\\\001 \c\\\177' +recho $'\c \c' diff --git a/tests/printf.right b/tests/printf.right index cf6f77e1c..29f46f182 100644 --- a/tests/printf.right +++ b/tests/printf.right @@ -21,6 +21,8 @@ this\&that echo a\\;ls echo a\'\;ls echo 'a'\''b'\;ls +\* +\* 1 2 3 4 5 onestring 0 0 0 onestring 0 0 0.00 @@ -38,7 +40,7 @@ A7 --\"abcd\"-- --\'abcd\'-- --a\x-- -./printf.tests: line 112: printf: missing hex digit for \x +./printf.tests: line 115: printf: missing hex digit for \x --\x-- ---- ---- @@ -99,12 +101,12 @@ A7 26 26 26 -./printf.tests: line 236: printf: `%10': missing format character -./printf.tests: line 237: printf: `M': invalid format character -ab./printf.tests: line 240: printf: `y': invalid format character -./printf.tests: line 243: printf: GNU: invalid number +./printf.tests: line 239: printf: `%10': missing format character +./printf.tests: line 240: printf: `M': invalid format character +ab./printf.tests: line 243: printf: `y': invalid format character +./printf.tests: line 246: printf: GNU: invalid number 0 -./printf.tests: line 244: printf: GNU: invalid number +./printf.tests: line 247: printf: GNU: invalid number 0 - (foo )(bar ) @@ -157,9 +159,15 @@ b xx xx < >< > -./printf.tests: line 348: printf: 9223372036854775825: Result too large +0 +^@ +0 +0.00 +'' +'' +./printf.tests: line 364: printf: 9223372036854775825: Result too large 9223372036854775807 -./printf.tests: line 349: printf: -9223372036854775815: Result too large +./printf.tests: line 365: printf: -9223372036854775815: Result too large -9223372036854775808 one one\ctwo @@ -330,6 +338,10 @@ hello -- hello -- 123 -- 6 -- +0000000 000 +0000001 +0000000 000 +0000001 0000000 340 262 207 340 262 263 340 262 277 340 262 225 340 263 206 340 0000010 262 227 340 262 263 340 263 201 012 0000019 @@ -355,3 +367,38 @@ hello -- 0000007 0000000 340 262 207 040 040 040 055 055 055 012 000000a +[][] +./printf7.sub: line 19: printf: 21474836470: Result too large +[] +./printf7.sub: line 20: printf: 21474836470: Result too large +[X] +./printf7.sub: line 22: printf: 21474836470: Result too large +VAR=[] +./printf7.sub: line 25: printf: 21474836470: Result too large +VAR=[X] +./printf7.sub: line 31: printf: 9223372036854775825: Result too large +[ ] +./printf7.sub: line 32: printf: 9223372036854775825: Result too large +[X] +./printf7.sub: line 34: printf: 9223372036854775825: Result too large +VAR=[ ] +./printf7.sub: line 37: printf: 9223372036854775825: Result too large +VAR=[X] +./printf7.sub: line 43: printf: 21474836470: Result too large +[] +./printf7.sub: line 44: printf: 21474836470: Result too large +[X] +./printf7.sub: line 46: printf: 21474836470: Result too large +VAR=[] +./printf7.sub: line 49: printf: 21474836470: Result too large +VAR=[X] +./printf7.sub: line 55: printf: 9223372036854775825: Result too large +[] +./printf7.sub: line 56: printf: 9223372036854775825: Result too large +[X] +./printf7.sub: line 58: printf: 9223372036854775825: Result too large +VAR=[] +./printf7.sub: line 61: printf: 9223372036854775825: Result too large +VAR=[X] +XY +XY diff --git a/tests/printf.tests b/tests/printf.tests index f334f8b1e..af03960a8 100644 --- a/tests/printf.tests +++ b/tests/printf.tests @@ -77,6 +77,9 @@ printf 'echo %.2Q%Q\n' "$S" "$T" # note the difference # a different way to do it printf 'echo %.*s%q\n' ${#S1} "$S1" "$T" +printf '%.1Q\n' '**' +printf '%.*Q\n' 1 '**' + # make sure the format string is reused to use up arguments printf "%d " 1 2 3 4 5; printf "\n" @@ -341,6 +344,19 @@ printf -v var "%b" @(hugo); echo "x${var}x" # make sure that missing arguments are always handled like the empty string printf "<%3s><%3b>\n" +# other format specifiers with missing arguments +# 0 +printf '%d\n' +# null char +printf '%c\n' + +printf '%x\n' +printf '%4.2f\n' + +printf '%b' +printf '%q\n' +printf '%Q\n' + # let's test some out-of-range integer errors for POSIX-specified behavior TOOBIG=9223372036854775825 TOOSMALL=-9223372036854775815 @@ -348,8 +364,6 @@ TOOSMALL=-9223372036854775815 printf '%d\n' "$TOOBIG" printf '%d\n' "$TOOSMALL" -# invalid variable name - # tests variable assignment with -v ${THIS_SH} ./printf1.sub ${THIS_SH} ./printf2.sub @@ -358,3 +372,4 @@ ${THIS_SH} ./printf4.sub ${THIS_SH} ./printf5.sub # multibyte characters with %ls/%S and %lc/%C ${THIS_SH} ./printf6.sub +${THIS_SH} ./printf7.sub diff --git a/tests/printf6.sub b/tests/printf6.sub index 0dc3b28b1..fbacd4d53 100644 --- a/tests/printf6.sub +++ b/tests/printf6.sub @@ -1,3 +1,24 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# this should echo nothing +printf '%ls' +# this should echo a null byte +printf '%lc' | hexdump -b + +# this should echo a null byte per posix interp 1647 +printf '%lc' '' | hexdump -b + # test %ls and %lc with multibyte characters V=ಇಳಿಕೆಗಳು @@ -19,3 +40,6 @@ printf "%C\n" "$V3" | hexdump -b printf "%4.2lc\n" "$V3" | hexdump -b printf "%-4.2lc---\n" "$V3" | hexdump -b + +# make sure %ls handles 0 precision the same as %s +printf '[%.0s][%.0ls]\n' X X diff --git a/tests/printf7.sub b/tests/printf7.sub new file mode 100644 index 000000000..f1aa856b3 --- /dev/null +++ b/tests/printf7.sub @@ -0,0 +1,68 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# tests of integer overflow for field width and precision arguments +INT_MAX=$(getconf INT_MAX) +TOOBIG=$(( $INT_MAX * 10 )) + +printf '[%*s]\n' "${TOOBIG}" +printf '[%*s]\n' "${TOOBIG}" X + +printf -v VAR '[%*s]' "${TOOBIG}" +echo VAR="$VAR" +unset -v VAR +printf -v VAR '[%*s]' "${TOOBIG}" X +echo VAR="$VAR" +unset -v VAR + +TOOBIG=9223372036854775825 + +printf '[%*s]\n' "${TOOBIG}" +printf '[%*s]\n' "${TOOBIG}" X + +printf -v VAR '[%*s]' "${TOOBIG}" +echo VAR="$VAR" +unset -v VAR +printf -v VAR '[%*s]' "${TOOBIG}" X +echo VAR="$VAR" +unset -v VAR + +TOOBIG=$(( $INT_MAX * 10 )) + +printf '[%.*s]\n' "${TOOBIG}" +printf '[%.*s]\n' "${TOOBIG}" X + +printf -v VAR '[%.*s]' "${TOOBIG}" +echo VAR="$VAR" +unset -v VAR +printf -v VAR '[%.*s]' "${TOOBIG}" X +echo VAR="$VAR" +unset -v VAR + +TOOBIG=9223372036854775825 + +printf '[%.*s]\n' "${TOOBIG}" +printf '[%.*s]\n' "${TOOBIG}" X + +printf -v VAR '[%.*s]' "${TOOBIG}" +echo VAR="$VAR" +unset -v VAR +printf -v VAR '[%.*s]' "${TOOBIG}" X +echo VAR="$VAR" +unset -v VAR + +# out of range inline precisions + +printf "%.${TOOBIG}s\n" XY +printf "%.${TOOBIG}Q\n" XY diff --git a/tests/read.right b/tests/read.right index 9e0f0c9d4..9c7fd33bd 100644 --- a/tests/read.right +++ b/tests/read.right @@ -31,6 +31,8 @@ argv[1] = < foo> argv[1] = argv[1] = argv[1] = < foo> +./read.tests: line 101: b: readonly variable +a = a b = c = stat = 2 a = abcdefg xyz a = xyz diff --git a/tests/read.tests b/tests/read.tests index 5829df01f..c06cd7a76 100644 --- a/tests/read.tests +++ b/tests/read.tests @@ -96,6 +96,15 @@ echo " foo" | { IFS=$' \t\n' ; read line; recho "$line"; } echo " foo" | { IFS=$':' ; read line; recho "$line"; } +# this leaves `b' readonly +readonly b +read a b c < 1 +echo a = $a b = $b c = $c stat = $? + # test read -d delim behavior ${THIS_SH} ./read1.sub diff --git a/tests/run-braces b/tests/run-braces index 554e21839..1045d1f52 100644 --- a/tests/run-braces +++ b/tests/run-braces @@ -1,2 +1,2 @@ -${THIS_SH} ./braces.tests > ${BASH_TSTOUT} +${THIS_SH} ./braces.tests > ${BASH_TSTOUT} 2>&1 diff ${BASH_TSTOUT} braces.right && rm -f ${BASH_TSTOUT} diff --git a/variables.c b/variables.c index 3b95df0ba..48f67774f 100644 --- a/variables.c +++ b/variables.c @@ -3695,7 +3695,9 @@ copy_variable (SHELL_VAR *var) static void dispose_variable_value (SHELL_VAR *var) { - if (function_p (var)) + if (nofree_p (var)) + var_setvalue (var, (char *)NULL); + else if (function_p (var)) dispose_command (function_cell (var)); #if defined (ARRAY_VARS) else if (array_p (var)) @@ -3930,18 +3932,8 @@ makunbound (const char *name, VAR_CONTEXT *vc) 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); -#if defined (ARRAY_VARS) - else if (array_p (old_var)) - array_dispose (array_cell (old_var)); - else if (assoc_p (old_var)) - assoc_dispose (assoc_cell (old_var)); -#endif - else if (nameref_p (old_var)) - FREE (nameref_cell (old_var)); - else - FREE (value_cell (old_var)); + dispose_variable_value (old_var); + #if 0 /* Reset the attributes. Preserve the export attribute if the variable came from a temporary environment. Make sure it stays local, and -- 2.47.3