This file is read.def, from which is created read.c.
It implements the builtin "read" in Bash.
-Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+Copyright (C) 1987-2005 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
$BUILTIN read
$FUNCTION read_builtin
-$SHORT_DOC read [-ers] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
-One line is read from the standard input, and the first word is
-assigned to the first NAME, the second word to the second NAME, and so
-on, with leftover words assigned to the last NAME. Only the characters
-found in $IFS are recognized as word delimiters. If no NAMEs are supplied,
-the line read is stored in the REPLY variable. If the -r option is given,
-this signifies `raw' input, and backslash escaping is disabled. The
--d option causes read to continue until the first character of DELIM is
-read, rather than newline. If the `-p' option is supplied, the string
-PROMPT is output without a trailing newline before attempting to read.
-If -a is supplied, the words read are assigned to sequential indices of
-ARRAY, starting at zero. If -e is supplied and the shell is interactive,
-readline is used to obtain the line. If -n is supplied with a non-zero
-NCHARS argument, read returns after NCHARS characters have been read.
-The -s option causes input coming from a terminal to not be echoed.
+$SHORT_DOC read [-ers] [-u fd] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
+One line is read from the standard input, or from file descriptor FD if the
+-u option is supplied, and the first word is assigned to the first NAME,
+the second word to the second NAME, and so on, with leftover words assigned
+to the last NAME. Only the characters found in $IFS are recognized as word
+delimiters. If no NAMEs are supplied, the line read is stored in the REPLY
+variable. If the -r option is given, this signifies `raw' input, and
+backslash escaping is disabled. The -d option causes read to continue
+until the first character of DELIM is read, rather than newline. If the -p
+option is supplied, the string PROMPT is output without a trailing newline
+before attempting to read. If -a is supplied, the words read are assigned
+to sequential indices of ARRAY, starting at zero. If -e is supplied and
+the shell is interactive, readline is used to obtain the line. If -n is
+supplied with a non-zero NCHARS argument, read returns after NCHARS
+characters have been read. The -s option causes input coming from a
+terminal to not be echoed.
The -t option causes read to time out and return failure if a complete line
-of input is not read within TIMEOUT seconds. The return code is zero,
-unless end-of-file is encountered or read times out.
+of input is not read within TIMEOUT seconds. If the TMOUT variable is set,
+its value is the default timeout. The return code is zero, unless end-of-file
+is encountered, read times out, or an invalid file descriptor is supplied as
+the argument to -u.
$END
#include <config.h>
#include <signal.h>
#include <errno.h>
+#ifdef __CYGWIN__
+# include <fcntl.h>
+# include <io.h>
+#endif
+
+#include "../bashintl.h"
+
#include "../shell.h"
#include "common.h"
#include "bashgetopt.h"
#include <readline/readline.h>
#endif
+#if defined (BUFFERED_INPUT)
+# include "input.h"
+#endif
+
#if !defined(errno)
extern int errno;
#endif
-#define issep(c) (strchr (ifs_chars, (c)))
-
-extern int interrupt_immediately;
-
#if defined (READLINE)
-static char *edit_line ();
-static void set_eol_delim ();
-static void reset_eol_delim ();
+static void reset_attempted_completion_function __P((char *));
+static char *edit_line __P((char *));
+static void set_eol_delim __P((int));
+static void reset_eol_delim __P((char *));
#endif
-static SHELL_VAR *bind_read_variable ();
+static SHELL_VAR *bind_read_variable __P((char *, char *));
+
+static sighandler sigalrm __P((int));
+static void reset_alarm __P((void));
static procenv_t alrmbuf;
static SigHandler *old_alrm;
-static int delim;
+static unsigned char delim;
static sighandler
sigalrm (s)
WORD_LIST *list;
{
register char *varname;
- int size, i, pass_next, saw_escape, eof, opt, retval, code;
- int input_is_tty, input_is_pipe, unbuffered_read;
- int raw, edit, tmout, nchars, silent;
- long timeoutval, ncharsval;
+ int size, i, nr, pass_next, saw_escape, eof, opt, retval, code, print_ps2;
+ int input_is_tty, input_is_pipe, unbuffered_read, skip_ctlesc, skip_ctlnul;
+ int raw, edit, nchars, silent, have_timeout, fd;
+ unsigned int tmout;
+ intmax_t intval;
char c;
char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname;
- char *e, *t, *t1;
+ char *e, *t, *t1, *ps2, *tofree;
struct stat tsb;
SHELL_VAR *var;
#if defined (ARRAY_VARS)
int rlind;
#endif
+ USE_VAR(size);
+ USE_VAR(i);
+ USE_VAR(pass_next);
+ USE_VAR(print_ps2);
+ USE_VAR(saw_escape);
+ USE_VAR(input_is_pipe);
+/* USE_VAR(raw); */
+ USE_VAR(edit);
+ USE_VAR(tmout);
+ USE_VAR(nchars);
+ USE_VAR(silent);
+ USE_VAR(ifs_chars);
+ USE_VAR(prompt);
+ USE_VAR(arrayname);
+#if defined (READLINE)
+ USE_VAR(rlbuf);
+ USE_VAR(rlind);
+#endif
+ USE_VAR(list);
+ USE_VAR(ps2);
+
i = 0; /* Index into the string that we are reading. */
raw = edit = 0; /* Not reading raw input by default. */
silent = 0;
arrayname = prompt = (char *)NULL;
+ fd = 0; /* file descriptor to read from */
#if defined (READLINE)
rlbuf = (char *)0;
rlind = 0;
#endif
- tmout = -1; /* no timeout */
- nchars = input_is_tty = input_is_pipe = unbuffered_read = 0;
+ tmout = 0; /* no timeout */
+ nr = nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0;
delim = '\n'; /* read until newline */
reset_internal_getopt ();
- while ((opt = internal_getopt (list, "erp:a:d:t:n:s")) != -1)
+ while ((opt = internal_getopt (list, "ersa:d:n:p:t:u:")) != -1)
{
switch (opt)
- {
- case 'r':
+ {
+ case 'r':
raw = 1;
break;
case 'p':
break;
#endif
case 't':
- code = legal_number (list_optarg, &timeoutval);
- if (code == 0 || timeoutval < 0)
+ code = legal_number (list_optarg, &intval);
+ if (code == 0 || intval < 0 || intval != (unsigned int)intval)
{
- builtin_error ("%s: invalid timeout specification", list_optarg);
+ builtin_error (_("%s: invalid timeout specification"), list_optarg);
return (EXECUTION_FAILURE);
}
else
- tmout = timeoutval;
+ {
+ have_timeout = 1;
+ tmout = intval;
+ }
break;
case 'n':
- code = legal_number (list_optarg, &ncharsval);
- if (code == 0 || ncharsval < 0)
+ code = legal_number (list_optarg, &intval);
+ if (code == 0 || intval < 0 || intval != (int)intval)
{
- builtin_error ("%s: invalid number specification", list_optarg);
+ sh_invalidnum (list_optarg);
return (EXECUTION_FAILURE);
}
else
- nchars = ncharsval;
+ nchars = intval;
+ break;
+ case 'u':
+ code = legal_number (list_optarg, &intval);
+ if (code == 0 || intval < 0 || intval != (int)intval)
+ {
+ builtin_error (_("%s: invalid file descriptor specification"), list_optarg);
+ return (EXECUTION_FAILURE);
+ }
+ else
+ fd = intval;
+ if (sh_validfd (fd) == 0)
+ {
+ builtin_error (_("%d: invalid file descriptor: %s"), fd, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
break;
case 'd':
delim = *list_optarg;
}
list = loptend;
- /* `read -t 0 var' returns failure immediately. */
- if (tmout == 0)
+ /* `read -t 0 var' returns failure immediately. XXX - should it test
+ whether input is available with select/FIONREAD, and fail if those
+ are unavailable? */
+ if (have_timeout && tmout == 0)
return (EXECUTION_FAILURE);
/* IF IFS is unset, we use the default of " \t\n". */
- var = find_variable ("IFS");
- ifs_chars = var ? value_cell (var) : " \t\n";
- if (ifs_chars == 0) /* XXX */
- ifs_chars = ""; /* XXX */
+ ifs_chars = getifs ();
+ if (ifs_chars == 0) /* XXX - shouldn't happen */
+ ifs_chars = "";
- input_string = xmalloc (size = 128);
+ input_string = (char *)xmalloc (size = 112); /* XXX was 128 */
+
+ /* $TMOUT, if set, is the default timeout for read. */
+ if (have_timeout == 0 && (e = get_string_value ("TMOUT")))
+ {
+ code = legal_number (e, &intval);
+ if (code == 0 || intval < 0 || intval != (unsigned int)intval)
+ tmout = 0;
+ else
+ tmout = intval;
+ }
begin_unwind_frame ("read_builtin");
- add_unwind_protect (xfree, input_string);
-#if defined (READLINE)
- add_unwind_protect (xfree, rlbuf);
+
+#if defined (BUFFERED_INPUT)
+ if (interactive == 0 && default_buffered_input >= 0 && fd_is_bash_input (fd))
+ sync_buffered_stream (default_buffered_input);
#endif
- interrupt_immediately++;
- input_is_tty = isatty (0);
+ input_is_tty = isatty (fd);
if (input_is_tty == 0)
-#ifndef __CYGWIN32__
- input_is_pipe = (lseek (0, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
+#ifndef __CYGWIN__
+ input_is_pipe = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
#else
input_is_pipe = 1;
#endif
edit = silent = 0;
}
+#if defined (READLINE)
+ if (edit)
+ add_unwind_protect (xfree, rlbuf);
+#endif
+
if (prompt && edit == 0)
{
fprintf (stderr, "%s", prompt);
{
/* Turn off the timeout if stdin is a regular file (e.g. from
input redirection). */
- if ((fstat (0, &tsb) < 0) || S_ISREG (tsb.st_mode))
- tmout = -1;
+ if ((fstat (fd, &tsb) < 0) || S_ISREG (tsb.st_mode))
+ tmout = 0;
}
if (tmout > 0)
}
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);
+#endif
alarm (tmout);
}
else
#endif
if (input_is_tty)
- {
+ {
ttsave ();
if (silent)
ttcbreak ();
add_unwind_protect ((Function *)ttrestore, (char *)NULL);
}
+ /* 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 = 1;
+
unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;
- for (eof = 0;;)
+#if defined (__CYGWIN__) && defined (O_TEXT)
+ setmode (0, O_TEXT);
+#endif
+
+ ps2 = 0;
+ for (print_ps2 = eof = retval = 0;;)
{
#if defined (READLINE)
if (edit)
{
#endif
+ if (print_ps2)
+ {
+ if (ps2 == 0)
+ ps2 = get_string_value ("PS2");
+ fprintf (stderr, "%s", ps2 ? ps2 : "");
+ fflush (stderr);
+ print_ps2 = 0;
+ }
+
if (unbuffered_read)
- retval = zread (0, &c, 1);
+ retval = zread (fd, &c, 1);
else
- retval = zreadc (0, &c);
+ retval = zreadc (fd, &c);
if (retval <= 0)
{
#endif
if (i + 2 >= size)
- input_string = xrealloc (input_string, size += 128);
+ {
+ input_string = (char *)xrealloc (input_string, size += 128);
+ remove_unwind_protect ();
+ add_unwind_protect (xfree, input_string);
+ }
/* If the next character is to be accepted verbatim, a backslash
newline pair still disappears from the input. */
if (pass_next)
{
+ pass_next = 0;
if (c == '\n')
- i--; /* back up over the CTLESC */
+ {
+ i--; /* back up over the CTLESC */
+ if (interactive && input_is_tty && raw == 0)
+ print_ps2 = 1;
+ }
else
- input_string[i++] = c;
- pass_next = 0;
+ goto add_char;
continue;
}
continue;
}
- if (c == delim)
+ if ((unsigned char)c == delim)
break;
if (c == CTLESC || c == CTLNUL)
input_string[i++] = CTLESC;
}
+add_char:
input_string[i++] = c;
+ nr++;
- if (nchars > 0 && i >= nchars)
+ if (nchars > 0 && nr >= nchars)
break;
}
input_string[i] = '\0';
+#if 1
+ if (retval < 0)
+ {
+ builtin_error (_("read error: %d: %s"), fd, strerror (errno));
+ run_unwind_frame ("read_builtin");
+ return (EXECUTION_FAILURE);
+ }
+#endif
+
if (tmout > 0)
reset_alarm ();
if (nchars > 0)
rl_num_chars_to_read = 0;
if (delim != '\n')
- reset_eol_delim ();
+ reset_eol_delim ((char *)NULL);
}
else
#endif
ttrestore ();
if (unbuffered_read == 0)
- zsyncfd (0);
+ zsyncfd (fd);
interrupt_immediately--;
+ terminate_immediately = 0;
discard_unwind_frame ("read_builtin");
retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
an assign them to `arrayname' in turn. */
if (arrayname)
{
- var = find_variable (arrayname);
- if (var == 0)
- var = make_new_array_variable (arrayname);
- else if (array_p (var) == 0)
- var = convert_var_to_array (var);
+ if (legal_identifier (arrayname) == 0)
+ {
+ sh_invalidid (arrayname);
+ xfree (input_string);
+ return (EXECUTION_FAILURE);
+ }
- empty_array (array_cell (var));
+ var = find_or_make_array_variable (arrayname, 1);
+ if (var == 0)
+ {
+ xfree (input_string);
+ return EXECUTION_FAILURE; /* readonly or noassign */
+ }
+ array_flush (array_cell (var));
alist = list_string (input_string, ifs_chars, 0);
if (alist)
{
- assign_array_var_from_word_list (var, alist);
+ if (saw_escape)
+ dequote_list (alist);
+ else
+ word_list_remove_quoted_nulls (alist);
+ assign_array_var_from_word_list (var, alist, 0);
dispose_words (alist);
}
xfree (input_string);
so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
same way, but I believe that the difference in behaviors is useful
enough to not do it. Without the bash behavior, there is no way
- to read a line completely without interpretation or modification.
+ to read a line completely without interpretation or modification
+ unless you mess with $IFS (e.g., setting it to the empty string).
If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
if (list == 0)
{
#if 0
orig_input_string = input_string;
- for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && issep(*t); t++)
+ for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
;
input_string = t;
input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
if (saw_escape)
{
t = dequote_string (input_string);
- var = bind_variable ("REPLY", t);
+ var = bind_variable ("REPLY", t, 0);
free (t);
}
else
- var = bind_variable ("REPLY", input_string);
+ var = bind_variable ("REPLY", input_string, 0);
VUNSETATTR (var, att_invisible);
-#if 0
- free (orig_input_string);
-#else
+
free (input_string);
-#endif
return (retval);
}
/* Remove IFS white space at the beginning of the input string. If
$IFS is null, no field splitting is performed. */
- for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && issep(*t); t++)
+ for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
;
input_string = t;
if (legal_identifier (varname) == 0)
#endif
{
- builtin_error ("`%s': not a valid identifier", varname);
- free (orig_input_string);
+ sh_invalidid (varname);
+ xfree (orig_input_string);
return (EXECUTION_FAILURE);
}
{
t1 = dequote_string (t);
var = bind_read_variable (varname, t1);
- free (t1);
+ xfree (t1);
}
else
var = bind_read_variable (varname, t);
FREE (t);
if (var == 0)
{
- free (orig_input_string);
+ xfree (orig_input_string);
return (EXECUTION_FAILURE);
}
if (legal_identifier (list->word->word) == 0)
#endif
{
- builtin_error ("`%s': not a valid identifier", list->word->word);
- free (orig_input_string);
+ sh_invalidid (list->word->word);
+ xfree (orig_input_string);
return (EXECUTION_FAILURE);
}
+#if 0
/* This has to be done this way rather than using string_list
and list_string because Posix.2 says that the last variable gets the
remaining words and their intervening separators. */
input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
+#else
+ /* Check whether or not the number of fields is exactly the same as the
+ number of variables. */
+ tofree = NULL;
+ if (*input_string)
+ {
+ t1 = input_string;
+ t = get_word_from_string (&input_string, ifs_chars, &e);
+ if (*input_string == 0)
+ tofree = input_string = t;
+ else
+ input_string = strip_trailing_ifs_whitespace (t1, ifs_chars, saw_escape);
+ }
+#endif
if (saw_escape)
{
t = dequote_string (input_string);
var = bind_read_variable (list->word->word, t);
- free (t);
+ xfree (t);
}
else
var = bind_read_variable (list->word->word, input_string);
stupidly_hack_special_variables (list->word->word);
+ FREE (tofree);
+
if (var)
VUNSETATTR (var, att_invisible);
- free (orig_input_string);
+ xfree (orig_input_string);
return (retval);
}
{
#if defined (ARRAY_VARS)
if (valid_array_reference (name) == 0)
- {
-#if 0
- if (legal_identifier (name) == 0)
- {
- builtin_error ("`%s': not a valid identifier", name);
- return ((SHELL_VAR *)NULL);
- }
-#endif
- return (bind_variable (name, value));
- }
+ return (bind_variable (name, value, 0));
else
- return (do_array_element_assignment (name, value));
-#else
- return bind_variable (name, value);
-#endif
+ return (assign_array_element (name, value, 0));
+#else /* !ARRAY_VARS */
+ return bind_variable (name, value, 0);
+#endif /* !ARRAY_VARS */
}
#if defined (READLINE)
+static rl_completion_func_t *old_attempted_completion_function = 0;
+
+static void
+reset_attempted_completion_function (cp)
+ char *cp;
+{
+ if (rl_attempted_completion_function == 0 && old_attempted_completion_function)
+ rl_attempted_completion_function = old_attempted_completion_function;
+}
+
static char *
edit_line (p)
char *p;
char *ret;
int len;
- if (!bash_readline_initialized)
+ if (bash_readline_initialized == 0)
initialize_readline ();
+
+ old_attempted_completion_function = rl_attempted_completion_function;
+ rl_attempted_completion_function = (rl_completion_func_t *)NULL;
ret = readline (p);
+ rl_attempted_completion_function = old_attempted_completion_function;
+ old_attempted_completion_function = (rl_completion_func_t *)NULL;
+
if (ret == 0)
return ret;
len = strlen (ret);
- ret = xrealloc (ret, len + 2);
+ ret = (char *)xrealloc (ret, len + 2);
ret[len++] = delim;
ret[len] = '\0';
return ret;
}
static int old_delim_ctype;
-static Function *old_delim_func;
+static rl_command_func_t *old_delim_func;
static int old_newline_ctype;
-static Function *old_newline_func;
+static rl_command_func_t *old_newline_func;
-static int delim_char;
+static unsigned char delim_char;
static void
set_eol_delim (c)
}
static void
-reset_eol_delim (c)
- int c;
+reset_eol_delim (cp)
+ char *cp;
{
Keymap cmap;