Imported from ../bash-2.05a.tar.gz.
[platform/upstream/bash.git] / input.c
diff --git a/input.c b/input.c
index c352d2f..9cb5401 100644 (file)
--- a/input.c
+++ b/input.c
@@ -47,7 +47,7 @@ extern int errno;
 /* Functions to handle reading input on systems that don't restart read(2)
    if a signal is received. */
 
-static unsigned char localbuf[128];
+static char localbuf[128];
 static int local_index, local_bufused;
 
 /* Posix and USG systems do not guarantee to restart read () if it is
@@ -57,6 +57,8 @@ int
 getc_with_restart (stream)
      FILE *stream;
 {
+  unsigned char uc;
+
   /* Try local buffering to reduce the number of read(2) calls. */
   if (local_index == local_bufused || local_bufused == 0)
     {
@@ -73,7 +75,8 @@ getc_with_restart (stream)
        }
       local_index = 0;
     }
-  return (localbuf[local_index++]);
+  uc = localbuf[local_index++];
+  return uc;
 }
 
 int
@@ -83,7 +86,8 @@ ungetc_with_restart (c, stream)
 {
   if (local_index == 0 || c == EOF)
     return EOF;
-  return (localbuf[--local_index] = c);
+  localbuf[--local_index] = c;
+  return c;
 }
 
 #if defined (BUFFERED_INPUT)
@@ -100,7 +104,14 @@ ungetc_with_restart (c, stream)
 #  define SEEK_CUR 1
 #endif /* !SEEK_CUR */
 
-extern int return_EOF ();
+#ifdef max
+#  undef max
+#endif
+#define max(a, b)      (((a) > (b)) ? (a) : (b))
+#ifdef min
+#  undef min
+#endif
+#define min(a, b)      ((a) > (b) ? (b) : (a))
 
 extern int interactive_shell;
 
@@ -114,8 +125,6 @@ int bash_input_fd_changed;
 static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;
 static int nbuffers;
 
-#define max(a, b)  (((a) > (b)) ? (a) : (b))
-
 #define ALLOCATE_BUFFERS(n) \
        do { if ((n) >= nbuffers) allocate_buffers (n); } while (0)
 
@@ -173,65 +182,102 @@ copy_buffered_stream (bp)
   return (nbp);
 }
 
+int
+set_bash_input_fd (fd)
+     int fd;
+{
+  if (bash_input.type == st_bstream)
+    bash_input.location.buffered_fd = fd;
+  else if (interactive_shell == 0)
+    default_buffered_input = fd;
+  return 0;
+}
+
+int
+fd_is_bash_input (fd)
+     int fd;
+{
+  if (bash_input.type == st_bstream && bash_input.location.buffered_fd == fd)
+    return 1;
+  else if (interactive_shell == 0 && default_buffered_input == fd)
+    return 1;
+  return 0;
+}
+
+/* Save the buffered stream corresponding to file descriptor FD (which bash
+   is using to read input) to a buffered stream associated with NEW_FD.  If
+   NEW_FD is -1, a new file descriptor is allocated with fcntl.  The new
+   file descriptor is returned on success, -1 on error. */
+int
+save_bash_input (fd, new_fd)
+     int fd, new_fd;
+{
+  int nfd;
+
+  /* Sync the stream so we can re-read from the new file descriptor.  We
+     might be able to avoid this by copying the buffered stream verbatim
+     to the new file descriptor. */
+  if (buffers[fd])
+    sync_buffered_stream (fd);
+
+  /* Now take care of duplicating the file descriptor that bash is
+     using for input, so we can reinitialize it later. */
+  nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd;
+  if (nfd == -1)
+    {
+      if (fcntl (fd, F_GETFD, 0) == 0)
+       sys_error ("cannot allocate new file descriptor for bash input from fd %d", fd);
+      return -1;
+    }
+
+  if (buffers[nfd])
+    {
+      /* What's this?  A stray buffer without an associated open file
+        descriptor?  Free up the buffer and report the error. */
+      internal_error ("check_bash_input: buffer already exists for new fd %d", nfd);
+      free_buffered_stream (buffers[nfd]);
+    }
+
+  /* Reinitialize bash_input.location. */
+  if (bash_input.type == st_bstream)
+    {
+      bash_input.location.buffered_fd = nfd;
+      fd_to_buffered_stream (nfd);
+      close_buffered_fd (fd);  /* XXX */
+    }
+  else
+    /* If the current input type is not a buffered stream, but the shell
+       is not interactive and therefore using a buffered stream to read
+       input (e.g. with an `eval exec 3>output' inside a script), note
+       that the input fd has been changed.  pop_stream() looks at this
+       value and adjusts the input fd to the new value of
+       default_buffered_input accordingly. */
+    bash_input_fd_changed++;
+
+  if (default_buffered_input == fd)
+    default_buffered_input = nfd;
+
+  SET_CLOSE_ON_EXEC (nfd);
+  return nfd;
+}
+
 /* Check that file descriptor FD is not the one that bash is currently
    using to read input from a script.  FD is about to be duplicated onto,
    which means that the kernel will close it for us.  If FD is the bash
    input file descriptor, we need to seek backwards in the script (if
    possible and necessary -- scripts read from stdin are still unbuffered),
    allocate a new file descriptor to use for bash input, and re-initialize
-   the buffered stream. */
+   the buffered stream.  Make sure the file descriptor used to save bash
+   input is set close-on-exec. Returns 0 on success, -1 on failure.  This
+   works only if fd is > 0 -- if fd == 0 and bash is reading input from
+   fd 0, save_bash_input is used instead, to cooperate with input
+   redirection (look at redir.c:add_undo_redirect()). */
 int
 check_bash_input (fd)
      int fd;
 {
-  int nfd;
-
-  if (fd > 0 && ((bash_input.type == st_bstream && bash_input.location.buffered_fd == fd) ||
-                (interactive_shell == 0 && default_buffered_input == fd)))
-    {
-      /* Sync the stream so we can re-read from the new file descriptor.  We
-        might be able to avoid this by copying the buffered stream verbatim
-        to the new file descriptor. */
-      if (buffers[fd])
-       sync_buffered_stream (fd);
-
-      /* Now take care of duplicating the file descriptor that bash is
-        using for input, so we can reinitialize it later. */
-      nfd = fcntl (fd, F_DUPFD, 10);
-      if (nfd == -1)
-       {
-         if (fcntl (fd, F_GETFD, 0) == 0)
-           sys_error ("cannot allocate new file descriptor for bash input from fd %d", fd);
-         return -1;
-       }
-
-      if (buffers[nfd])
-       {
-         /* What's this?  A stray buffer without an associated open file
-            descriptor?  Free up the buffer and report the error. */
-         internal_error ("check_bash_input: buffer already exists for new fd %d", nfd);
-         free_buffered_stream (buffers[nfd]);
-       }
-
-      /* Reinitialize bash_input.location. */
-      if (bash_input.type == st_bstream)
-       {
-         bash_input.location.buffered_fd = nfd;
-         fd_to_buffered_stream (nfd);
-         close_buffered_fd (fd);       /* XXX */
-       }
-      else
-       /* If the current input type is not a buffered stream, but the shell
-          is not interactive and therefore using a buffered stream to read
-          input (e.g. with an `eval exec 3>output' inside a script), note
-          that the input fd has been changed.  pop_stream() looks at this
-          value and adjusts the input fd to the new value of
-          default_buffered_input accordingly. */
-       bash_input_fd_changed++;
-
-      if (default_buffered_input == fd)
-       default_buffered_input = nfd;
-    }
+  if (fd > 0 && fd_is_bash_input (fd))
+    return ((save_bash_input (fd, -1) == -1) ? -1 : 0);
   return 0;
 }
       
@@ -264,18 +310,22 @@ duplicate_buffered_stream (fd1, fd2)
   if (buffers[fd2])
     buffers[fd2]->b_fd = fd2;
 
-  if (is_bash_input && !buffers[fd2])
-    fd_to_buffered_stream (fd2);
+  if (is_bash_input)
+    {
+      if (!buffers[fd2])
+       fd_to_buffered_stream (fd2);
+      buffers[fd2]->b_flag |= B_WASBASHINPUT;
+    }
 
   return (fd2);
 }
 
 /* Return 1 if a seek on FD will succeed. */
-#ifndef __CYGWIN32__
+#ifndef __CYGWIN__
 #  define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
 #else
 #  define fd_is_seekable(fd) 0
-#endif /* __CYGWIN32__ */
+#endif /* __CYGWIN__ */
 
 /* Take FD, a file descriptor, and create and return a buffered stream
    corresponding to it.  If something is wrong and the file descriptor
@@ -294,13 +344,9 @@ fd_to_buffered_stream (fd)
       return ((BUFFERED_STREAM *)NULL);
     }
 
-  if (fd_is_seekable (fd) == 0)
+  size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
+  if (size == 0)
     size = 1;
-  else
-    size = (size_t)((sb.st_size > MAX_INPUT_BUFFER_SIZE)
-                               ? MAX_INPUT_BUFFER_SIZE
-                               : sb.st_size);
-      
   buffer = (char *)xmalloc (size);
 
   return (make_buffered_stream (fd, buffer, size));
@@ -356,6 +402,11 @@ int
 close_buffered_fd (fd)
      int fd;
 {
+  if (fd < 0)
+    {
+      errno = EBADF;
+      return -1;
+    }
   if (fd >= nbuffers || !buffers || !buffers[fd])
     return (close (fd));
   return (close_buffered_stream (buffers[fd]));
@@ -380,16 +431,30 @@ static int
 b_fill_buffer (bp)
      BUFFERED_STREAM *bp;
 {
-  bp->b_used = zread (bp->b_fd, bp->b_buffer, bp->b_size);
-  if (bp->b_used <= 0)
+  ssize_t nr;
+
+  nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
+  if (nr <= 0)
     {
+      bp->b_used = 0;
       bp->b_buffer[0] = 0;
-      if (bp->b_used == 0)
+      if (nr == 0)
        bp->b_flag |= B_EOF;
       else
        bp->b_flag |= B_ERROR;
       return (EOF);
     }
+
+#if defined (__CYGWIN__)
+  /* If on cygwin, translate \r\n to \n. */
+  if (nr >= 2 && bp->b_buffer[nr - 2] == '\r' && bp->b_buffer[nr - 1] == '\n')
+    {
+      bp->b_buffer[nr - 2] = '\n';
+      nr--;
+    }
+#endif
+
+  bp->b_used = nr;
   bp->b_inputp = 0;
   return (bp->b_buffer[bp->b_inputp++] & 0xFF);
 }
@@ -422,9 +487,9 @@ sync_buffered_stream (bfd)
   BUFFERED_STREAM *bp;
   off_t chars_left;
 
-  bp = buffers[bfd];
-  if (!bp)
+  if (buffers == 0 || (bp = buffers[bfd]) == 0)
     return (-1);
+
   chars_left = bp->b_used - bp->b_inputp;
   if (chars_left)
     lseek (bp->b_fd, -chars_left, SEEK_CUR);
@@ -435,7 +500,15 @@ sync_buffered_stream (bfd)
 int
 buffered_getchar ()
 {
+#if !defined (DJGPP)
   return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
+#else
+  /* On DJGPP, ignore \r. */
+  int ch;
+  while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd])) == '\r')
+    ;
+  return ch;
+#endif
 }
 
 int
@@ -462,22 +535,22 @@ with_input_from_buffered_stream (bfd, name)
 }
 
 #if defined (TEST)
-char *
+void *
 xmalloc(s)
 int s;
 {
-       return ((char *)malloc (s));
+       return (malloc (s));
 }
 
-char *
+void *
 xrealloc(s, size)
 char   *s;
 int    size;
 {
        if (!s)
-               return((char *)malloc (size));
+               return(malloc (size));
        else
-               return((char *)realloc (s, size));
+               return(realloc (s, size));
 }
 
 void