/* redir.c -- Functions to perform input and output redirection. */
-/* Copyright (C) 1997 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2002 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#endif
extern int posixly_correct;
-extern int interactive, interactive_shell;
extern REDIRECT *redirection_undo_list;
extern REDIRECT *exec_redirection_undo_list;
/* Static functions defined and used in this file. */
-static void add_undo_close_redirect ();
-static void add_exec_redirect ();
-static int add_undo_redirect ();
-static int do_redirection_internal ();
-static int expandable_redirection_filename ();
-static int stdin_redirection ();
-
-/* Spare redirector used when translating [N]>&WORD or [N]<&WORD to a new
- redirection and when creating the redirection undo list. */
+static void add_undo_close_redirect __P((int));
+static void add_exec_redirect __P((REDIRECT *));
+static int add_undo_redirect __P((int));
+static int expandable_redirection_filename __P((REDIRECT *));
+static int stdin_redirection __P((enum r_instruction, int));
+static int do_redirection_internal __P((REDIRECT *, int, int, int));
+
+static int write_here_document __P((int, WORD_DESC *));
+static int write_here_string __P((int, WORD_DESC *));
+static int here_document_to_fd __P((WORD_DESC *, enum r_instruction));
+
+static int redir_special_open __P((int, char *, int, int, enum r_instruction));
+static int noclobber_open __P((char *, int, int, enum r_instruction));
+static int redir_open __P((char *, int, int, enum r_instruction));
+
+/* Spare redirector used when translating [N]>&WORD[-] or [N]<&WORD[-] to
+ a new redirection and when creating the redirection undo list. */
static REDIRECTEE rd;
/* Set to errno when a here document cannot be created for some reason.
REDIRECT *temp;
int error;
{
- char *filename;
+ char *filename, *allocname;
int oflags;
- if (expandable_redirection_filename (temp))
+ allocname = 0;
+ if (temp->redirector < 0)
+ /* This can happen when read_token_word encounters overflow, like in
+ exec 4294967297>x */
+ filename = "file descriptor out of range";
+#ifdef EBADF
+ else if (temp->redirector >= 0 && errno == EBADF)
+ {
+ /* If we're dealing with two file descriptors, we have to guess about
+ which one is invalid; in the cases of r_{duplicating,move}_input and
+ r_{duplicating,move}_output we're here because dup2() failed. */
+ switch (temp->instruction)
+ {
+ case r_duplicating_input:
+ case r_duplicating_output:
+ case r_move_input:
+ case r_move_output:
+ filename = allocname = itos (temp->redirectee.dest);
+ break;
+ default:
+ filename = allocname = itos (temp->redirector);
+ break;
+ }
+ }
+#endif
+ else if (expandable_redirection_filename (temp))
{
if (posixly_correct && interactive_shell == 0)
{
oflags = temp->redirectee.filename->flags;
temp->redirectee.filename->flags |= W_NOGLOB;
}
- filename = redirection_expand (temp->redirectee.filename);
+ filename = allocname = redirection_expand (temp->redirectee.filename);
if (posixly_correct && interactive_shell == 0)
- temp->redirectee.filename->flags = oflags;
- if (filename == 0)
- filename = savestring (temp->redirectee.filename->word);
+ temp->redirectee.filename->flags = oflags;
if (filename == 0)
- {
- filename = xmalloc (1);
- filename[0] = '\0';
- }
+ filename = temp->redirectee.filename->word;
}
+ else if (temp->redirectee.dest < 0)
+ filename = "file descriptor out of range";
else
- filename = itos (temp->redirectee.dest);
+ filename = allocname = itos (temp->redirectee.dest);
switch (error)
{
break;
}
- FREE (filename);
+ FREE (allocname);
}
/* Perform the redirections on LIST. If FOR_REAL, then actually make
case r_output_force:
case r_duplicating_input_word:
case r_duplicating_output_word:
+ case r_move_input_word:
+ case r_move_output_word:
return 1;
default:
return (result);
}
+static int
+write_here_string (fd, redirectee)
+ int fd;
+ WORD_DESC *redirectee;
+{
+ char *herestr;
+ int herelen, n, e;
+
+ herestr = expand_string_to_string (redirectee->word, 0);
+ herelen = strlen (herestr);
+
+ n = write (fd, herestr, herelen);
+ if (n == herelen)
+ {
+ n = write (fd, "\n", 1);
+ herelen = 1;
+ }
+ e = errno;
+ free (herestr);
+ if (n != herelen)
+ {
+ if (e == 0)
+ e = ENOSPC;
+ return e;
+ }
+ return 0;
+}
+
/* Write the text of the here document pointed to by REDIRECTEE to the file
descriptor FD, which is already open to a temp file. Return 0 if the
write is successful, otherwise return errno. */
return (errno);
}
else
- return 0;
+ return 0;
}
tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT);
return (fd2);
}
}
- fclose (fp);
dispose_words (tlist);
+ if (fclose (fp) != 0)
+ {
+ if (errno == 0)
+ errno = ENOSPC;
+ return (errno);
+ }
}
return 0;
}
by REDIRECTEE, and return a file descriptor open for reading to the temp
file. Return -1 on any error, and make sure errno is set appropriately. */
static int
-here_document_to_fd (redirectee)
+here_document_to_fd (redirectee, ri)
WORD_DESC *redirectee;
+ enum r_instruction ri;
{
- char filename[24];
+ char *filename;
int r, fd, fd2;
- static int fnum = 0;
- do
- {
- /* Make the filename for the temp file. */
- sprintf (filename, "/tmp/t%d-%d-sh", (int)getpid (), fnum++);
-
- /* Make sure we open it exclusively. */
- fd = open (filename, O_TRUNC | O_WRONLY | O_CREAT | O_EXCL, 0600);
- }
- while (fd < 0 && errno == EEXIST);
+ fd = sh_mktmpfd ("sh-thd", MT_USERANDOM, &filename);
/* If we failed for some reason other than the file existing, abort */
if (fd < 0)
- return (fd);
+ {
+ FREE (filename);
+ return (fd);
+ }
errno = r = 0; /* XXX */
/* write_here_document returns 0 on success, errno on failure. */
if (redirectee->word)
- r = write_here_document (fd, redirectee);
+ r = (ri != r_reading_string) ? write_here_document (fd, redirectee)
+ : write_here_string (fd, redirectee);
if (r)
{
close (fd);
unlink (filename);
+ free (filename);
errno = r;
return (-1);
}
{
r = errno;
unlink (filename);
+ free (filename);
close (fd);
errno = r;
return -1;
if (unlink (filename) < 0)
{
r = errno;
+#if defined (__CYGWIN__)
+ /* Under CygWin 1.1.0, the unlink will fail if the file is
+ open. This hack will allow the previous action of silently
+ ignoring the error, but will still leave the file there. This
+ needs some kind of magic. */
+ if (r == EACCES)
+ return (fd2);
+#endif /* __CYGWIN__ */
close (fd2);
+ free (filename);
errno = r;
return (-1);
}
+ free (filename);
return (fd2);
}
enum r_instruction ri;
{
int fd;
- long lfd;
+#if !defined (HAVE_DEV_FD)
+ intmax_t lfd;
+#endif
fd = -1;
switch (spec)
{
#if !defined (HAVE_DEV_FD)
case RF_DEVFD:
- if (legal_number, filename+8, &lfd)
- fd = fcntl ((int)lfd, F_DUPFD, 10);
+ if (all_digits (filename+8) && legal_number (filename+8, &lfd) && lfd == (int)lfd)
+ {
+ fd = lfd;
+ fd = fcntl (fd, F_DUPFD, 10);
+ }
else
fd = AMBIGUOUS_REDIRECT;
break;
/* If we are in noclobber mode, you are not allowed to overwrite
existing files. Check before opening. */
- if (noclobber && OUTPUT_REDIRECT (ri))
+ if (noclobber && CLOBBERING_REDIRECT (ri))
{
fd = noclobber_open (filename, flags, mode, ri);
if (fd == NOCLOBBER_REDIRECT)
{
WORD_DESC *redirectee;
int redir_fd, fd, redirector, r, oflags;
+ intmax_t lfd;
char *redirectee_word;
enum r_instruction ri;
REDIRECT *new_redirect;
redirector = redirect->redirector;
ri = redirect->instruction;
- if (ri == r_duplicating_input_word || ri == r_duplicating_output_word)
+ if (TRANSLATE_REDIRECT (ri))
{
- /* We have [N]>&WORD or [N]<&WORD. Expand WORD, then translate
+ /* We have [N]>&WORD[-] or [N]<&WORD[-]. Expand WORD, then translate
the redirection into a new one and continue. */
redirectee_word = redirection_expand (redirectee);
+ /* XXX - what to do with [N]<&$w- where w is unset or null? ksh93
+ closes N. */
if (redirectee_word == 0)
return (AMBIGUOUS_REDIRECT);
else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0')
{
- rd.dest = 0L;
+ rd.dest = 0;
new_redirect = make_redirection (redirector, r_close_this, rd);
}
else if (all_digits (redirectee_word))
{
- if (ri == r_duplicating_input_word)
- {
- rd.dest = atol (redirectee_word);
- new_redirect = make_redirection (redirector, r_duplicating_input, rd);
- }
+ if (legal_number (redirectee_word, &lfd) && (int)lfd == lfd)
+ rd.dest = lfd;
else
+ rd.dest = -1; /* XXX */
+ switch (ri)
{
- rd.dest = atol (redirectee_word);
+ case r_duplicating_input_word:
+ new_redirect = make_redirection (redirector, r_duplicating_input, rd);
+ break;
+ case r_duplicating_output_word:
new_redirect = make_redirection (redirector, r_duplicating_output, rd);
+ break;
+ case r_move_input_word:
+ new_redirect = make_redirection (redirector, r_move_input, rd);
+ break;
+ case r_move_output_word:
+ new_redirect = make_redirection (redirector, r_move_output, rd);
+ break;
}
}
else if (ri == r_duplicating_output_word && redirector == 1)
{
- if (posixly_correct == 0)
- {
- rd.filename = make_bare_word (redirectee_word);
- new_redirect = make_redirection (1, r_err_and_out, rd);
- }
- else
- new_redirect = copy_redirect (redirect);
+ rd.filename = make_bare_word (redirectee_word);
+ new_redirect = make_redirection (1, r_err_and_out, rd);
}
else
{
case r_input_output:
case r_output_force:
if (posixly_correct && interactive_shell == 0)
- {
- oflags = redirectee->flags;
- redirectee->flags |= W_NOGLOB;
- }
+ {
+ oflags = redirectee->flags;
+ redirectee->flags |= W_NOGLOB;
+ }
redirectee_word = redirection_expand (redirectee);
if (posixly_correct && interactive_shell == 0)
redirectee->flags = oflags;
free (redirectee_word);
if (fd == NOCLOBBER_REDIRECT)
- return (fd);
+ return (fd);
if (fd < 0)
return (errno);
if (for_real)
{
if (remembering)
- /* Only setup to undo it if the thing to undo is active. */
- if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
- add_undo_redirect (redirector);
- else
- add_undo_close_redirect (redirector);
+ {
+ /* Only setup to undo it if the thing to undo is active. */
+ if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
+ add_undo_redirect (redirector);
+ else
+ add_undo_close_redirect (redirector);
+ }
#if defined (BUFFERED_INPUT)
check_bash_input (redirector);
case r_reading_until:
case r_deblank_reading_until:
+ case r_reading_string:
/* REDIRECTEE is a pointer to a WORD_DESC containing the text of
the new input. Place it in a temporary file. */
if (redirectee)
{
- fd = here_document_to_fd (redirectee);
+ fd = here_document_to_fd (redirectee, ri);
if (fd < 0)
{
if (for_real)
{
if (remembering)
- /* Only setup to undo it if the thing to undo is active. */
- if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
- add_undo_redirect (redirector);
- else
- add_undo_close_redirect (redirector);
+ {
+ /* Only setup to undo it if the thing to undo is active. */
+ if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
+ add_undo_redirect (redirector);
+ else
+ add_undo_close_redirect (redirector);
+ }
#if defined (BUFFERED_INPUT)
check_bash_input (redirector);
case r_duplicating_input:
case r_duplicating_output:
+ case r_move_input:
+ case r_move_output:
if (for_real && (redir_fd != redirector))
{
if (remembering)
- /* Only setup to undo it if the thing to undo is active. */
- if (fcntl (redirector, F_GETFD, 0) != -1)
- add_undo_redirect (redirector);
- else
- add_undo_close_redirect (redirector);
+ {
+ /* Only setup to undo it if the thing to undo is active. */
+ if (fcntl (redirector, F_GETFD, 0) != -1)
+ add_undo_redirect (redirector);
+ else
+ add_undo_close_redirect (redirector);
+ }
#if defined (BUFFERED_INPUT)
check_bash_input (redirector);
#endif
-
/* This is correct. 2>&1 means dup2 (1, 2); */
if (dup2 (redir_fd, redirector) < 0)
return (errno);
#if defined (BUFFERED_INPUT)
- if (ri == r_duplicating_input)
+ if (ri == r_duplicating_input || ri == r_move_input)
duplicate_buffered_stream (redir_fd, redirector);
#endif /* BUFFERED_INPUT */
if (((fcntl (redir_fd, F_GETFD, 0) == 1) || set_clexec) &&
(redirector > 2))
SET_CLOSE_ON_EXEC (redirector);
+
+ /* dup-and-close redirection */
+ if (ri == r_move_input || ri == r_move_output)
+ close (redir_fd);
}
break;
if (new_fd < 0)
{
- sys_error ("redirection error");
+ sys_error ("redirection error: cannot duplicate fd");
return (-1);
}
clexec_flag = fcntl (fd, F_GETFD, 0);
- rd.dest = 0L;
+ rd.dest = 0;
closer = make_redirection (new_fd, r_close_this, rd);
dummy_redirect = copy_redirects (closer);
- rd.dest = (long)new_fd;
- new_redirect = make_redirection (fd, r_duplicating_output, rd);
+ rd.dest = new_fd;
+ if (fd == 0)
+ new_redirect = make_redirection (fd, r_duplicating_input, rd);
+ else
+ new_redirect = make_redirection (fd, r_duplicating_output, rd);
new_redirect->next = closer;
closer->next = redirection_undo_list;
{
REDIRECT *closer;
- rd.dest = 0L;
+ rd.dest = 0;
closer = make_redirection (fd, r_close_this, rd);
closer->next = redirection_undo_list;
redirection_undo_list = closer;
case r_input_output:
case r_reading_until:
case r_deblank_reading_until:
+ case r_reading_string:
return (1);
case r_duplicating_input:
case r_duplicating_input_word: