|
Packit |
b5e560 |
/* input.c -- functions to perform buffered input with synchronization. */
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Copyright (C) 1992-2009 Free Software Foundation, Inc.
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
This file is part of GNU Bash, the Bourne Again SHell.
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
Bash is free software: you can redistribute it and/or modify
|
|
Packit |
b5e560 |
it under the terms of the GNU General Public License as published by
|
|
Packit |
b5e560 |
the Free Software Foundation, either version 3 of the License, or
|
|
Packit |
b5e560 |
(at your option) any later version.
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
Bash is distributed in the hope that it will be useful,
|
|
Packit |
b5e560 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
b5e560 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
b5e560 |
GNU General Public License for more details.
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
You should have received a copy of the GNU General Public License
|
|
Packit |
b5e560 |
along with Bash. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
b5e560 |
*/
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#include "config.h"
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#include "bashtypes.h"
|
|
Packit |
b5e560 |
#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
|
|
Packit |
b5e560 |
# include <sys/file.h>
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
#include "filecntl.h"
|
|
Packit |
b5e560 |
#include "posixstat.h"
|
|
Packit |
b5e560 |
#include <stdio.h>
|
|
Packit |
b5e560 |
#include <errno.h>
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if defined (HAVE_UNISTD_H)
|
|
Packit |
b5e560 |
# include <unistd.h>
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#include "bashansi.h"
|
|
Packit |
b5e560 |
#include "bashintl.h"
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#include "command.h"
|
|
Packit |
b5e560 |
#include "general.h"
|
|
Packit |
b5e560 |
#include "input.h"
|
|
Packit |
b5e560 |
#include "error.h"
|
|
Packit |
b5e560 |
#include "externs.h"
|
|
Packit |
b5e560 |
#include "quit.h"
|
|
Packit |
b5e560 |
#include "trap.h"
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if !defined (errno)
|
|
Packit |
b5e560 |
extern int errno;
|
|
Packit |
b5e560 |
#endif /* !errno */
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if defined (EAGAIN)
|
|
Packit |
b5e560 |
# define X_EAGAIN EAGAIN
|
|
Packit |
b5e560 |
#else
|
|
Packit |
b5e560 |
# define X_EAGAIN -99
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if defined (EWOULDBLOCK)
|
|
Packit |
b5e560 |
# define X_EWOULDBLOCK EWOULDBLOCK
|
|
Packit |
b5e560 |
#else
|
|
Packit |
b5e560 |
# define X_EWOULDBLOCK -99
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
extern void termsig_handler __P((int));
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Functions to handle reading input on systems that don't restart read(2)
|
|
Packit |
b5e560 |
if a signal is received. */
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
static char localbuf[128];
|
|
Packit |
b5e560 |
static int local_index = 0, local_bufused = 0;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Posix and USG systems do not guarantee to restart read () if it is
|
|
Packit |
b5e560 |
interrupted by a signal. We do the read ourselves, and restart it
|
|
Packit |
b5e560 |
if it returns EINTR. */
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
getc_with_restart (stream)
|
|
Packit |
b5e560 |
FILE *stream;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
unsigned char uc;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
CHECK_TERMSIG;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Try local buffering to reduce the number of read(2) calls. */
|
|
Packit |
b5e560 |
if (local_index == local_bufused || local_bufused == 0)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
while (1)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
QUIT;
|
|
Packit |
b5e560 |
run_pending_traps ();
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
|
|
Packit |
b5e560 |
if (local_bufused > 0)
|
|
Packit |
b5e560 |
break;
|
|
Packit |
b5e560 |
else if (local_bufused == 0)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
local_index = 0;
|
|
Packit |
b5e560 |
return EOF;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
else if (errno == X_EAGAIN || errno == X_EWOULDBLOCK)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (sh_unset_nodelay_mode (fileno (stream)) < 0)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
sys_error (_("cannot reset nodelay mode for fd %d"), fileno (stream));
|
|
Packit |
b5e560 |
local_index = local_bufused = 0;
|
|
Packit |
b5e560 |
return EOF;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
continue;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
else if (errno != EINTR)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
local_index = local_bufused = 0;
|
|
Packit |
b5e560 |
return EOF;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
else if (interrupt_state || terminating_signal) /* QUIT; */
|
|
Packit |
b5e560 |
local_index = local_bufused = 0;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
local_index = 0;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
uc = localbuf[local_index++];
|
|
Packit |
b5e560 |
return uc;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
ungetc_with_restart (c, stream)
|
|
Packit |
b5e560 |
int c;
|
|
Packit |
b5e560 |
FILE *stream;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (local_index == 0 || c == EOF)
|
|
Packit |
b5e560 |
return EOF;
|
|
Packit |
b5e560 |
localbuf[--local_index] = c;
|
|
Packit |
b5e560 |
return c;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if defined (BUFFERED_INPUT)
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* A facility similar to stdio, but input-only. */
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if defined (USING_BASH_MALLOC)
|
|
Packit |
b5e560 |
# define MAX_INPUT_BUFFER_SIZE 8176
|
|
Packit |
b5e560 |
#else
|
|
Packit |
b5e560 |
# define MAX_INPUT_BUFFER_SIZE 8192
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if !defined (SEEK_CUR)
|
|
Packit |
b5e560 |
# define SEEK_CUR 1
|
|
Packit |
b5e560 |
#endif /* !SEEK_CUR */
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#ifdef max
|
|
Packit |
b5e560 |
# undef max
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
|
Packit |
b5e560 |
#ifdef min
|
|
Packit |
b5e560 |
# undef min
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
#define min(a, b) ((a) > (b) ? (b) : (a))
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
extern int interactive_shell;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
int bash_input_fd_changed;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* This provides a way to map from a file descriptor to the buffer
|
|
Packit |
b5e560 |
associated with that file descriptor, rather than just the other
|
|
Packit |
b5e560 |
way around. This is needed so that buffers are managed properly
|
|
Packit |
b5e560 |
in constructs like 3<&4. buffers[x]->b_fd == x -- that is how the
|
|
Packit |
b5e560 |
correspondence is maintained. */
|
|
Packit |
b5e560 |
static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;
|
|
Packit |
b5e560 |
static int nbuffers;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#define ALLOCATE_BUFFERS(n) \
|
|
Packit |
b5e560 |
do { if ((n) >= nbuffers) allocate_buffers (n); } while (0)
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Make sure `buffers' has at least N elements. */
|
|
Packit |
b5e560 |
static void
|
|
Packit |
b5e560 |
allocate_buffers (n)
|
|
Packit |
b5e560 |
int n;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
register int i, orig_nbuffers;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
orig_nbuffers = nbuffers;
|
|
Packit |
b5e560 |
nbuffers = n + 20;
|
|
Packit |
b5e560 |
buffers = (BUFFERED_STREAM **)xrealloc
|
|
Packit |
b5e560 |
(buffers, nbuffers * sizeof (BUFFERED_STREAM *));
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Zero out the new buffers. */
|
|
Packit |
b5e560 |
for (i = orig_nbuffers; i < nbuffers; i++)
|
|
Packit |
b5e560 |
buffers[i] = (BUFFERED_STREAM *)NULL;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Construct and return a BUFFERED_STREAM corresponding to file descriptor
|
|
Packit |
b5e560 |
FD, using BUFFER. */
|
|
Packit |
b5e560 |
static BUFFERED_STREAM *
|
|
Packit |
b5e560 |
make_buffered_stream (fd, buffer, bufsize)
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
char *buffer;
|
|
Packit |
b5e560 |
size_t bufsize;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
bp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
|
|
Packit |
b5e560 |
ALLOCATE_BUFFERS (fd);
|
|
Packit |
b5e560 |
buffers[fd] = bp;
|
|
Packit |
b5e560 |
bp->b_fd = fd;
|
|
Packit |
b5e560 |
bp->b_buffer = buffer;
|
|
Packit |
b5e560 |
bp->b_size = bufsize;
|
|
Packit |
b5e560 |
bp->b_used = bp->b_inputp = bp->b_flag = 0;
|
|
Packit |
b5e560 |
if (bufsize == 1)
|
|
Packit |
b5e560 |
bp->b_flag |= B_UNBUFF;
|
|
Packit |
b5e560 |
if (O_TEXT && (fcntl (fd, F_GETFL) & O_TEXT) != 0)
|
|
Packit |
b5e560 |
bp->b_flag |= B_TEXT;
|
|
Packit |
b5e560 |
return (bp);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */
|
|
Packit |
b5e560 |
static BUFFERED_STREAM *
|
|
Packit |
b5e560 |
copy_buffered_stream (bp)
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
BUFFERED_STREAM *nbp;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (!bp)
|
|
Packit |
b5e560 |
return ((BUFFERED_STREAM *)NULL);
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
nbp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
|
|
Packit |
b5e560 |
xbcopy ((char *)bp, (char *)nbp, sizeof (BUFFERED_STREAM));
|
|
Packit |
b5e560 |
return (nbp);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
set_bash_input_fd (fd)
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (bash_input.type == st_bstream)
|
|
Packit |
b5e560 |
bash_input.location.buffered_fd = fd;
|
|
Packit |
b5e560 |
else if (interactive_shell == 0)
|
|
Packit |
b5e560 |
default_buffered_input = fd;
|
|
Packit |
b5e560 |
return 0;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
fd_is_bash_input (fd)
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (bash_input.type == st_bstream && bash_input.location.buffered_fd == fd)
|
|
Packit |
b5e560 |
return 1;
|
|
Packit |
b5e560 |
else if (interactive_shell == 0 && default_buffered_input == fd)
|
|
Packit |
b5e560 |
return 1;
|
|
Packit |
b5e560 |
return 0;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Save the buffered stream corresponding to file descriptor FD (which bash
|
|
Packit |
b5e560 |
is using to read input) to a buffered stream associated with NEW_FD. If
|
|
Packit |
b5e560 |
NEW_FD is -1, a new file descriptor is allocated with fcntl. The new
|
|
Packit |
b5e560 |
file descriptor is returned on success, -1 on error. */
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
save_bash_input (fd, new_fd)
|
|
Packit |
b5e560 |
int fd, new_fd;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
int nfd;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Sync the stream so we can re-read from the new file descriptor. We
|
|
Packit |
b5e560 |
might be able to avoid this by copying the buffered stream verbatim
|
|
Packit |
b5e560 |
to the new file descriptor. */
|
|
Packit |
b5e560 |
if (buffers[fd])
|
|
Packit |
b5e560 |
sync_buffered_stream (fd);
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Now take care of duplicating the file descriptor that bash is
|
|
Packit |
b5e560 |
using for input, so we can reinitialize it later. */
|
|
Packit |
b5e560 |
nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd;
|
|
Packit |
b5e560 |
if (nfd == -1)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (fcntl (fd, F_GETFD, 0) == 0)
|
|
Packit |
b5e560 |
sys_error (_("cannot allocate new file descriptor for bash input from fd %d"), fd);
|
|
Packit |
b5e560 |
return -1;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (nfd < nbuffers && buffers[nfd])
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
/* What's this? A stray buffer without an associated open file
|
|
Packit |
b5e560 |
descriptor? Free up the buffer and report the error. */
|
|
Packit |
b5e560 |
internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd);
|
|
Packit |
b5e560 |
free_buffered_stream (buffers[nfd]);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Reinitialize bash_input.location. */
|
|
Packit |
b5e560 |
if (bash_input.type == st_bstream)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
bash_input.location.buffered_fd = nfd;
|
|
Packit |
b5e560 |
fd_to_buffered_stream (nfd);
|
|
Packit |
b5e560 |
close_buffered_fd (fd); /* XXX */
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
else
|
|
Packit |
b5e560 |
/* If the current input type is not a buffered stream, but the shell
|
|
Packit |
b5e560 |
is not interactive and therefore using a buffered stream to read
|
|
Packit |
b5e560 |
input (e.g. with an `eval exec 3>output' inside a script), note
|
|
Packit |
b5e560 |
that the input fd has been changed. pop_stream() looks at this
|
|
Packit |
b5e560 |
value and adjusts the input fd to the new value of
|
|
Packit |
b5e560 |
default_buffered_input accordingly. */
|
|
Packit |
b5e560 |
bash_input_fd_changed++;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (default_buffered_input == fd)
|
|
Packit |
b5e560 |
default_buffered_input = nfd;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
SET_CLOSE_ON_EXEC (nfd);
|
|
Packit |
b5e560 |
return nfd;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Check that file descriptor FD is not the one that bash is currently
|
|
Packit |
b5e560 |
using to read input from a script. FD is about to be duplicated onto,
|
|
Packit |
b5e560 |
which means that the kernel will close it for us. If FD is the bash
|
|
Packit |
b5e560 |
input file descriptor, we need to seek backwards in the script (if
|
|
Packit |
b5e560 |
possible and necessary -- scripts read from stdin are still unbuffered),
|
|
Packit |
b5e560 |
allocate a new file descriptor to use for bash input, and re-initialize
|
|
Packit |
b5e560 |
the buffered stream. Make sure the file descriptor used to save bash
|
|
Packit |
b5e560 |
input is set close-on-exec. Returns 0 on success, -1 on failure. This
|
|
Packit |
b5e560 |
works only if fd is > 0 -- if fd == 0 and bash is reading input from
|
|
Packit |
b5e560 |
fd 0, sync_buffered_stream is used instead, to cooperate with input
|
|
Packit |
b5e560 |
redirection (look at redir.c:add_undo_redirect()). */
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
check_bash_input (fd)
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (fd_is_bash_input (fd))
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (fd > 0)
|
|
Packit |
b5e560 |
return ((save_bash_input (fd, -1) == -1) ? -1 : 0);
|
|
Packit |
b5e560 |
else if (fd == 0)
|
|
Packit |
b5e560 |
return ((sync_buffered_stream (fd) == -1) ? -1 : 0);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
return 0;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* This is the buffered stream analogue of dup2(fd1, fd2). The
|
|
Packit |
b5e560 |
BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists.
|
|
Packit |
b5e560 |
BUFFERS[fd1] is copied to BUFFERS[fd2]. This is called by the
|
|
Packit |
b5e560 |
redirect code for constructs like 4<&0 and 3
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
duplicate_buffered_stream (fd1, fd2)
|
|
Packit |
b5e560 |
int fd1, fd2;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
int is_bash_input, m;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (fd1 == fd2)
|
|
Packit |
b5e560 |
return 0;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
m = max (fd1, fd2);
|
|
Packit |
b5e560 |
ALLOCATE_BUFFERS (m);
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* If FD2 is the file descriptor bash is currently using for shell input,
|
|
Packit |
b5e560 |
we need to do some extra work to make sure that the buffered stream
|
|
Packit |
b5e560 |
actually exists (it might not if fd1 was not active, and the copy
|
|
Packit |
b5e560 |
didn't actually do anything). */
|
|
Packit |
b5e560 |
is_bash_input = (bash_input.type == st_bstream) &&
|
|
Packit |
b5e560 |
(bash_input.location.buffered_fd == fd2);
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (buffers[fd2])
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
/* If the two objects share the same b_buffer, don't free it. */
|
|
Packit |
b5e560 |
if (buffers[fd1] && buffers[fd1]->b_buffer && buffers[fd1]->b_buffer == buffers[fd2]->b_buffer)
|
|
Packit |
b5e560 |
buffers[fd2] = (BUFFERED_STREAM *)NULL;
|
|
Packit |
b5e560 |
else
|
|
Packit |
b5e560 |
free_buffered_stream (buffers[fd2]);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
buffers[fd2] = copy_buffered_stream (buffers[fd1]);
|
|
Packit |
b5e560 |
if (buffers[fd2])
|
|
Packit |
b5e560 |
buffers[fd2]->b_fd = fd2;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (is_bash_input)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (!buffers[fd2])
|
|
Packit |
b5e560 |
fd_to_buffered_stream (fd2);
|
|
Packit |
b5e560 |
buffers[fd2]->b_flag |= B_WASBASHINPUT;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
return (fd2);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Return 1 if a seek on FD will succeed. */
|
|
Packit |
b5e560 |
#define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Take FD, a file descriptor, and create and return a buffered stream
|
|
Packit |
b5e560 |
corresponding to it. If something is wrong and the file descriptor
|
|
Packit |
b5e560 |
is invalid, return a NULL stream. */
|
|
Packit |
b5e560 |
BUFFERED_STREAM *
|
|
Packit |
b5e560 |
fd_to_buffered_stream (fd)
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
char *buffer;
|
|
Packit |
b5e560 |
size_t size;
|
|
Packit |
b5e560 |
struct stat sb;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (fstat (fd, &sb) < 0)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
close (fd);
|
|
Packit |
b5e560 |
return ((BUFFERED_STREAM *)NULL);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
|
|
Packit |
b5e560 |
if (size == 0)
|
|
Packit |
b5e560 |
size = 1;
|
|
Packit |
b5e560 |
buffer = (char *)xmalloc (size);
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
return (make_buffered_stream (fd, buffer, size));
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Return a buffered stream corresponding to FILE, a file name. */
|
|
Packit |
b5e560 |
BUFFERED_STREAM *
|
|
Packit |
b5e560 |
open_buffered_stream (file)
|
|
Packit |
b5e560 |
char *file;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
fd = open (file, O_RDONLY);
|
|
Packit |
b5e560 |
return ((fd >= 0) ? fd_to_buffered_stream (fd) : (BUFFERED_STREAM *)NULL);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Deallocate a buffered stream and free up its resources. Make sure we
|
|
Packit |
b5e560 |
zero out the slot in BUFFERS that points to BP. */
|
|
Packit |
b5e560 |
void
|
|
Packit |
b5e560 |
free_buffered_stream (bp)
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
int n;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (!bp)
|
|
Packit |
b5e560 |
return;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
n = bp->b_fd;
|
|
Packit |
b5e560 |
if (bp->b_buffer)
|
|
Packit |
b5e560 |
free (bp->b_buffer);
|
|
Packit |
b5e560 |
free (bp);
|
|
Packit |
b5e560 |
buffers[n] = (BUFFERED_STREAM *)NULL;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Close the file descriptor associated with BP, a buffered stream, and free
|
|
Packit |
b5e560 |
up the stream. Return the status of closing BP's file descriptor. */
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
close_buffered_stream (bp)
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (!bp)
|
|
Packit |
b5e560 |
return (0);
|
|
Packit |
b5e560 |
fd = bp->b_fd;
|
|
Packit |
b5e560 |
free_buffered_stream (bp);
|
|
Packit |
b5e560 |
return (close (fd));
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Deallocate the buffered stream associated with file descriptor FD, and
|
|
Packit |
b5e560 |
close FD. Return the status of the close on FD. */
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
close_buffered_fd (fd)
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (fd < 0)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
errno = EBADF;
|
|
Packit |
b5e560 |
return -1;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
if (fd >= nbuffers || !buffers || !buffers[fd])
|
|
Packit |
b5e560 |
return (close (fd));
|
|
Packit |
b5e560 |
return (close_buffered_stream (buffers[fd]));
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Make the BUFFERED_STREAM associated with buffers[FD] be BP, and return
|
|
Packit |
b5e560 |
the old BUFFERED_STREAM. */
|
|
Packit |
b5e560 |
BUFFERED_STREAM *
|
|
Packit |
b5e560 |
set_buffered_stream (fd, bp)
|
|
Packit |
b5e560 |
int fd;
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
BUFFERED_STREAM *ret;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
ret = buffers[fd];
|
|
Packit |
b5e560 |
buffers[fd] = bp;
|
|
Packit |
b5e560 |
return ret;
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Read a buffer full of characters from BP, a buffered stream. */
|
|
Packit |
b5e560 |
static int
|
|
Packit |
b5e560 |
b_fill_buffer (bp)
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
ssize_t nr;
|
|
Packit |
b5e560 |
off_t o;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
CHECK_TERMSIG;
|
|
Packit |
b5e560 |
/* In an environment where text and binary files are treated differently,
|
|
Packit |
b5e560 |
compensate for lseek() on text files returning an offset different from
|
|
Packit |
b5e560 |
the count of characters read() returns. Text-mode streams have to be
|
|
Packit |
b5e560 |
treated as unbuffered. */
|
|
Packit |
b5e560 |
if ((bp->b_flag & (B_TEXT | B_UNBUFF)) == B_TEXT)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
o = lseek (bp->b_fd, 0, SEEK_CUR);
|
|
Packit |
b5e560 |
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
|
|
Packit |
b5e560 |
if (nr > 0 && nr < lseek (bp->b_fd, 0, SEEK_CUR) - o)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
lseek (bp->b_fd, o, SEEK_SET);
|
|
Packit |
b5e560 |
bp->b_flag |= B_UNBUFF;
|
|
Packit |
b5e560 |
bp->b_size = 1;
|
|
Packit |
b5e560 |
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
else
|
|
Packit |
b5e560 |
nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
|
|
Packit |
b5e560 |
if (nr <= 0)
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
bp->b_used = 0;
|
|
Packit |
b5e560 |
bp->b_buffer[0] = 0;
|
|
Packit |
b5e560 |
if (nr == 0)
|
|
Packit |
b5e560 |
bp->b_flag |= B_EOF;
|
|
Packit |
b5e560 |
else
|
|
Packit |
b5e560 |
bp->b_flag |= B_ERROR;
|
|
Packit |
b5e560 |
return (EOF);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
bp->b_used = nr;
|
|
Packit |
b5e560 |
bp->b_inputp = 0;
|
|
Packit |
b5e560 |
return (bp->b_buffer[bp->b_inputp++] & 0xFF);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Get a character from buffered stream BP. */
|
|
Packit |
b5e560 |
#define bufstream_getc(bp) \
|
|
Packit |
b5e560 |
(bp->b_inputp == bp->b_used || !bp->b_used) \
|
|
Packit |
b5e560 |
? b_fill_buffer (bp) \
|
|
Packit |
b5e560 |
: bp->b_buffer[bp->b_inputp++] & 0xFF
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Push C back onto buffered stream BP. */
|
|
Packit |
b5e560 |
static int
|
|
Packit |
b5e560 |
bufstream_ungetc(c, bp)
|
|
Packit |
b5e560 |
int c;
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (c == EOF || bp->b_inputp == 0)
|
|
Packit |
b5e560 |
return (EOF);
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
bp->b_buffer[--bp->b_inputp] = c;
|
|
Packit |
b5e560 |
return (c);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Seek backwards on file BFD to synchronize what we've read so far
|
|
Packit |
b5e560 |
with the underlying file pointer. */
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
sync_buffered_stream (bfd)
|
|
Packit |
b5e560 |
int bfd;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
off_t chars_left;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (buffers == 0 || (bp = buffers[bfd]) == 0)
|
|
Packit |
b5e560 |
return (-1);
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
chars_left = bp->b_used - bp->b_inputp;
|
|
Packit |
b5e560 |
if (chars_left)
|
|
Packit |
b5e560 |
lseek (bp->b_fd, -chars_left, SEEK_CUR);
|
|
Packit |
b5e560 |
bp->b_used = bp->b_inputp = 0;
|
|
Packit |
b5e560 |
return (0);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
buffered_getchar ()
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
CHECK_TERMSIG;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if !defined (DJGPP)
|
|
Packit |
b5e560 |
return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
|
|
Packit |
b5e560 |
#else
|
|
Packit |
b5e560 |
/* On DJGPP, ignore \r. */
|
|
Packit |
b5e560 |
int ch;
|
|
Packit |
b5e560 |
while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd])) == '\r')
|
|
Packit |
b5e560 |
;
|
|
Packit |
b5e560 |
return ch;
|
|
Packit |
b5e560 |
#endif
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
int
|
|
Packit |
b5e560 |
buffered_ungetchar (c)
|
|
Packit |
b5e560 |
int c;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
return (bufstream_ungetc (c, buffers[bash_input.location.buffered_fd]));
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* Make input come from file descriptor BFD through a buffered stream. */
|
|
Packit |
b5e560 |
void
|
|
Packit |
b5e560 |
with_input_from_buffered_stream (bfd, name)
|
|
Packit |
b5e560 |
int bfd;
|
|
Packit |
b5e560 |
char *name;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
INPUT_STREAM location;
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
location.buffered_fd = bfd;
|
|
Packit |
b5e560 |
/* Make sure the buffered stream exists. */
|
|
Packit |
b5e560 |
bp = fd_to_buffered_stream (bfd);
|
|
Packit |
b5e560 |
init_yy_io (bp == 0 ? return_EOF : buffered_getchar,
|
|
Packit |
b5e560 |
buffered_ungetchar, st_bstream, name, location);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
#if defined (TEST)
|
|
Packit |
b5e560 |
void *
|
|
Packit |
b5e560 |
xmalloc(s)
|
|
Packit |
b5e560 |
int s;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
return (malloc (s));
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
void *
|
|
Packit |
b5e560 |
xrealloc(s, size)
|
|
Packit |
b5e560 |
char *s;
|
|
Packit |
b5e560 |
int size;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
if (!s)
|
|
Packit |
b5e560 |
return(malloc (size));
|
|
Packit |
b5e560 |
else
|
|
Packit |
b5e560 |
return(realloc (s, size));
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
void
|
|
Packit |
b5e560 |
init_yy_io ()
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
process(bp)
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
int c;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
while ((c = bufstream_getc(bp)) != EOF)
|
|
Packit |
b5e560 |
putchar(c);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
BASH_INPUT bash_input;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
struct stat dsb; /* can be used from gdb */
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
/* imitate /bin/cat */
|
|
Packit |
b5e560 |
main(argc, argv)
|
|
Packit |
b5e560 |
int argc;
|
|
Packit |
b5e560 |
char **argv;
|
|
Packit |
b5e560 |
{
|
|
Packit |
b5e560 |
register int i;
|
|
Packit |
b5e560 |
BUFFERED_STREAM *bp;
|
|
Packit |
b5e560 |
|
|
Packit |
b5e560 |
if (argc == 1) {
|
|
Packit |
b5e560 |
bp = fd_to_buffered_stream (0);
|
|
Packit |
b5e560 |
process(bp);
|
|
Packit |
b5e560 |
exit(0);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
for (i = 1; i < argc; i++) {
|
|
Packit |
b5e560 |
if (argv[i][0] == '-' && argv[i][1] == '\0') {
|
|
Packit |
b5e560 |
bp = fd_to_buffered_stream (0);
|
|
Packit |
b5e560 |
if (!bp)
|
|
Packit |
b5e560 |
continue;
|
|
Packit |
b5e560 |
process(bp);
|
|
Packit |
b5e560 |
free_buffered_stream (bp);
|
|
Packit |
b5e560 |
} else {
|
|
Packit |
b5e560 |
bp = open_buffered_stream (argv[i]);
|
|
Packit |
b5e560 |
if (!bp)
|
|
Packit |
b5e560 |
continue;
|
|
Packit |
b5e560 |
process(bp);
|
|
Packit |
b5e560 |
close_buffered_stream (bp);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
exit(0);
|
|
Packit |
b5e560 |
}
|
|
Packit |
b5e560 |
#endif /* TEST */
|
|
Packit |
b5e560 |
#endif /* BUFFERED_INPUT */
|