Imported from ../bash-3.2.48.tar.gz.
[platform/upstream/bash.git] / builtins / read.def
index d63f27a..afa549e 100644 (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, 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.
 
@@ -23,25 +23,28 @@ $PRODUCES read.c
 
 $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>
@@ -63,6 +66,8 @@ $END
 #  include <io.h>
 #endif
 
+#include "../bashintl.h"
+
 #include "../shell.h"
 #include "common.h"
 #include "bashgetopt.h"
@@ -74,24 +79,28 @@ $END
 #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)
@@ -118,13 +127,14 @@ read_builtin (list)
      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)
@@ -135,22 +145,44 @@ read_builtin (list)
   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)
        {
@@ -174,24 +206,42 @@ read_builtin (list)
          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;
@@ -203,27 +253,40 @@ read_builtin (list)
     }
   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");
-#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
 
-  input_is_tty = isatty (0);
+  input_is_tty = isatty (fd);
   if (input_is_tty == 0)
 #ifndef __CYGWIN__
-    input_is_pipe = (lseek (0, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
+    input_is_pipe = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
 #else
     input_is_pipe = 1;
 #endif
@@ -236,6 +299,11 @@ read_builtin (list)
       edit = silent = 0;
     }
 
+#if defined (READLINE)
+  if (edit)
+    add_unwind_protect (xfree, rlbuf);
+#endif
+
   if (prompt && edit == 0)
     {
       fprintf (stderr, "%s", prompt);
@@ -249,8 +317,8 @@ read_builtin (list)
     {
       /* 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)
@@ -263,6 +331,10 @@ read_builtin (list)
        }
       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);
     }
 
@@ -308,6 +380,7 @@ read_builtin (list)
      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;
 
@@ -315,7 +388,8 @@ read_builtin (list)
   setmode (0, O_TEXT);
 #endif
 
-  for (eof = 0;;)
+  ps2 = 0;
+  for (print_ps2 = eof = retval = 0;;)
     {
 #if defined (READLINE)
       if (edit)
@@ -341,10 +415,19 @@ read_builtin (list)
        {
 #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)
        {
@@ -358,7 +441,7 @@ read_builtin (list)
 
       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);
        }
@@ -367,11 +450,15 @@ read_builtin (list)
         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;
        }
 
@@ -383,7 +470,7 @@ read_builtin (list)
          continue;
        }
 
-      if (c == delim)
+      if ((unsigned char)c == delim)
        break;
 
       if (c == CTLESC || c == CTLNUL)
@@ -392,13 +479,24 @@ read_builtin (list)
          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 ();
 
@@ -410,7 +508,7 @@ read_builtin (list)
          if (nchars > 0)
            rl_num_chars_to_read = 0;
          if (delim != '\n')
-           reset_eol_delim ();
+           reset_eol_delim ((char *)NULL);
        }
       else
 #endif
@@ -421,9 +519,10 @@ read_builtin (list)
     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;
@@ -433,18 +532,29 @@ read_builtin (list)
      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);
@@ -464,7 +574,7 @@ read_builtin (list)
     {
 #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);
@@ -473,11 +583,11 @@ read_builtin (list)
       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);
 
       free (input_string);
@@ -490,7 +600,7 @@ read_builtin (list)
 
   /* 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;
 
@@ -503,8 +613,8 @@ read_builtin (list)
       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);
        }
 
@@ -522,7 +632,7 @@ read_builtin (list)
            {
              t1 = dequote_string (t);
              var = bind_read_variable (varname, t1);
-             free (t1);
+             xfree (t1);
            }
          else
            var = bind_read_variable (varname, t);
@@ -536,7 +646,7 @@ read_builtin (list)
       FREE (t);
       if (var == 0)
        {
-         free (orig_input_string);
+         xfree (orig_input_string);
          return (EXECUTION_FAILURE);
        }
 
@@ -551,28 +661,45 @@ read_builtin (list)
   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);
 }
@@ -583,24 +710,25 @@ bind_read_variable (name, value)
 {
 #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;
@@ -608,24 +736,30 @@ edit_line (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)
@@ -653,8 +787,8 @@ set_eol_delim (c)
 }
 
 static void
-reset_eol_delim (c)
-     int c;
+reset_eol_delim (cp)
+     char *cp;
 {
   Keymap cmap;