change `read -d' on a tty when the delimiter is not a newline to set the terminal EOL character instead of putting the terminal into character-at-a-time mode; change some calls to atoi to use strol instead

This commit is contained in:
Chet Ramey
2026-01-30 16:43:46 -05:00
parent b805bbec80
commit 468e98e574
34 changed files with 504 additions and 2161 deletions
+1 -1
View File
@@ -641,7 +641,7 @@ fc_gethnum (char *command, HIST_ENTRY **hlist, int mode)
if (s && DIGIT(*s))
{
n = atoi (s);
n = (int)strtol (s, (char **)NULL, 10);
n *= sign;
/* We want to return something that is an offset to HISTORY_BASE. */
+31 -15
View File
@@ -1,7 +1,7 @@
This file is read.def, from which is created read.c.
It implements the builtin "read" in Bash.
Copyright (C) 1987-2025 Free Software Foundation, Inc.
Copyright (C) 1987-2026 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@@ -249,7 +249,7 @@ read_builtin (WORD_LIST *list)
{
char *varname;
int nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2, nflag;
size_t size;
size_t input_string_size;
volatile int i;
int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul;
int edit, use_bash_completion;
@@ -279,7 +279,7 @@ read_builtin (WORD_LIST *list)
FILE *save_instream;
#endif
USE_VAR(size);
USE_VAR(input_string_size);
USE_VAR(i);
USE_VAR(pass_next);
USE_VAR(print_ps2);
@@ -421,6 +421,8 @@ read_builtin (WORD_LIST *list)
{
int ct; /* change terminal settings */
/* XXX - revisit this now that bash doesn't change terminal settings if
the delimiter is not a newline. */
ct = (nflag || delim != '\n') && isatty (fd);
return (check_read_input (fd, ct) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}
@@ -460,7 +462,7 @@ read_builtin (WORD_LIST *list)
for (skip_ctlesc = skip_ctlnul = 0, e = ifs_chars; *e; e++)
skip_ctlesc |= *e == CTLESC, skip_ctlnul |= *e == CTLNUL;
input_string = (char *)xmalloc (size = 112); /* XXX was 128 */
input_string = (char *)xmalloc (input_string_size = 496); /* XXX was 128 */
input_string[0] = '\0';
pass_next = 0; /* Non-zero signifies last char was backslash. */
@@ -616,13 +618,24 @@ read_builtin (WORD_LIST *list)
#endif
if (input_is_tty)
{
int rc;
/* ttsave() */
termsave.fd = fd;
ttgetattr (fd, &ttattrs);
termsave.attrs = ttattrs;
ttset = ttattrs;
if ((silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset)) < 0)
ttset = ttattrs;
if (nchars > 0)
rc = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset);
else /* delim != '\n' */
{
rc = silent ? tt_setnoecho (&ttset) : 0;
if (rc >= 0)
rc = ttfd_seteol (fd, &ttset, delim);
}
if (rc < 0)
sh_ttyerror (1);
tty_modified = 1;
add_unwind_protect (uw_ttyrestore, &termsave);
@@ -690,12 +703,11 @@ read_builtin (WORD_LIST *list)
/* These only matter if edit == 0 */
if ((nchars > 0) && (input_is_tty == 0) && ignore_delim) /* read -N */
unbuffered_read = 2;
#if 0
else if ((nchars > 0) || (delim != '\n') || input_is_pipe)
#else
else if (((nchars > 0 || delim != '\n') && input_is_tty) || input_is_pipe)
else if ((nchars > 0 && input_is_tty) || input_is_pipe) /* read -n */
unbuffered_read = 1;
#endif
else if (delim != '\n' && input_is_tty) /* read -d */
unbuffered_read = 3;
if (prompt && edit == 0)
{
fprintf (stderr, "%s", prompt);
@@ -759,9 +771,11 @@ read_builtin (WORD_LIST *list)
if (tmsec > 0 || tmusec > 0)
sigprocmask (SIG_SETMASK, &chldset, &prevset);
#endif
if (unbuffered_read == 2)
if (unbuffered_read == 2) /* read -N */
retval = posixly_correct ? zreadintr (fd, &c, 1) : zreadn (fd, &c, nchars - nr);
else if (unbuffered_read)
else if (unbuffered_read == 3) /* read -d on a tty */
retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c);
else if (unbuffered_read) /* read -n or input_is_pipe */
retval = posixly_correct ? zreadintr (fd, &c, 1) : zread (fd, &c, 1);
else
retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c);
@@ -811,10 +825,10 @@ read_builtin (WORD_LIST *list)
check_read_timeout ();
/* XXX -- use i + mb_cur_max (at least 4) for multibyte/read_mbchar */
if (i + (mb_cur_max > 4 ? mb_cur_max : 4) >= size)
if (i + (mb_cur_max > 4 ? mb_cur_max : 4) >= input_string_size)
{
char *x;
x = (char *)xrealloc (input_string, size += 128);
x = (char *)xrealloc (input_string, input_string_size += 512);
#if 0
/* This is, in theory, undefined behavior, since input_string may
@@ -1213,6 +1227,8 @@ read_mbchar (int fd, char *string, int ind, int ch, int delim, int unbuffered)
/* We don't want to be interrupted during a multibyte char read */
if (unbuffered == 2)
r = zreadn (fd, &c, 1);
else if (unbuffered == 3)
r = zreadc (fd, &c);
else if (unbuffered)
r = zread (fd, &c, 1);
else