- 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 <grishalevit@gmail.com>
+ - printf_builtin: handle field width and precision overflow from
+ getint() by ignoring the argument (fieldwidth = 0, precision = -1)
+
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
/* 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
/* 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
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
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))
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
{
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;
wp = getwidestr (&slen);
r = printwidestr (start, wp, slen, fieldwidth, precision);
- free (wp);
+ FREE (wp);
if (r < 0)
PRETURN (EXECUTION_FAILURE);
break;
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 */
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;
ret = INT_MIN;
}
+ garglist = garglist->next;
return ((int)ret);
}
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));
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))
ts = (const wchar_t *)ws;
- if (prec > 0)
+ if (prec >= 0)
{
rsize = prec * MB_CUR_MAX;
ret = (char *)xmalloc (rsize + 1);
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);
/*(*/
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;
#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;
b c
$0
declare -a A=([0]="X=a" [1]="b")
+2 3 4 5 6
FIN1:0
FIN2:0
FIN3:0
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:$?
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
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}
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
recho '\ 1\7f'
recho '\ 1\7f\ 1\7f'
recho '\ 1\ 1\7f'
+
+# tests of backslash-escaped CTLESC
+
+recho "${_+\\ 1}"
+recho ${_+\\ 1}
+recho "${_+\\\ 1}"
+recho ${_+\\\ 1}
+recho "${_+\ 1}"
+recho ${_+\ 1}
+recho "${_+\ 1\ 1}"
+recho ${_+\ 1\ 1}
+
+recho $'\\\1'
+recho "\\\ 1"
+
+recho "\\\ 1\7f"
+recho \\\ 1\7f
+
+recho $'\c\\\001 \c\\\177'
+recho $'\c\ 1 \c\7f'
echo a\\;ls
echo a\'\;ls
echo 'a'\''b'\;ls
+\*
+\*
1 2 3 4 5
onestring 0 0 0
onestring 0 0 0.00
--\"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--
----
----
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 )
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
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
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
# 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"
# 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
printf '%d\n' "$TOOBIG"
printf '%d\n' "$TOOSMALL"
-# invalid variable name
-
# tests variable assignment with -v
${THIS_SH} ./printf1.sub
${THIS_SH} ./printf2.sub
${THIS_SH} ./printf5.sub
# multibyte characters with %ls/%S and %lc/%C
${THIS_SH} ./printf6.sub
+${THIS_SH} ./printf7.sub
+# 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 <http://www.gnu.org/licenses/>.
+#
+# 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=ಇಳಿಕೆಗಳು
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
--- /dev/null
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+# 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
argv[1] = <foo>
argv[1] = <foo>
argv[1] = < foo>
+./read.tests: line 101: b: readonly variable
+a = a b = c = stat = 2
a = abcdefg
xyz
a = xyz
echo " foo" | { IFS=$':' ; read line; recho "$line"; }
+# this leaves `b' readonly
+readonly b
+read a b c <<EOF
+a b c
+EOF
+
+# the latest POSIX draft says $? should be > 1
+echo a = $a b = $b c = $c stat = $?
+
# test read -d delim behavior
${THIS_SH} ./read1.sub
-${THIS_SH} ./braces.tests > ${BASH_TSTOUT}
+${THIS_SH} ./braces.tests > ${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} braces.right && rm -f ${BASH_TSTOUT}
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))
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