📄 redir.c
字号:
/* redir.c -- Functions to perform input and output redirection. *//* Copyright (C) 1997-2009 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash. If not, see <http://www.gnu.org/licenses/>.*/#include "config.h"#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX) #pragma alloca#endif /* _AIX && RISC6000 && !__GNUC__ */#include <stdio.h>#include "bashtypes.h"#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)# include <sys/file.h>#endif#include "filecntl.h"#include "posixstat.h"#if defined (HAVE_UNISTD_H)# include <unistd.h>#endif#include <errno.h>#if !defined (errno)extern int errno;#endif#include "bashansi.h"#include "bashintl.h"#include "memalloc.h"#define NEED_FPURGE_DECL#include "shell.h"#include "flags.h"#include "execute_cmd.h"#include "redir.h"#if defined (BUFFERED_INPUT)# include "input.h"#endif#define SHELL_FD_BASE 10int expanding_redir;extern int posixly_correct;extern int last_command_exit_value;extern REDIRECT *redirection_undo_list;extern REDIRECT *exec_redirection_undo_list;/* Static functions defined and used in this file. */static void add_exec_redirect __P((REDIRECT *));static int add_undo_redirect __P((int, enum r_instruction, int));static int add_undo_close_redirect __P((int));static int expandable_redirection_filename __P((REDIRECT *));static int stdin_redirection __P((enum r_instruction, int));static int undoablefd __P((int));static int do_redirection_internal __P((REDIRECT *, 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));static int redir_varassign __P((REDIRECT *, int));static int redir_varvalue __P((REDIRECT *));/* 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. Used to print a reasonable error message. */static int heredoc_errno;#define REDIRECTION_ERROR(r, e, fd) \do { \ if ((r) < 0) \ { \ if (fd >= 0) \ close (fd); \ last_command_exit_value = EXECUTION_FAILURE;\ return ((e) == 0 ? EINVAL : (e));\ } \} while (0)voidredirection_error (temp, error) REDIRECT *temp; int error;{ char *filename, *allocname; int oflags; allocname = 0; if (temp->rflags & REDIR_VARASSIGN) filename = savestring (temp->redirector.filename->word); else if (temp->redirector.dest < 0) /* This can happen when read_token_word encounters overflow, like in exec 4294967297>x */ filename = _("file descriptor out of range");#ifdef EBADF /* This error can never involve NOCLOBBER */ else if (error != NOCLOBBER_REDIRECT && temp->redirector.dest >= 0 && error == 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; case r_duplicating_input_word: if (temp->redirector.dest == 0) /* Guess */ filename = temp->redirectee.filename->word; /* XXX */ else filename = allocname = itos (temp->redirector.dest); break; case r_duplicating_output_word: if (temp->redirector.dest == 1) /* Guess */ filename = temp->redirectee.filename->word; /* XXX */ else filename = allocname = itos (temp->redirector.dest); break; default: filename = allocname = itos (temp->redirector.dest); break; } }#endif else if (expandable_redirection_filename (temp)) {expandable_filename: if (posixly_correct && interactive_shell == 0) { oflags = temp->redirectee.filename->flags; temp->redirectee.filename->flags |= W_NOGLOB; } filename = allocname = redirection_expand (temp->redirectee.filename); if (posixly_correct && interactive_shell == 0) temp->redirectee.filename->flags = oflags; if (filename == 0) filename = temp->redirectee.filename->word; } else if (temp->redirectee.dest < 0) filename = "file descriptor out of range"; else filename = allocname = itos (temp->redirectee.dest); switch (error) { case AMBIGUOUS_REDIRECT: internal_error (_("%s: ambiguous redirect"), filename); break; case NOCLOBBER_REDIRECT: internal_error (_("%s: cannot overwrite existing file"), filename); break;#if defined (RESTRICTED_SHELL) case RESTRICTED_REDIRECT: internal_error (_("%s: restricted: cannot redirect output"), filename); break;#endif /* RESTRICTED_SHELL */ case HEREDOC_REDIRECT: internal_error (_("cannot create temp file for here-document: %s"), strerror (heredoc_errno)); break; case BADVAR_REDIRECT: internal_error (_("%s: cannot assign fd to variable"), filename); break; default: internal_error ("%s: %s", filename, strerror (error)); break; } FREE (allocname);}/* Perform the redirections on LIST. If flags & RX_ACTIVE, then actually make input and output file descriptors, otherwise just do whatever is neccessary for side effecting. flags & RX_UNDOABLE says to remember how to undo the redirections later, if non-zero. If flags & RX_CLEXEC is non-zero, file descriptors opened in do_redirection () have their close-on-exec flag set. */intdo_redirections (list, flags) REDIRECT *list; int flags;{ int error; REDIRECT *temp; if (flags & RX_UNDOABLE) { if (redirection_undo_list) { dispose_redirects (redirection_undo_list); redirection_undo_list = (REDIRECT *)NULL; } if (exec_redirection_undo_list) dispose_exec_redirects (); } for (temp = list; temp; temp = temp->next) { error = do_redirection_internal (temp, flags); if (error) { redirection_error (temp, error); return (error); } } return (0);}/* Return non-zero if the redirection pointed to by REDIRECT has a redirectee.filename that can be expanded. */static intexpandable_redirection_filename (redirect) REDIRECT *redirect;{ switch (redirect->instruction) { case r_output_direction: case r_appending_to: case r_input_direction: case r_inputa_direction: case r_err_and_out: case r_append_err_and_out: case r_input_output: 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 0; }}/* Expand the word in WORD returning a string. If WORD expands to multiple words (or no words), then return NULL. */char *redirection_expand (word) WORD_DESC *word;{ char *result; WORD_LIST *tlist1, *tlist2; WORD_DESC *w; w = copy_word (word); if (posixly_correct) w->flags |= W_NOSPLIT; tlist1 = make_word_list (w, (WORD_LIST *)NULL); expanding_redir = 1; tlist2 = expand_words_no_vars (tlist1); expanding_redir = 0; dispose_words (tlist1); if (!tlist2 || tlist2->next) { /* We expanded to no words, or to more than a single word. Dispose of the word list and return NULL. */ if (tlist2) dispose_words (tlist2); return ((char *)NULL); } result = string_list (tlist2); /* XXX savestring (tlist2->word->word)? */ dispose_words (tlist2); return (result);}static intwrite_here_string (fd, redirectee) int fd; WORD_DESC *redirectee;{ char *herestr; int herelen, n, e; expanding_redir = 1; herestr = expand_string_to_string (redirectee->word, 0); expanding_redir = 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. */static intwrite_here_document (fd, redirectee) int fd; WORD_DESC *redirectee;{ char *document; int document_len, fd2; FILE *fp; register WORD_LIST *t, *tlist; /* Expand the text if the word that was specified had no quoting. The text that we expand is treated exactly as if it were surrounded by double quotes. */ if (redirectee->flags & W_QUOTED) { document = redirectee->word; document_len = strlen (document); /* Set errno to something reasonable if the write fails. */ if (write (fd, document, document_len) < document_len) { if (errno == 0) errno = ENOSPC; return (errno); } else return 0; } expanding_redir = 1; tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT); expanding_redir = 0; if (tlist) { /* Try using buffered I/O (stdio) and writing a word at a time, letting stdio do the work of buffering for us rather than managing our own strings. Most stdios are not particularly fast, however -- this may need to be reconsidered later. */ if ((fd2 = dup (fd)) < 0 || (fp = fdopen (fd2, "w")) == NULL) { if (fd2 >= 0) close (fd2); return (errno); } errno = 0; for (t = tlist; t; t = t->next) { /* This is essentially the body of string_list_internal expanded inline. */ document = t->word->word; document_len = strlen (document); if (t != tlist) putc (' ', fp); /* separator */ fwrite (document, document_len, 1, fp); if (ferror (fp)) { if (errno == 0) errno = ENOSPC; fd2 = errno; fclose(fp); dispose_words (tlist); return (fd2); } } dispose_words (tlist); if (fclose (fp) != 0) { if (errno == 0) errno = ENOSPC; return (errno); } } return 0;}/* Create a temporary file holding the text of the here document pointed to 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 inthere_document_to_fd (redirectee, ri) WORD_DESC *redirectee; enum r_instruction ri;{ char *filename; int r, fd, fd2; fd = sh_mktmpfd ("sh-thd", MT_USERANDOM|MT_USETMPDIR, &filename); /* If we failed for some reason other than the file existing, abort */ if (fd < 0) { FREE (filename); return (fd); } errno = r = 0; /* XXX */ /* write_here_document returns 0 on success, errno on failure. */ if (redirectee->word) r = (ri != r_reading_string) ? write_here_document (fd, redirectee) : write_here_string (fd, redirectee);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -