This file is read.def, from which is created read.c.
It implements the builtin "read" in Bash.
-Copyright (C) 1987-2009 Free Software Foundation, Inc.
+Copyright (C) 1987-2015 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
$BUILTIN read
$FUNCTION read_builtin
-$SHORT_DOC read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
+$SHORT_DOC read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]
Read a line from the standard input and split it into fields.
Reads a single line from the standard input, or from file descriptor FD
variable ARRAY, starting at zero
-d delim continue until the first character of DELIM is read, rather
than newline
- -e use Readline to obtain the line in an interactive shell
- -i text Use TEXT as the initial text for Readline
+ -e use Readline to obtain the line in an interactive shell
+ -i text use TEXT as the initial text for Readline
-n nchars return after reading NCHARS characters rather than waiting
- for a newline
+ for a newline, but honor a delimiter if fewer than
+ NCHARS characters are read before the delimiter
+ -N nchars return only after reading exactly NCHARS characters, unless
+ EOF is encountered or read times out, ignoring any
+ delimiter
-p prompt output the string PROMPT without a trailing newline before
attempting to read
- -r do not allow backslashes to escape any characters
- -s do not echo input coming from a terminal
- -t timeout time out and return failure if a complete line of input is
- not read withint TIMEOUT seconds. The value of the TMOUT
- variable is the default timeout. TIMEOUT may be a
- fractional number. If TIMEOUT is 0, read returns success only
- if input is available on the specified file descriptor. The
- exit status is greater than 128 if the timeout is exceeded
- -u fd read from file descriptor FD instead of the standard input
+ -r do not allow backslashes to escape any characters
+ -s do not echo input coming from a terminal
+ -t timeout time out and return failure if a complete line of
+ input is not read within TIMEOUT seconds. The value of the
+ TMOUT variable is the default timeout. TIMEOUT may be a
+ fractional number. If TIMEOUT is 0, read returns
+ immediately, without trying to read any data, returning
+ success only if input is available on the specified
+ file descriptor. The exit status is greater than 128
+ if the timeout is exceeded
+ -u fd read from file descriptor FD instead of the standard input
Exit Status:
-The return code is zero, unless end-of-file is encountered, read times out,
+The return code is zero, unless end-of-file is encountered, read times out
+(in which case it's greater than 128), a variable assignment error occurs,
or an invalid file descriptor is supplied as the argument to -u.
$END
# include "input.h"
#endif
+#include "shmbutil.h"
+
#if !defined(errno)
extern int errno;
#endif
+extern void run_pending_traps __P((void));
+
+extern int posixly_correct;
+extern int trapped_signal_received;
+
struct ttsave
{
int fd;
static sighandler sigalrm __P((int));
static void reset_alarm __P((void));
-static procenv_t alrmbuf;
+/* Try this to see what the rest of the shell can do with the information. */
+procenv_t alrmbuf;
+int sigalrm_seen;
+
+static int reading, tty_modified;
static SigHandler *old_alrm;
static unsigned char delim;
+static struct ttsave termsave;
+
+/* In all cases, SIGALRM just sets a flag that we check periodically. This
+ avoids problems with the semi-tricky stuff we do with the xfree of
+ input_string at the top of the unwind-protect list (see below). */
+
+/* Set a flag that CHECK_ALRM can check. This relies on zread calling
+ trap.c:check_signals_and_traps(), which knows about sigalrm_seen and
+ alrmbuf. */
static sighandler
sigalrm (s)
int s;
{
- longjmp (alrmbuf, 1);
+ sigalrm_seen = 1;
}
static void
reset_alarm ()
{
- set_signal_handler (SIGALRM, old_alrm);
+ /* Cancel alarm before restoring signal handler. */
falarm (0, 0);
+ set_signal_handler (SIGALRM, old_alrm);
}
/* Read the value of the shell variables whose names follow.
WORD_LIST *list;
{
register char *varname;
- int size, i, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2;
+ int size, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2;
+ volatile int i;
int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul;
- int raw, edit, nchars, silent, have_timeout, fd;
+ int raw, edit, nchars, silent, have_timeout, ignore_delim, fd, lastsig, t_errno;
unsigned int tmsec, tmusec;
long ival, uval;
intmax_t intval;
struct stat tsb;
SHELL_VAR *var;
TTYSTRUCT ttattrs, ttset;
- struct ttsave termsave;
#if defined (ARRAY_VARS)
WORD_LIST *alist;
#endif
#endif
USE_VAR(list);
USE_VAR(ps2);
+ USE_VAR(lastsig);
+
+ sigalrm_seen = reading = tty_modified = 0;
i = 0; /* Index into the string that we are reading. */
raw = edit = 0; /* Not reading raw input by default. */
tmsec = tmusec = 0; /* no timeout */
nr = nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0;
delim = '\n'; /* read until newline */
+ ignore_delim = 0;
reset_internal_getopt ();
- while ((opt = internal_getopt (list, "ersa:d:i:n:p:t:u:")) != -1)
+ while ((opt = internal_getopt (list, "ersa:d:i:n:p:t:u:N:")) != -1)
{
switch (opt)
{
tmusec = uval;
}
break;
+ case 'N':
+ ignore_delim = 1;
+ delim = -1;
case 'n':
code = legal_number (list_optarg, &intval);
if (code == 0 || intval < 0 || intval != (int)intval)
case 'd':
delim = *list_optarg;
break;
+ CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
return (input_avail (fd) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
#endif
+ /* Convenience: check early whether or not the first of possibly several
+ variable names is a valid identifier, and bail early if so. */
+#if defined (ARRAY_VARS)
+ if (list && legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word, 0) == 0)
+#else
+ if (list && legal_identifier (list->word->word) == 0)
+#endif
+ {
+ sh_invalidid (list->word->word);
+ return (EXECUTION_FAILURE);
+ }
+
+ /* If we're asked to ignore the delimiter, make sure we do. */
+ if (ignore_delim)
+ delim = -1;
+
/* IF IFS is unset, we use the default of " \t\n". */
ifs_chars = getifs ();
if (ifs_chars == 0) /* XXX - shouldn't happen */
ifs_chars = "";
+ /* If we want to read exactly NCHARS chars, don't split on IFS */
+ if (ignore_delim)
+ ifs_chars = "";
for (skip_ctlesc = skip_ctlnul = 0, e = ifs_chars; *e; e++)
skip_ctlesc |= *e == CTLESC, skip_ctlnul |= *e == CTLNUL;
if (tmsec > 0 || tmusec > 0)
{
- code = setjmp (alrmbuf);
+ code = setjmp_nosigs (alrmbuf);
if (code)
{
-#if 0
- run_unwind_frame ("read_builtin");
- return (EXECUTION_FAILURE);
-#else
+ sigalrm_seen = 0;
+ /* Tricky. The top of the unwind-protect stack is the free of
+ input_string. We want to run all the rest and use input_string,
+ so we have to save input_string temporarily, run the unwind-
+ protects, then restore input_string so we can use it later */
+ orig_input_string = 0;
input_string[i] = '\0'; /* make sure it's terminated */
- retval = 128+SIGALRM;;
+ if (i == 0)
+ {
+ t = (char *)xmalloc (1);
+ t[0] = 0;
+ }
+ else
+ t = savestring (input_string);
+
+ run_unwind_frame ("read_builtin");
+ input_string = t;
+ retval = 128+SIGALRM;
goto assign_vars;
-#endif
}
+ if (interactive_shell == 0)
+ initialize_terminating_signals ();
old_alrm = set_signal_handler (SIGALRM, sigalrm);
add_unwind_protect (reset_alarm, (char *)NULL);
#if defined (READLINE)
if (edit)
- add_unwind_protect (reset_attempted_completion_function, (char *)NULL);
+ {
+ add_unwind_protect (reset_attempted_completion_function, (char *)NULL);
+ add_unwind_protect (bashline_reset_event_hook, (char *)NULL);
+ }
#endif
falarm (tmsec, tmusec);
}
i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset);
if (i < 0)
sh_ttyerror (1);
+ tty_modified = 1;
add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
+ if (interactive_shell == 0)
+ initialize_terminating_signals ();
}
}
else if (silent) /* turn off echo but leave term in canonical mode */
if (i < 0)
sh_ttyerror (1);
+ tty_modified = 1;
add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
+ if (interactive_shell == 0)
+ initialize_terminating_signals ();
}
/* This *must* be the top unwind-protect on the stack, so the manipulation
of the unwind-protect stack after the realloc() works right. */
add_unwind_protect (xfree, input_string);
- interrupt_immediately++;
- terminate_immediately++;
- unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
+ CHECK_ALRM;
+ if ((nchars > 0) && (input_is_tty == 0) && ignore_delim) /* read -N */
+ unbuffered_read = 2;
+ else if ((nchars > 0) || (delim != '\n') || input_is_pipe)
+ unbuffered_read = 1;
if (prompt && edit == 0)
{
ps2 = 0;
for (print_ps2 = eof = retval = 0;;)
{
+ CHECK_ALRM;
+
#if defined (READLINE)
if (edit)
{
}
if (rlbuf == 0)
{
+ reading = 1;
rlbuf = edit_line (prompt ? prompt : "", itext);
+ reading = 0;
rlind = 0;
}
if (rlbuf == 0)
print_ps2 = 0;
}
- if (unbuffered_read)
- retval = zread (fd, &c, 1);
+#if 0
+ if (posixly_correct == 0)
+ interrupt_immediately++;
+#endif
+ reading = 1;
+ if (unbuffered_read == 2)
+ retval = posixly_correct ? zreadintr (fd, &c, 1) : zreadn (fd, &c, nchars - nr);
+ else if (unbuffered_read)
+ retval = posixly_correct ? zreadintr (fd, &c, 1) : zread (fd, &c, 1);
else
- retval = zreadc (fd, &c);
+ retval = posixly_correct ? zreadcintr (fd, &c) : zreadc (fd, &c);
+ reading = 0;
+#if 0
+ if (posixly_correct == 0)
+ interrupt_immediately--;
+#endif
if (retval <= 0)
{
+ if (retval < 0 && errno == EINTR)
+ {
+ lastsig = LASTSIG();
+ if (lastsig == 0)
+ lastsig = trapped_signal_received;
+ run_pending_traps (); /* because interrupt_immediately is not set */
+ }
+ else
+ lastsig = 0;
+ if (terminating_signal && tty_modified)
+ ttyrestore (&termsave); /* fix terminal before exiting */
+ CHECK_TERMSIG;
eof = 1;
break;
}
+ CHECK_ALRM;
+ QUIT; /* in case we didn't call check_signals() */
#if defined (READLINE)
}
#endif
+ CHECK_ALRM;
if (i + 4 >= size) /* XXX was i + 2; use i + 4 for multibyte/read_mbchar */
{
- input_string = (char *)xrealloc (input_string, size += 128);
- remove_unwind_protect ();
- add_unwind_protect (xfree, input_string);
+ char *t;
+ t = (char *)xrealloc (input_string, size += 128);
+
+ /* Only need to change unwind-protect if input_string changes */
+ if (t != input_string)
+ {
+ input_string = t;
+ remove_unwind_protect ();
+ add_unwind_protect (xfree, input_string);
+ }
}
/* If the next character is to be accepted verbatim, a backslash
continue;
}
- if ((unsigned char)c == delim)
+ if (ignore_delim == 0 && (unsigned char)c == delim)
break;
+ if (c == '\0' && delim != '\0')
+ continue; /* skip NUL bytes in input */
+
if ((skip_ctlesc == 0 && c == CTLESC) || (skip_ctlnul == 0 && c == CTLNUL))
{
saw_escape++;
add_char:
input_string[i++] = c;
+ CHECK_ALRM;
#if defined (HANDLE_MULTIBYTE)
- if (nchars > 0 && MB_CUR_MAX > 1)
+ if (nchars > 0 && MB_CUR_MAX > 1 && is_basic (c) == 0)
{
input_string[i] = '\0'; /* for simplicity and debugging */
i += read_mbchar (fd, input_string, i, c, unbuffered_read);
break;
}
input_string[i] = '\0';
+ CHECK_ALRM;
+
+#if defined (READLINE)
+ if (edit)
+ free (rlbuf);
+#endif
-#if 1
if (retval < 0)
{
- builtin_error (_("read error: %d: %s"), fd, strerror (errno));
+ t_errno = errno;
+ if (errno != EINTR)
+ builtin_error (_("read error: %d: %s"), fd, strerror (errno));
run_unwind_frame ("read_builtin");
- return (EXECUTION_FAILURE);
+ return ((t_errno != EINTR) ? EXECUTION_FAILURE : 128+lastsig);
}
-#endif
if (tmsec > 0 || tmusec > 0)
reset_alarm ();
if (unbuffered_read == 0)
zsyncfd (fd);
- interrupt_immediately--;
- terminate_immediately--;
discard_unwind_frame ("read_builtin");
retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
xfree (input_string);
return EXECUTION_FAILURE; /* readonly or noassign */
}
+ if (assoc_p (var))
+ {
+ builtin_error (_("%s: cannot convert associative to indexed array"), arrayname);
+ xfree (input_string);
+ return EXECUTION_FAILURE; /* existing associative array */
+ }
+ else if (invisible_p (var))
+ VUNSETATTR (var, att_invisible);
array_flush (array_cell (var));
alist = list_string (input_string, ifs_chars, 0);
}
else
var = bind_variable ("REPLY", input_string, 0);
- VUNSETATTR (var, att_invisible);
+ if (var == 0 || readonly_p (var) || noassign_p (var))
+ retval = EXECUTION_FAILURE;
+ else
+ VUNSETATTR (var, att_invisible);
- free (input_string);
+ xfree (input_string);
return (retval);
}
{
varname = list->word->word;
#if defined (ARRAY_VARS)
- if (legal_identifier (varname) == 0 && valid_array_reference (varname) == 0)
+ if (legal_identifier (varname) == 0 && valid_array_reference (varname, 0) == 0)
#else
if (legal_identifier (varname) == 0)
#endif
xfree (t1);
}
else
- var = bind_read_variable (varname, t);
+ var = bind_read_variable (varname, t ? t : "");
}
else
{
/* Now assign the rest of the line to the last variable argument. */
#if defined (ARRAY_VARS)
- if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word) == 0)
+ if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word, 0) == 0)
#else
if (legal_identifier (list->word->word) == 0)
#endif
if (*input_string == 0)
tofree = input_string = t;
else
- input_string = strip_trailing_ifs_whitespace (t1, ifs_chars, saw_escape);
+ {
+ input_string = strip_trailing_ifs_whitespace (t1, ifs_chars, saw_escape);
+ tofree = t;
+ }
}
#endif
- if (saw_escape)
+ if (saw_escape && input_string && *input_string)
{
t = dequote_string (input_string);
var = bind_read_variable (list->word->word, t);
xfree (t);
}
else
- var = bind_read_variable (list->word->word, input_string);
- stupidly_hack_special_variables (list->word->word);
- FREE (tofree);
+ var = bind_read_variable (list->word->word, input_string ? input_string : "");
if (var)
- VUNSETATTR (var, att_invisible);
+ {
+ stupidly_hack_special_variables (list->word->word);
+ VUNSETATTR (var, att_invisible);
+ }
+ else
+ retval = EXECUTION_FAILURE;
+
+ FREE (tofree);
xfree (orig_input_string);
return (retval);
bind_read_variable (name, value)
char *name, *value;
{
+ SHELL_VAR *v;
+
#if defined (ARRAY_VARS)
- if (valid_array_reference (name) == 0)
- return (bind_variable (name, value, 0));
+ if (valid_array_reference (name, 0) == 0)
+ v = bind_variable (name, value, 0);
else
- return (assign_array_element (name, value, 0));
+ v = assign_array_element (name, value, 0);
#else /* !ARRAY_VARS */
- return bind_variable (name, value, 0);
+ v = bind_variable (name, value, 0);
#endif /* !ARRAY_VARS */
+ return (v == 0 ? v
+ : ((readonly_p (v) || noassign_p (v)) ? (SHELL_VAR *)NULL : v));
}
#if defined (HANDLE_MULTIBYTE)
if (ret == (size_t)-2)
{
ps = ps_back;
+ /* We don't want to be interrupted during a multibyte char read */
if (unbuffered)
r = zread (fd, &c, 1);
else
struct ttsave *ttp;
{
ttsetattr (ttp->fd, ttp->attrs);
+ tty_modified = 0;
+}
+
+void
+read_tty_cleanup ()
+{
+ if (tty_modified)
+ ttyrestore (&termsave);
+}
+
+int
+read_tty_modified ()
+{
+ return (tty_modified);
}
#if defined (READLINE)
old_attempted_completion_function = rl_attempted_completion_function;
rl_attempted_completion_function = (rl_completion_func_t *)NULL;
+ bashline_set_event_hook ();
if (itext)
{
old_startup_hook = rl_startup_hook;
rl_startup_hook = set_itext;
deftext = itext;
}
+
ret = readline (p);
+
rl_attempted_completion_function = old_attempted_completion_function;
old_attempted_completion_function = (rl_completion_func_t *)NULL;
+ bashline_reset_event_hook ();
if (ret == 0)
return ret;