diff options
-rw-r--r-- | CWRU/CWRU.chlog | 34 | ||||
-rw-r--r-- | CWRU/CWRU.chlog~ | 33 | ||||
-rw-r--r-- | command.h | 7 | ||||
-rw-r--r-- | command.h~ | 384 | ||||
-rw-r--r-- | copy_cmd.c | 8 | ||||
-rw-r--r-- | ddd | 286 | ||||
-rw-r--r-- | dispose_cmd.c | 4 | ||||
-rw-r--r-- | make_cmd.c | 6 | ||||
-rw-r--r-- | make_cmd.c~ | 5 | ||||
-rw-r--r-- | make_cmd.h | 2 | ||||
-rw-r--r-- | make_cmd.h~ | 70 | ||||
-rw-r--r-- | parse.y | 100 | ||||
-rw-r--r-- | parse.y~ | 102 | ||||
-rw-r--r-- | print_cmd.c | 8 | ||||
-rw-r--r-- | print_cmd.c~ | 29 | ||||
-rw-r--r-- | redir.c | 50 | ||||
-rw-r--r-- | redir.c~ | 1175 |
17 files changed, 2201 insertions, 102 deletions
diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index e55ecd83..d09f8b7c 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -8767,3 +8767,37 @@ variables.c bashline.c - fix to bash_execute_unix_command to avoid possible null pointer dereference if READLINE_LINE or READLINE_POINT is not bound + + 9/11 + ---- +[Prayers for the victimes of 9/11/2001] + +command.h + - add `rflags' member to struct redirect to hold private flags and + state information + - change redirector to a REDIRECTEE instead of int to prepare for + possible future changes + +{copy_cmd,dispose_cmd,make_cmd,print_cmd,redir}.c + - changes resulting from type change of `redirector' member of struct + redirect: change x->redirector to x->redirector.dest and add code + where appropriate to deal with x->redirector.filename + +make_cmd.h + - change extern declaration for make_redirection + +make_cmd.c + - first argument of make_redirection is now a `REDIRECTEE' to prepare + for possible future changes. First arg is now assigned directly to + redirector member instead of assigning int to redirector.dest + +{make_cmd,redir}.c,parse.y + - changes resulting from type change of first argument to + make_redirection from int to REDIRECTEE. In general, changes are + using REDIRECTEE sd and assigning old argument to sd.dest, then + passing sd to make_redirection + +make_cmd.[ch],parse.y + - add fourth argument to make_redirection: flags. Sets initial value + of `rflags' member of struct redirect + - changed all callers of make_redirection to add fourth argument of 0 diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index 61731efe..2004ba18 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -8763,3 +8763,36 @@ builtins/mapfile.def variables.c - fix to valid_exportstr to avoid possible null pointer dereferences pointed out by clang/scan-build + +bashline.c + - fix to bash_execute_unix_command to avoid possible null pointer + dereference if READLINE_LINE or READLINE_POINT is not bound + + 9/11 + ---- +[Prayers for the victimes of 9/11/2001] + +command.h + - add `rflags' member to struct redirect to hold private flags and + state information + - change redirector to a REDIRECTEE instead of int to prepare for + possible future changes + +{copy_cmd,dispose_cmd,make_cmd,print_cmd,redir}.c + - changes resulting from type change of `redirector' member of struct + redirect: change x->redirector to x->redirector.dest and add code + where appropriate to deal with x->redirector.filename + +make_cmd.h + - change extern declaration for make_redirection + +make_cmd.c + - first argument of make_redirection is now a `REDIRECTEE' to prepare + for possible future changes. First arg is now assigned directly to + redirector member instead of assigning int to redirector.dest + +{make_cmd,redir}.c,parse.y + - changes resulting from type change of first argument to + make_redirection from int to REDIRECTEE. In general, changes are + using REDIRECTEE sd and assigning old argument to sd.dest, then + passing sd to make_redirection @@ -35,11 +35,15 @@ enum r_instruction { r_append_err_and_out }; +/* Redirection flags; values for rflags */ +#define REDIR_VARASSIGN 0x01 + /* Redirection errors. */ #define AMBIGUOUS_REDIRECT -1 #define NOCLOBBER_REDIRECT -2 #define RESTRICTED_REDIRECT -3 /* can only happen in restricted shells. */ #define HEREDOC_REDIRECT -4 /* here-doc temp file can't be created */ +#define BADVAR_REDIRECT -5 /* something wrong with {varname}redir */ #define CLOBBERING_REDIRECT(ri) \ (ri == r_output_direction || ri == r_err_and_out) @@ -135,7 +139,8 @@ typedef union { (or translator in redir.c) encountered an out-of-range file descriptor. */ typedef struct redirect { struct redirect *next; /* Next element, or NULL. */ - int redirector; /* Descriptor to be redirected. */ + REDIRECTEE redirector; /* Descriptor or varname to be redirected. */ + int rflags; /* Private flags for this redirection */ int flags; /* Flag value for `open'. */ enum r_instruction instruction; /* What to do with the information. */ REDIRECTEE redirectee; /* File descriptor or filename */ diff --git a/command.h~ b/command.h~ new file mode 100644 index 00000000..4ae1c712 --- /dev/null +++ b/command.h~ @@ -0,0 +1,384 @@ +/* command.h -- The structures used internally to represent commands, and + the extern declarations of the functions used to create them. */ + +/* Copyright (C) 1993-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/>. +*/ + +#if !defined (_COMMAND_H_) +#define _COMMAND_H_ + +#include "stdc.h" + +/* Instructions describing what kind of thing to do for a redirection. */ +enum r_instruction { + r_output_direction, r_input_direction, r_inputa_direction, + r_appending_to, r_reading_until, r_reading_string, + r_duplicating_input, r_duplicating_output, r_deblank_reading_until, + r_close_this, r_err_and_out, r_input_output, r_output_force, + r_duplicating_input_word, r_duplicating_output_word, + r_move_input, r_move_output, r_move_input_word, r_move_output_word, + r_append_err_and_out +}; + +/* Redirection flags; values for rflags */ +#define REDIR_VARASSIGN 0x01 + +/* Redirection errors. */ +#define AMBIGUOUS_REDIRECT -1 +#define NOCLOBBER_REDIRECT -2 +#define RESTRICTED_REDIRECT -3 /* can only happen in restricted shells. */ +#define HEREDOC_REDIRECT -4 /* here-doc temp file can't be created */ +#define BADVAR_REDIRECT -5 /* something wrong with {varname}redir + +#define CLOBBERING_REDIRECT(ri) \ + (ri == r_output_direction || ri == r_err_and_out) + +#define OUTPUT_REDIRECT(ri) \ + (ri == r_output_direction || ri == r_input_output || ri == r_err_and_out || ri == r_append_err_and_out) + +#define INPUT_REDIRECT(ri) \ + (ri == r_input_direction || ri == r_inputa_direction || ri == r_input_output) + +#define WRITE_REDIRECT(ri) \ + (ri == r_output_direction || \ + ri == r_input_output || \ + ri == r_err_and_out || \ + ri == r_appending_to || \ + ri == r_append_err_and_out || \ + ri == r_output_force) + +/* redirection needs translation */ +#define TRANSLATE_REDIRECT(ri) \ + (ri == r_duplicating_input_word || ri == r_duplicating_output_word || \ + ri == r_move_input_word || ri == r_move_output_word) + +/* Command Types: */ +enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select, + cm_connection, cm_function_def, cm_until, cm_group, + cm_arith, cm_cond, cm_arith_for, cm_subshell, cm_coproc }; + +/* Possible values for the `flags' field of a WORD_DESC. */ +#define W_HASDOLLAR 0x000001 /* Dollar sign present. */ +#define W_QUOTED 0x000002 /* Some form of quote character is present. */ +#define W_ASSIGNMENT 0x000004 /* This word is a variable assignment. */ +#define W_GLOBEXP 0x000008 /* This word is the result of a glob expansion. */ +#define W_NOSPLIT 0x000010 /* Do not perform word splitting on this word. */ +#define W_NOGLOB 0x000020 /* Do not perform globbing on this word. */ +#define W_NOSPLIT2 0x000040 /* Don't split word except for $@ expansion. */ +#define W_TILDEEXP 0x000080 /* Tilde expand this assignment word */ +#define W_DOLLARAT 0x000100 /* $@ and its special handling */ +#define W_DOLLARSTAR 0x000200 /* $* and its special handling */ +#define W_NOCOMSUB 0x000400 /* Don't perform command substitution on this word */ +#define W_ASSIGNRHS 0x000800 /* Word is rhs of an assignment statement */ +#define W_NOTILDE 0x001000 /* Don't perform tilde expansion on this word */ +#define W_ITILDE 0x002000 /* Internal flag for word expansion */ +#define W_NOEXPAND 0x004000 /* Don't expand at all -- do quote removal */ +#define W_COMPASSIGN 0x008000 /* Compound assignment */ +#define W_ASSNBLTIN 0x010000 /* word is a builtin command that takes assignments */ +#define W_ASSIGNARG 0x020000 /* word is assignment argument to command */ +#define W_HASQUOTEDNULL 0x040000 /* word contains a quoted null character */ +#define W_DQUOTE 0x080000 /* word should be treated as if double-quoted */ +#define W_NOPROCSUB 0x100000 /* don't perform process substitution */ +#define W_HASCTLESC 0x200000 /* word contains literal CTLESC characters */ +#define W_ASSIGNASSOC 0x400000 /* word looks like associative array assignment */ + +/* Possible values for subshell_environment */ +#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */ +#define SUBSHELL_PAREN 0x02 /* subshell caused by ( ... ) */ +#define SUBSHELL_COMSUB 0x04 /* subshell caused by `command` or $(command) */ +#define SUBSHELL_FORK 0x08 /* subshell caused by executing a disk command */ +#define SUBSHELL_PIPE 0x10 /* subshell from a pipeline element */ +#define SUBSHELL_PROCSUB 0x20 /* subshell caused by <(command) or >(command) */ +#define SUBSHELL_COPROC 0x40 /* subshell from a coproc pipeline */ + +/* A structure which represents a word. */ +typedef struct word_desc { + char *word; /* Zero terminated string. */ + int flags; /* Flags associated with this word. */ +} WORD_DESC; + +/* A linked list of words. */ +typedef struct word_list { + struct word_list *next; + WORD_DESC *word; +} WORD_LIST; + + +/* **************************************************************** */ +/* */ +/* Shell Command Structs */ +/* */ +/* **************************************************************** */ + +/* What a redirection descriptor looks like. If the redirection instruction + is ri_duplicating_input or ri_duplicating_output, use DEST, otherwise + use the file in FILENAME. Out-of-range descriptors are identified by a + negative DEST. */ + +typedef union { + int dest; /* Place to redirect REDIRECTOR to, or ... */ + WORD_DESC *filename; /* filename to redirect to. */ +} REDIRECTEE; + +/* Structure describing a redirection. If REDIRECTOR is negative, the parser + (or translator in redir.c) encountered an out-of-range file descriptor. */ +typedef struct redirect { + struct redirect *next; /* Next element, or NULL. */ + REDIRECTEE redirector; /* Descriptor or varname to be redirected. */ + int rflags; /* Private flags for this redirection */ + int flags; /* Flag value for `open'. */ + enum r_instruction instruction; /* What to do with the information. */ + REDIRECTEE redirectee; /* File descriptor or filename */ + char *here_doc_eof; /* The word that appeared in <<foo. */ +} REDIRECT; + +/* An element used in parsing. A single word or a single redirection. + This is an ephemeral construct. */ +typedef struct element { + WORD_DESC *word; + REDIRECT *redirect; +} ELEMENT; + +/* Possible values for command->flags. */ +#define CMD_WANT_SUBSHELL 0x01 /* User wants a subshell: ( command ) */ +#define CMD_FORCE_SUBSHELL 0x02 /* Shell needs to force a subshell. */ +#define CMD_INVERT_RETURN 0x04 /* Invert the exit value. */ +#define CMD_IGNORE_RETURN 0x08 /* Ignore the exit value. For set -e. */ +#define CMD_NO_FUNCTIONS 0x10 /* Ignore functions during command lookup. */ +#define CMD_INHIBIT_EXPANSION 0x20 /* Do not expand the command words. */ +#define CMD_NO_FORK 0x40 /* Don't fork; just call execve */ +#define CMD_TIME_PIPELINE 0x80 /* Time a pipeline */ +#define CMD_TIME_POSIX 0x100 /* time -p; use POSIX.2 time output spec. */ +#define CMD_AMPERSAND 0x200 /* command & */ +#define CMD_STDIN_REDIR 0x400 /* async command needs implicit </dev/null */ +#define CMD_COMMAND_BUILTIN 0x0800 /* command executed by `command' builtin */ +#define CMD_COPROC_SUBSHELL 0x1000 + +/* What a command looks like. */ +typedef struct command { + enum command_type type; /* FOR CASE WHILE IF CONNECTION or SIMPLE. */ + int flags; /* Flags controlling execution environment. */ + int line; /* line number the command starts on */ + REDIRECT *redirects; /* Special redirects for FOR CASE, etc. */ + union { + struct for_com *For; + struct case_com *Case; + struct while_com *While; + struct if_com *If; + struct connection *Connection; + struct simple_com *Simple; + struct function_def *Function_def; + struct group_com *Group; +#if defined (SELECT_COMMAND) + struct select_com *Select; +#endif +#if defined (DPAREN_ARITHMETIC) + struct arith_com *Arith; +#endif +#if defined (COND_COMMAND) + struct cond_com *Cond; +#endif +#if defined (ARITH_FOR_COMMAND) + struct arith_for_com *ArithFor; +#endif + struct subshell_com *Subshell; + struct coproc_com *Coproc; + } value; +} COMMAND; + +/* Structure used to represent the CONNECTION type. */ +typedef struct connection { + int ignore; /* Unused; simplifies make_command (). */ + COMMAND *first; /* Pointer to the first command. */ + COMMAND *second; /* Pointer to the second command. */ + int connector; /* What separates this command from others. */ +} CONNECTION; + +/* Structures used to represent the CASE command. */ + +/* Values for FLAGS word in a PATTERN_LIST */ +#define CASEPAT_FALLTHROUGH 0x01 +#define CASEPAT_TESTNEXT 0x02 + +/* Pattern/action structure for CASE_COM. */ +typedef struct pattern_list { + struct pattern_list *next; /* Clause to try in case this one failed. */ + WORD_LIST *patterns; /* Linked list of patterns to test. */ + COMMAND *action; /* Thing to execute if a pattern matches. */ + int flags; +} PATTERN_LIST; + +/* The CASE command. */ +typedef struct case_com { + int flags; /* See description of CMD flags. */ + int line; /* line number the `case' keyword appears on */ + WORD_DESC *word; /* The thing to test. */ + PATTERN_LIST *clauses; /* The clauses to test against, or NULL. */ +} CASE_COM; + +/* FOR command. */ +typedef struct for_com { + int flags; /* See description of CMD flags. */ + int line; /* line number the `for' keyword appears on */ + WORD_DESC *name; /* The variable name to get mapped over. */ + WORD_LIST *map_list; /* The things to map over. This is never NULL. */ + COMMAND *action; /* The action to execute. + During execution, NAME is bound to successive + members of MAP_LIST. */ +} FOR_COM; + +#if defined (ARITH_FOR_COMMAND) +typedef struct arith_for_com { + int flags; + int line; /* generally used for error messages */ + WORD_LIST *init; + WORD_LIST *test; + WORD_LIST *step; + COMMAND *action; +} ARITH_FOR_COM; +#endif + +#if defined (SELECT_COMMAND) +/* KSH SELECT command. */ +typedef struct select_com { + int flags; /* See description of CMD flags. */ + int line; /* line number the `select' keyword appears on */ + WORD_DESC *name; /* The variable name to get mapped over. */ + WORD_LIST *map_list; /* The things to map over. This is never NULL. */ + COMMAND *action; /* The action to execute. + During execution, NAME is bound to the member of + MAP_LIST chosen by the user. */ +} SELECT_COM; +#endif /* SELECT_COMMAND */ + +/* IF command. */ +typedef struct if_com { + int flags; /* See description of CMD flags. */ + COMMAND *test; /* Thing to test. */ + COMMAND *true_case; /* What to do if the test returned non-zero. */ + COMMAND *false_case; /* What to do if the test returned zero. */ +} IF_COM; + +/* WHILE command. */ +typedef struct while_com { + int flags; /* See description of CMD flags. */ + COMMAND *test; /* Thing to test. */ + COMMAND *action; /* Thing to do while test is non-zero. */ +} WHILE_COM; + +#if defined (DPAREN_ARITHMETIC) +/* The arithmetic evaluation command, ((...)). Just a set of flags and + a WORD_LIST, of which the first element is the only one used, for the + time being. */ +typedef struct arith_com { + int flags; + int line; + WORD_LIST *exp; +} ARITH_COM; +#endif /* DPAREN_ARITHMETIC */ + +/* The conditional command, [[...]]. This is a binary tree -- we slippped + a recursive-descent parser into the YACC grammar to parse it. */ +#define COND_AND 1 +#define COND_OR 2 +#define COND_UNARY 3 +#define COND_BINARY 4 +#define COND_TERM 5 +#define COND_EXPR 6 + +typedef struct cond_com { + int flags; + int line; + int type; + WORD_DESC *op; + struct cond_com *left, *right; +} COND_COM; + +/* The "simple" command. Just a collection of words and redirects. */ +typedef struct simple_com { + int flags; /* See description of CMD flags. */ + int line; /* line number the command starts on */ + WORD_LIST *words; /* The program name, the arguments, + variable assignments, etc. */ + REDIRECT *redirects; /* Redirections to perform. */ +} SIMPLE_COM; + +/* The "function definition" command. */ +typedef struct function_def { + int flags; /* See description of CMD flags. */ + int line; /* Line number the function def starts on. */ + WORD_DESC *name; /* The name of the function. */ + COMMAND *command; /* The parsed execution tree. */ + char *source_file; /* file in which function was defined, if any */ +} FUNCTION_DEF; + +/* A command that is `grouped' allows pipes and redirections to affect all + commands in the group. */ +typedef struct group_com { + int ignore; /* See description of CMD flags. */ + COMMAND *command; +} GROUP_COM; + +typedef struct subshell_com { + int flags; + COMMAND *command; +} SUBSHELL_COM; + +#define COPROC_RUNNING 0x01 +#define COPROC_DEAD 0x02 + +typedef struct coproc { + char *c_name; + pid_t c_pid; + int c_rfd; + int c_wfd; + int c_rsave; + int c_wsave; + int c_flags; + int c_status; +} Coproc; + +typedef struct coproc_com { + int flags; + char *name; + COMMAND *command; +} COPROC_COM; + +extern COMMAND *global_command; +extern Coproc sh_coproc; + +/* Possible command errors */ +#define CMDERR_DEFAULT 0 +#define CMDERR_BADTYPE 1 +#define CMDERR_BADCONN 2 +#define CMDERR_BADJUMP 3 + +#define CMDERR_LAST 3 + +/* Forward declarations of functions declared in copy_cmd.c. */ + +extern FUNCTION_DEF *copy_function_def_contents __P((FUNCTION_DEF *, FUNCTION_DEF *)); +extern FUNCTION_DEF *copy_function_def __P((FUNCTION_DEF *)); + +extern WORD_DESC *copy_word __P((WORD_DESC *)); +extern WORD_LIST *copy_word_list __P((WORD_LIST *)); +extern REDIRECT *copy_redirect __P((REDIRECT *)); +extern REDIRECT *copy_redirects __P((REDIRECT *)); +extern COMMAND *copy_command __P((COMMAND *)); + +#endif /* _COMMAND_H_ */ @@ -113,7 +113,15 @@ copy_redirect (redirect) REDIRECT *new_redirect; new_redirect = (REDIRECT *)xmalloc (sizeof (REDIRECT)); +#if 0 FASTCOPY ((char *)redirect, (char *)new_redirect, (sizeof (REDIRECT))); +#else + *new_redirect = *redirect; /* let the compiler do the fast structure copy */ +#endif + + if (redirect->rflags & REDIR_VARASSIGN) + new_redirect->redirector.filename = copy_word (redirect->redirector.filename); + switch (redirect->instruction) { case r_reading_until: @@ -0,0 +1,286 @@ +*** ../bash-20090903/redir.c 2009-08-17 17:46:34.000000000 -0400 +--- redir.c 2009-09-11 17:29:54.000000000 -0400 +*************** +*** 99,111 **** + int oflags; + + 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 + /* This error can never involve NOCLOBBER */ +! else if (error != NOCLOBBER_REDIRECT && temp->redirector >= 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 +--- 99,113 ---- + 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 +*************** +*** 119,125 **** + filename = allocname = itos (temp->redirectee.dest); + break; + default: +! filename = allocname = itos (temp->redirector); + break; + } + } +--- 121,127 ---- + filename = allocname = itos (temp->redirectee.dest); + break; + default: +! filename = allocname = itos (temp->redirector.dest); + break; + } + } +*************** +*** 162,167 **** +--- 164,173 ---- + internal_error (_("cannot create temp file for here-document: %s"), strerror (heredoc_errno)); + break; + ++ case BADVAR_REDIRECT: ++ internal_error (_("cannot assign fd to variable %s"), filename); ++ break; ++ + default: + internal_error ("%s: %s", filename, strerror (error)); + break; +*************** +*** 649,658 **** + char *redirectee_word; + enum r_instruction ri; + REDIRECT *new_redirect; + + redirectee = redirect->redirectee.filename; + redir_fd = redirect->redirectee.dest; +! redirector = redirect->redirector; + ri = redirect->instruction; + + if (redirect->flags & RX_INTERNAL) +--- 655,665 ---- + char *redirectee_word; + enum r_instruction ri; + REDIRECT *new_redirect; ++ REDIRECTEE sd; + + redirectee = redirect->redirectee.filename; + redir_fd = redirect->redirectee.dest; +! redirector = redirect->redirector.dest; + ri = redirect->instruction; + + if (redirect->flags & RX_INTERNAL) +*************** +*** 670,680 **** + return (AMBIGUOUS_REDIRECT); + else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0') + { + rd.dest = 0; +! new_redirect = make_redirection (redirector, r_close_this, rd); + } + else if (all_digits (redirectee_word)) + { + if (legal_number (redirectee_word, &lfd) && (int)lfd == lfd) + rd.dest = lfd; + else +--- 677,689 ---- + return (AMBIGUOUS_REDIRECT); + else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0') + { ++ sd.dest = redirector; + rd.dest = 0; +! new_redirect = make_redirection (sd, r_close_this, rd, 0); + } + else if (all_digits (redirectee_word)) + { ++ sd.dest = redirector; + if (legal_number (redirectee_word, &lfd) && (int)lfd == lfd) + rd.dest = lfd; + else +*************** +*** 682,704 **** + switch (ri) + { + 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) + { + rd.filename = make_bare_word (redirectee_word); +! new_redirect = make_redirection (1, r_err_and_out, rd); + } + else + { +--- 691,714 ---- + switch (ri) + { + case r_duplicating_input_word: +! new_redirect = make_redirection (sd, r_duplicating_input, rd, 0); + break; + case r_duplicating_output_word: +! new_redirect = make_redirection (sd, r_duplicating_output, rd, 0); + break; + case r_move_input_word: +! new_redirect = make_redirection (sd, r_move_input, rd, 0); + break; + case r_move_output_word: +! new_redirect = make_redirection (sd, r_move_output, rd, 0); + break; + } + } + else if (ri == r_duplicating_output_word && redirector == 1) + { ++ sd.dest = 1; + rd.filename = make_bare_word (redirectee_word); +! new_redirect = make_redirection (sd, r_err_and_out, rd, 0); + } + else + { +*************** +*** 730,736 **** + redirectee = new_redirect->redirectee.filename; + + redir_fd = new_redirect->redirectee.dest; +! redirector = new_redirect->redirector; + ri = new_redirect->instruction; + + /* Overwrite the flags element of the old redirect with the new value. */ +--- 740,746 ---- + redirectee = new_redirect->redirectee.filename; + + redir_fd = new_redirect->redirectee.dest; +! redirector = new_redirect->redirector.dest; + ri = new_redirect->instruction; + + /* Overwrite the flags element of the old redirect with the new value. */ +*************** +*** 1021,1026 **** +--- 1031,1037 ---- + { + int new_fd, clexec_flag; + REDIRECT *new_redirect, *closer, *dummy_redirect; ++ REDIRECTEE sd; + + new_fd = fcntl (fd, F_DUPFD, (fdbase < SHELL_FD_BASE) ? SHELL_FD_BASE : fdbase+1); + if (new_fd < 0) +*************** +*** 1034,1049 **** + + clexec_flag = fcntl (fd, F_GETFD, 0); + + rd.dest = 0; +! closer = make_redirection (new_fd, r_close_this, rd); + closer->flags |= RX_INTERNAL; + dummy_redirect = copy_redirects (closer); + + 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->flags |= RX_INTERNAL; + if (clexec_flag == 0 && fd >= 3 && new_fd >= SHELL_FD_BASE) + new_redirect->flags |= RX_SAVCLEXEC; +--- 1045,1062 ---- + + clexec_flag = fcntl (fd, F_GETFD, 0); + ++ sd.dest = new_fd; + rd.dest = 0; +! closer = make_redirection (sd, r_close_this, rd, 0); + closer->flags |= RX_INTERNAL; + dummy_redirect = copy_redirects (closer); + ++ sd.dest = fd; + rd.dest = new_fd; + if (fd == 0) +! new_redirect = make_redirection (sd, r_duplicating_input, rd, 0); + else +! new_redirect = make_redirection (sd, r_duplicating_output, rd, 0); + new_redirect->flags |= RX_INTERNAL; + if (clexec_flag == 0 && fd >= 3 && new_fd >= SHELL_FD_BASE) + new_redirect->flags |= RX_SAVCLEXEC; +*************** +*** 1066,1073 **** + to save others. */ + if (fd >= SHELL_FD_BASE && ri != r_close_this && clexec_flag) + { + rd.dest = new_fd; +! new_redirect = make_redirection (fd, r_duplicating_output, rd); + new_redirect->flags |= RX_INTERNAL; + + add_exec_redirect (new_redirect); +--- 1079,1087 ---- + to save others. */ + if (fd >= SHELL_FD_BASE && ri != r_close_this && clexec_flag) + { ++ sd.dest = fd; + rd.dest = new_fd; +! new_redirect = make_redirection (sd, r_duplicating_output, rd, 0); + new_redirect->flags |= RX_INTERNAL; + + add_exec_redirect (new_redirect); +*************** +*** 1096,1104 **** + int fd; + { + REDIRECT *closer; + + rd.dest = 0; +! closer = make_redirection (fd, r_close_this, rd); + closer->flags |= RX_INTERNAL; + closer->next = redirection_undo_list; + redirection_undo_list = closer; +--- 1110,1120 ---- + int fd; + { + REDIRECT *closer; ++ REDIRECTEE sd; + ++ sd.dest = fd; + rd.dest = 0; +! closer = make_redirection (sd, r_close_this, rd, 0); + closer->flags |= RX_INTERNAL; + closer->next = redirection_undo_list; + redirection_undo_list = closer; +*************** +*** 1154,1159 **** + int n; + + for (n = 0, rp = redirs; rp; rp = rp->next) +! n += stdin_redirection (rp->instruction, rp->redirector); + return n; + } +--- 1170,1175 ---- + int n; + + for (n = 0, rp = redirs; rp; rp = rp->next) +! n += stdin_redirection (rp->instruction, rp->redirector.dest); + return n; + } diff --git a/dispose_cmd.c b/dispose_cmd.c index 4c25ae53..c624605b 100644 --- a/dispose_cmd.c +++ b/dispose_cmd.c @@ -309,6 +309,10 @@ dispose_redirects (list) { t = list; list = list->next; + + if (t->rflags & REDIR_VARASSIGN) + dispose_word (t->redirector.filename); + switch (t->instruction) { case r_reading_until: @@ -673,10 +673,11 @@ document_done: INSTRUCTION is the instruction type, SOURCE is a file descriptor, and DEST is a file descriptor or a WORD_DESC *. */ REDIRECT * -make_redirection (source, instruction, dest_and_filename) - int source; +make_redirection (source, instruction, dest_and_filename, flags) + REDIRECTEE source; enum r_instruction instruction; REDIRECTEE dest_and_filename; + int flags; { REDIRECT *temp; WORD_DESC *w; @@ -690,6 +691,7 @@ make_redirection (source, instruction, dest_and_filename) temp->redirectee = dest_and_filename; temp->instruction = instruction; temp->flags = 0; + temp->rflags = flags; temp->next = (REDIRECT *)NULL; switch (instruction) diff --git a/make_cmd.c~ b/make_cmd.c~ index 8c866c54..3c20241a 100644 --- a/make_cmd.c~ +++ b/make_cmd.c~ @@ -526,7 +526,6 @@ make_simple_command (element, command) ELEMENT element; COMMAND *command; { -itrace("make_simple_command"); /* If we are starting from scratch, then make the initial command structure. Also note that we have to fill in all the slots, since malloc doesn't return zeroed space. */ @@ -675,7 +674,7 @@ document_done: and DEST is a file descriptor or a WORD_DESC *. */ REDIRECT * make_redirection (source, instruction, dest_and_filename) - int source; + REDIRECTEE source; enum r_instruction instruction; REDIRECTEE dest_and_filename; { @@ -690,7 +689,7 @@ make_redirection (source, instruction, dest_and_filename) temp->redirector = source; temp->redirectee = dest_and_filename; temp->instruction = instruction; - temp->flags = 0; + temp->flags = temp->rflags = 0; temp->next = (REDIRECT *)NULL; switch (instruction) @@ -47,7 +47,7 @@ extern COMMAND *make_until_command __P((COMMAND *, COMMAND *)); extern COMMAND *make_bare_simple_command __P((void)); extern COMMAND *make_simple_command __P((ELEMENT, COMMAND *)); extern void make_here_document __P((REDIRECT *, int)); -extern REDIRECT *make_redirection __P((int, enum r_instruction, REDIRECTEE)); +extern REDIRECT *make_redirection __P((REDIRECTEE, enum r_instruction, REDIRECTEE, int)); extern COMMAND *make_function_def __P((WORD_DESC *, COMMAND *, int, int)); extern COMMAND *clean_simple_command __P((COMMAND *)); diff --git a/make_cmd.h~ b/make_cmd.h~ new file mode 100644 index 00000000..9c969f79 --- /dev/null +++ b/make_cmd.h~ @@ -0,0 +1,70 @@ +/* make_cmd.h -- Declarations of functions found in make_cmd.c */ + +/* Copyright (C) 1993-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/>. +*/ + +#if !defined (_MAKE_CMD_H_) +#define _MAKE_CMD_H_ + +#include "stdc.h" + +extern void cmd_init __P((void)); + +extern WORD_DESC *alloc_word_desc __P((void)); +extern WORD_DESC *make_bare_word __P((const char *)); +extern WORD_DESC *make_word_flags __P((WORD_DESC *, const char *)); +extern WORD_DESC *make_word __P((const char *)); +extern WORD_DESC *make_word_from_token __P((int)); + +extern WORD_LIST *make_word_list __P((WORD_DESC *, WORD_LIST *)); + +#define add_string_to_list(s, l) make_word_list (make_word(s), (l)) + +extern COMMAND *make_command __P((enum command_type, SIMPLE_COM *)); +extern COMMAND *command_connect __P((COMMAND *, COMMAND *, int)); +extern COMMAND *make_for_command __P((WORD_DESC *, WORD_LIST *, COMMAND *, int)); +extern COMMAND *make_group_command __P((COMMAND *)); +extern COMMAND *make_case_command __P((WORD_DESC *, PATTERN_LIST *, int)); +extern PATTERN_LIST *make_pattern_list __P((WORD_LIST *, COMMAND *)); +extern COMMAND *make_if_command __P((COMMAND *, COMMAND *, COMMAND *)); +extern COMMAND *make_while_command __P((COMMAND *, COMMAND *)); +extern COMMAND *make_until_command __P((COMMAND *, COMMAND *)); +extern COMMAND *make_bare_simple_command __P((void)); +extern COMMAND *make_simple_command __P((ELEMENT, COMMAND *)); +extern void make_here_document __P((REDIRECT *, int)); +extern REDIRECT *make_redirection __P((REDIRECTEE, enum r_instruction, REDIRECTEE)); +extern COMMAND *make_function_def __P((WORD_DESC *, COMMAND *, int, int)); +extern COMMAND *clean_simple_command __P((COMMAND *)); + +extern COMMAND *make_arith_command __P((WORD_LIST *)); + +extern COMMAND *make_select_command __P((WORD_DESC *, WORD_LIST *, COMMAND *, int)); + +#if defined (COND_COMMAND) +extern COND_COM *make_cond_node __P((int, WORD_DESC *, COND_COM *, COND_COM *)); +extern COMMAND *make_cond_command __P((COND_COM *)); +#endif + +extern COMMAND *make_arith_for_command __P((WORD_LIST *, COMMAND *, int)); + +extern COMMAND *make_subshell_command __P((COMMAND *)); +extern COMMAND *make_coproc_command __P((char *, COMMAND *)); + +extern COMMAND *connect_async_list __P((COMMAND *, COMMAND *, int)); + +#endif /* !_MAKE_CMD_H */ @@ -310,6 +310,7 @@ static int word_top = -1; static int token_to_read; static WORD_DESC *word_desc_to_read; +static REDIRECTEE source; static REDIRECTEE redir; %} @@ -332,7 +333,7 @@ static REDIRECTEE redir; %token IN BANG TIME TIMEOPT /* More general tokens. yylex () knows how to make these. */ -%token <word> WORD ASSIGNMENT_WORD +%token <word> WORD ASSIGNMENT_WORD REDIR_WORD %token <number> NUMBER %token <word_list> ARITH_CMD ARITH_FOR_EXPRS %token <command> COND_CMD @@ -419,159 +420,187 @@ word_list: WORD redirection: '>' WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_output_direction, redir); + $$ = make_redirection (source, r_output_direction, redir, 0); } | '<' WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_input_direction, redir); + $$ = make_redirection (source, r_input_direction, redir, 0); } | NUMBER '>' WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_output_direction, redir); + $$ = make_redirection (source, r_output_direction, redir, 0); } | NUMBER '<' WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_input_direction, redir); + $$ = make_redirection (source, r_input_direction, redir, 0); } | GREATER_GREATER WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_appending_to, redir); + $$ = make_redirection (source, r_appending_to, redir, 0); } | NUMBER GREATER_GREATER WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_appending_to, redir); + $$ = make_redirection (source, r_appending_to, redir, 0); } | LESS_LESS WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_reading_until, redir); + $$ = make_redirection (source, r_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | NUMBER LESS_LESS WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_reading_until, redir); + $$ = make_redirection (source, r_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | LESS_LESS_LESS WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_reading_string, redir); + $$ = make_redirection (source, r_reading_string, redir, 0); } | NUMBER LESS_LESS_LESS WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_reading_string, redir); + $$ = make_redirection (source, r_reading_string, redir, 0); } | LESS_AND NUMBER { + source.dest = 0; redir.dest = $2; - $$ = make_redirection (0, r_duplicating_input, redir); + $$ = make_redirection (source, r_duplicating_input, redir, 0); } | NUMBER LESS_AND NUMBER { + source.dest = $1; redir.dest = $3; - $$ = make_redirection ($1, r_duplicating_input, redir); + $$ = make_redirection (source, r_duplicating_input, redir, 0); } | GREATER_AND NUMBER { + source.dest = 1; redir.dest = $2; - $$ = make_redirection (1, r_duplicating_output, redir); + $$ = make_redirection (source, r_duplicating_output, redir, 0); } | NUMBER GREATER_AND NUMBER { + source.dest = $1; redir.dest = $3; - $$ = make_redirection ($1, r_duplicating_output, redir); + $$ = make_redirection (source, r_duplicating_output, redir, 0); } | LESS_AND WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_duplicating_input_word, redir); + $$ = make_redirection (source, r_duplicating_input_word, redir, 0); } | NUMBER LESS_AND WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_duplicating_input_word, redir); + $$ = make_redirection (source, r_duplicating_input_word, redir, 0); } | GREATER_AND WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_duplicating_output_word, redir); + $$ = make_redirection (source, r_duplicating_output_word, redir, 0); } | NUMBER GREATER_AND WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_duplicating_output_word, redir); + $$ = make_redirection (source, r_duplicating_output_word, redir, 0); } | LESS_LESS_MINUS WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection - (0, r_deblank_reading_until, redir); + $$ = make_redirection (source, r_deblank_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | NUMBER LESS_LESS_MINUS WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection - ($1, r_deblank_reading_until, redir); + $$ = make_redirection (source, r_deblank_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | GREATER_AND '-' { + source.dest = 1; redir.dest = 0; - $$ = make_redirection (1, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | NUMBER GREATER_AND '-' { + source.dest = $1; redir.dest = 0; - $$ = make_redirection ($1, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | LESS_AND '-' { + source.dest = 0; redir.dest = 0; - $$ = make_redirection (0, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | NUMBER LESS_AND '-' { + source.dest = $1; redir.dest = 0; - $$ = make_redirection ($1, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | AND_GREATER WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_err_and_out, redir); + $$ = make_redirection (source, r_err_and_out, redir, 0); } | AND_GREATER_GREATER WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_append_err_and_out, redir); + $$ = make_redirection (source, r_append_err_and_out, redir, 0); } | NUMBER LESS_GREATER WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_input_output, redir); + $$ = make_redirection (source, r_input_output, redir, 0); } | LESS_GREATER WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_input_output, redir); + $$ = make_redirection (source, r_input_output, redir, 0); } | GREATER_BAR WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_output_force, redir); + $$ = make_redirection (source, r_output_force, redir, 0); } | NUMBER GREATER_BAR WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_output_force, redir); + $$ = make_redirection (source, r_output_force, redir, 0); } ; @@ -1119,12 +1148,13 @@ pipeline: pipeline '|' newline_list pipeline { /* Make cmd1 |& cmd2 equivalent to cmd1 2>&1 | cmd2 */ COMMAND *tc; - REDIRECTEE rd; + REDIRECTEE rd, sd; REDIRECT *r; tc = $1->type == cm_simple ? (COMMAND *)$1->value.Simple : $1; + sd.dest = 2; rd.dest = 1; - r = make_redirection (2, r_duplicating_output, rd); + r = make_redirection (sd, r_duplicating_output, rd, 0); if (tc->redirects) { register REDIRECT *t; @@ -310,6 +310,7 @@ static int word_top = -1; static int token_to_read; static WORD_DESC *word_desc_to_read; +static REDIRECTEE source; static REDIRECTEE redir; %} @@ -419,159 +420,187 @@ word_list: WORD redirection: '>' WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_output_direction, redir); + $$ = make_redirection (source, r_output_direction, redir, 0); } | '<' WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_input_direction, redir); + $$ = make_redirection (source, r_input_direction, redir, 0); } | NUMBER '>' WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_output_direction, redir); + $$ = make_redirection (source, r_output_direction, redir, 0); } | NUMBER '<' WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_input_direction, redir); + $$ = make_redirection (source, r_input_direction, redir, 0); } | GREATER_GREATER WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_appending_to, redir); + $$ = make_redirection (source, r_appending_to, redir, 0); } | NUMBER GREATER_GREATER WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_appending_to, redir); + $$ = make_redirection (source, r_appending_to, redir, 0); } | LESS_LESS WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_reading_until, redir); + $$ = make_redirection (source, r_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | NUMBER LESS_LESS WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_reading_until, redir); + $$ = make_redirection (source, r_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | LESS_LESS_LESS WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_reading_string, redir); + $$ = make_redirection (source, r_reading_string, redir, 0); } | NUMBER LESS_LESS_LESS WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_reading_string, redir); + $$ = make_redirection (source, r_reading_string, redir, 0); } | LESS_AND NUMBER { + source.dest = 0; redir.dest = $2; - $$ = make_redirection (0, r_duplicating_input, redir); + $$ = make_redirection (source, r_duplicating_input, redir, 0); } | NUMBER LESS_AND NUMBER { + source.dest = $1; redir.dest = $3; - $$ = make_redirection ($1, r_duplicating_input, redir); + $$ = make_redirection (source, r_duplicating_input, redir, 0); } | GREATER_AND NUMBER { + source.dest = 1; redir.dest = $2; - $$ = make_redirection (1, r_duplicating_output, redir); + $$ = make_redirection (source, r_duplicating_output, redir, 0); } | NUMBER GREATER_AND NUMBER { + source.dest = $1; redir.dest = $3; - $$ = make_redirection ($1, r_duplicating_output, redir); + $$ = make_redirection (source, r_duplicating_output, redir, 0); } | LESS_AND WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_duplicating_input_word, redir); + $$ = make_redirection (source, r_duplicating_input_word, redir, 0); } | NUMBER LESS_AND WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_duplicating_input_word, redir); + $$ = make_redirection (source, r_duplicating_input_word, redir, 0); } | GREATER_AND WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_duplicating_output_word, redir); + $$ = make_redirection (source, r_duplicating_output_word, redir, 0); } | NUMBER GREATER_AND WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_duplicating_output_word, redir); + $$ = make_redirection (source, r_duplicating_output_word, redir, 0); } | LESS_LESS_MINUS WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection - (0, r_deblank_reading_until, redir); + $$ = make_redirection (source, r_deblank_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | NUMBER LESS_LESS_MINUS WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection - ($1, r_deblank_reading_until, redir); + $$ = make_redirection (source, r_deblank_reading_until, redir, 0); redir_stack[need_here_doc++] = $$; } | GREATER_AND '-' { + source.dest = 1; redir.dest = 0; - $$ = make_redirection (1, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | NUMBER GREATER_AND '-' { + source.dest = $1; redir.dest = 0; - $$ = make_redirection ($1, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | LESS_AND '-' { + source.dest = 0; redir.dest = 0; - $$ = make_redirection (0, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | NUMBER LESS_AND '-' { + source.dest = $1; redir.dest = 0; - $$ = make_redirection ($1, r_close_this, redir); + $$ = make_redirection (source, r_close_this, redir, 0); } | AND_GREATER WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_err_and_out, redir); + $$ = make_redirection (source, r_err_and_out, redir, 0); } | AND_GREATER_GREATER WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_append_err_and_out, redir); + $$ = make_redirection (source, r_append_err_and_out, redir, 0); } | NUMBER LESS_GREATER WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_input_output, redir); + $$ = make_redirection (source, r_input_output, redir, 0); } | LESS_GREATER WORD { + source.dest = 0; redir.filename = $2; - $$ = make_redirection (0, r_input_output, redir); + $$ = make_redirection (source, r_input_output, redir, 0); } | GREATER_BAR WORD { + source.dest = 1; redir.filename = $2; - $$ = make_redirection (1, r_output_force, redir); + $$ = make_redirection (source, r_output_force, redir, 0); } | NUMBER GREATER_BAR WORD { + source.dest = $1; redir.filename = $3; - $$ = make_redirection ($1, r_output_force, redir); + $$ = make_redirection (source, r_output_force, redir, 0); } ; @@ -1119,12 +1148,13 @@ pipeline: pipeline '|' newline_list pipeline { /* Make cmd1 |& cmd2 equivalent to cmd1 2>&1 | cmd2 */ COMMAND *tc; - REDIRECTEE rd; + REDIRECTEE rd, sd; REDIRECT *r; tc = $1->type == cm_simple ? (COMMAND *)$1->value.Simple : $1; + sd.dest = 2; rd.dest = 1; - r = make_redirection (2, r_duplicating_output, rd); + r = make_redirection (sd, r_duplicating_output, rd, 0); if (tc->redirects) { register REDIRECT *t; @@ -3505,12 +3535,12 @@ eof_error: else if MBTEST(ch == close && (tflags & LEX_INCASE) == 0) /* ending delimiter */ { count--; -itrace("parse_comsub:%d: found close: count = %d", line_number, count); +/*itrace("parse_comsub:%d: found close: count = %d", line_number, count);*/ } else if MBTEST(((flags & P_FIRSTCLOSE) == 0) && (tflags & LEX_INCASE) == 0 && ch == open) /* nested begin */ { count++; -itrace("parse_comsub:%d: found open: count = %d", line_number, count); +/*itrace("parse_comsub:%d: found open: count = %d", line_number, count);*/ } /* Add this character. */ diff --git a/print_cmd.c b/print_cmd.c index d902a002..66bb0705 100644 --- a/print_cmd.c +++ b/print_cmd.c @@ -988,7 +988,7 @@ print_redirection_list (redirects) else hdtail = heredocs = newredir; } - else if (redirects->instruction == r_duplicating_output_word && redirects->redirector == 1) + else if (redirects->instruction == r_duplicating_output_word && redirects->redirector.dest == 1) { /* Temporarily translate it as the execution code does. */ redirects->instruction = r_err_and_out; @@ -1024,8 +1024,8 @@ print_heredoc_header (redirect) kill_leading = redirect->instruction == r_deblank_reading_until; /* Here doc header */ - if (redirect->redirector != 0) - cprintf ("%d", redirect->redirector); + if (redirect->redirector.dest != 0) + cprintf ("%d", redirect->redirector.dest); /* If the here document delimiter is quoted, single-quote it. */ if (redirect->redirectee.filename->flags & W_QUOTED) @@ -1055,7 +1055,7 @@ print_redirection (redirect) kill_leading = 0; redirectee = redirect->redirectee.filename; - redirector = redirect->redirector; + redirector = redirect->redirector.dest; redir_fd = redirect->redirectee.dest; switch (redirect->instruction) diff --git a/print_cmd.c~ b/print_cmd.c~ index aa92f393..d902a002 100644 --- a/print_cmd.c~ +++ b/print_cmd.c~ @@ -360,16 +360,16 @@ xtrace_set (fd, fp) { if (fd >= 0 && sh_validfd (fd) == 0) { - internal_error ("xtrace_set: %d: invalid file descriptor", fd); + internal_error (_("xtrace_set: %d: invalid file descriptor"), fd); return; } if (fp == 0) { - internal_error ("xtrace_set: NULL file pointer"); + internal_error (_("xtrace_set: NULL file pointer")); return; } if (fd >= 0 && fileno (fp) != fd) - internal_warning ("xtrace fd (%d) != fileno xtrace fp (%d)", fd, fileno (fp)); + internal_warning (_("xtrace fd (%d) != fileno xtrace fp (%d)"), fd, fileno (fp)); xtrace_fd = fd; xtrace_fp = fp; @@ -381,6 +381,29 @@ xtrace_init () xtrace_set (-1, stderr); } +void +xtrace_reset () +{ + if (xtrace_fd >= 0 && xtrace_fp) + { + fflush (xtrace_fp); + fclose (xtrace_fp); + } + else if (xtrace_fd >= 0) + close (xtrace_fd); + + xtrace_fd = -1; + xtrace_fp = stderr; +} + +void +xtrace_fdchk (fd) + int fd; +{ + if (fd == xtrace_fd) + xtrace_reset (); +} + /* Return a string denoting what our indirection level is. */ char * @@ -99,13 +99,15 @@ redirection_error (temp, error) int oflags; allocname = 0; - if (temp->redirector < 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 >= 0 && error == EBADF) + 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 @@ -119,7 +121,7 @@ redirection_error (temp, error) filename = allocname = itos (temp->redirectee.dest); break; default: - filename = allocname = itos (temp->redirector); + filename = allocname = itos (temp->redirector.dest); break; } } @@ -162,6 +164,10 @@ redirection_error (temp, error) internal_error (_("cannot create temp file for here-document: %s"), strerror (heredoc_errno)); break; + case BADVAR_REDIRECT: + internal_error (_("cannot assign fd to variable %s"), filename); + break; + default: internal_error ("%s: %s", filename, strerror (error)); break; @@ -649,10 +655,11 @@ do_redirection_internal (redirect, flags) char *redirectee_word; enum r_instruction ri; REDIRECT *new_redirect; + REDIRECTEE sd; redirectee = redirect->redirectee.filename; redir_fd = redirect->redirectee.dest; - redirector = redirect->redirector; + redirector = redirect->redirector.dest; ri = redirect->instruction; if (redirect->flags & RX_INTERNAL) @@ -670,11 +677,13 @@ do_redirection_internal (redirect, flags) return (AMBIGUOUS_REDIRECT); else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0') { + sd.dest = redirector; rd.dest = 0; - new_redirect = make_redirection (redirector, r_close_this, rd); + new_redirect = make_redirection (sd, r_close_this, rd, 0); } else if (all_digits (redirectee_word)) { + sd.dest = redirector; if (legal_number (redirectee_word, &lfd) && (int)lfd == lfd) rd.dest = lfd; else @@ -682,23 +691,24 @@ do_redirection_internal (redirect, flags) switch (ri) { case r_duplicating_input_word: - new_redirect = make_redirection (redirector, r_duplicating_input, rd); + new_redirect = make_redirection (sd, r_duplicating_input, rd, 0); break; case r_duplicating_output_word: - new_redirect = make_redirection (redirector, r_duplicating_output, rd); + new_redirect = make_redirection (sd, r_duplicating_output, rd, 0); break; case r_move_input_word: - new_redirect = make_redirection (redirector, r_move_input, rd); + new_redirect = make_redirection (sd, r_move_input, rd, 0); break; case r_move_output_word: - new_redirect = make_redirection (redirector, r_move_output, rd); + new_redirect = make_redirection (sd, r_move_output, rd, 0); break; } } else if (ri == r_duplicating_output_word && redirector == 1) { + sd.dest = 1; rd.filename = make_bare_word (redirectee_word); - new_redirect = make_redirection (1, r_err_and_out, rd); + new_redirect = make_redirection (sd, r_err_and_out, rd, 0); } else { @@ -730,7 +740,7 @@ do_redirection_internal (redirect, flags) redirectee = new_redirect->redirectee.filename; redir_fd = new_redirect->redirectee.dest; - redirector = new_redirect->redirector; + redirector = new_redirect->redirector.dest; ri = new_redirect->instruction; /* Overwrite the flags element of the old redirect with the new value. */ @@ -1021,6 +1031,7 @@ add_undo_redirect (fd, ri, fdbase) { int new_fd, clexec_flag; REDIRECT *new_redirect, *closer, *dummy_redirect; + REDIRECTEE sd; new_fd = fcntl (fd, F_DUPFD, (fdbase < SHELL_FD_BASE) ? SHELL_FD_BASE : fdbase+1); if (new_fd < 0) @@ -1034,16 +1045,18 @@ add_undo_redirect (fd, ri, fdbase) clexec_flag = fcntl (fd, F_GETFD, 0); + sd.dest = new_fd; rd.dest = 0; - closer = make_redirection (new_fd, r_close_this, rd); + closer = make_redirection (sd, r_close_this, rd, 0); closer->flags |= RX_INTERNAL; dummy_redirect = copy_redirects (closer); + sd.dest = fd; rd.dest = new_fd; if (fd == 0) - new_redirect = make_redirection (fd, r_duplicating_input, rd); + new_redirect = make_redirection (sd, r_duplicating_input, rd, 0); else - new_redirect = make_redirection (fd, r_duplicating_output, rd); + new_redirect = make_redirection (sd, r_duplicating_output, rd, 0); new_redirect->flags |= RX_INTERNAL; if (clexec_flag == 0 && fd >= 3 && new_fd >= SHELL_FD_BASE) new_redirect->flags |= RX_SAVCLEXEC; @@ -1066,8 +1079,9 @@ add_undo_redirect (fd, ri, fdbase) to save others. */ if (fd >= SHELL_FD_BASE && ri != r_close_this && clexec_flag) { + sd.dest = fd; rd.dest = new_fd; - new_redirect = make_redirection (fd, r_duplicating_output, rd); + new_redirect = make_redirection (sd, r_duplicating_output, rd, 0); new_redirect->flags |= RX_INTERNAL; add_exec_redirect (new_redirect); @@ -1096,9 +1110,11 @@ add_undo_close_redirect (fd) int fd; { REDIRECT *closer; + REDIRECTEE sd; + sd.dest = fd; rd.dest = 0; - closer = make_redirection (fd, r_close_this, rd); + closer = make_redirection (sd, r_close_this, rd, 0); closer->flags |= RX_INTERNAL; closer->next = redirection_undo_list; redirection_undo_list = closer; @@ -1154,6 +1170,6 @@ stdin_redirects (redirs) int n; for (n = 0, rp = redirs; rp; rp = rp->next) - n += stdin_redirection (rp->instruction, rp->redirector); + n += stdin_redirection (rp->instruction, rp->redirector.dest); return n; } diff --git a/redir.c~ b/redir.c~ new file mode 100644 index 00000000..20fc6837 --- /dev/null +++ b/redir.c~ @@ -0,0 +1,1175 @@ +/* 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 10 + +int expanding_redir; + +extern int posixly_correct; +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 __P((int)); +static void add_exec_redirect __P((REDIRECT *)); +static int add_undo_redirect __P((int, enum r_instruction, 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)); + +/* 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; + +void +redirection_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; + default: + filename = allocname = itos (temp->redirector.dest); + 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 = 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 (_("cannot assign fd to variable %s"), 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. */ +int +do_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 int +expandable_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 int +write_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 int +write_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 int +here_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); + + if (r) + { + close (fd); + unlink (filename); + free (filename); + errno = r; + return (-1); + } + + /* In an attempt to avoid races, we close the first fd only after opening + the second. */ + /* Make the document really temporary. Also make it the input. */ + fd2 = open (filename, O_RDONLY, 0600); + + if (fd2 < 0) + { + r = errno; + unlink (filename); + free (filename); + close (fd); + errno = r; + return -1; + } + + close (fd); + 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); +} + +#define RF_DEVFD 1 +#define RF_DEVSTDERR 2 +#define RF_DEVSTDIN 3 +#define RF_DEVSTDOUT 4 +#define RF_DEVTCP 5 +#define RF_DEVUDP 6 + +/* A list of pattern/value pairs for filenames that the redirection + code handles specially. */ +static STRING_INT_ALIST _redir_special_filenames[] = { +#if !defined (HAVE_DEV_FD) + { "/dev/fd/[0-9]*", RF_DEVFD }, +#endif +#if !defined (HAVE_DEV_STDIN) + { "/dev/stderr", RF_DEVSTDERR }, + { "/dev/stdin", RF_DEVSTDIN }, + { "/dev/stdout", RF_DEVSTDOUT }, +#endif +#if defined (NETWORK_REDIRECTIONS) + { "/dev/tcp/*/*", RF_DEVTCP }, + { "/dev/udp/*/*", RF_DEVUDP }, +#endif + { (char *)NULL, -1 } +}; + +static int +redir_special_open (spec, filename, flags, mode, ri) + int spec; + char *filename; + int flags, mode; + enum r_instruction ri; +{ + int fd; +#if !defined (HAVE_DEV_FD) + intmax_t lfd; +#endif + + fd = -1; + switch (spec) + { +#if !defined (HAVE_DEV_FD) + case RF_DEVFD: + if (all_digits (filename+8) && legal_number (filename+8, &lfd) && lfd == (int)lfd) + { + fd = lfd; + fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE); + } + else + fd = AMBIGUOUS_REDIRECT; + break; +#endif + +#if !defined (HAVE_DEV_STDIN) + case RF_DEVSTDIN: + fd = fcntl (0, F_DUPFD, SHELL_FD_BASE); + break; + case RF_DEVSTDOUT: + fd = fcntl (1, F_DUPFD, SHELL_FD_BASE); + break; + case RF_DEVSTDERR: + fd = fcntl (2, F_DUPFD, SHELL_FD_BASE); + break; +#endif + +#if defined (NETWORK_REDIRECTIONS) + case RF_DEVTCP: + case RF_DEVUDP: +#if defined (HAVE_NETWORK) + fd = netopen (filename); +#else + internal_warning (_("/dev/(tcp|udp)/host/port not supported without networking")); + fd = open (filename, flags, mode); +#endif + break; +#endif /* NETWORK_REDIRECTIONS */ + } + + return fd; +} + +/* Open FILENAME with FLAGS in noclobber mode, hopefully avoiding most + race conditions and avoiding the problem where the file is replaced + between the stat(2) and open(2). */ +static int +noclobber_open (filename, flags, mode, ri) + char *filename; + int flags, mode; + enum r_instruction ri; +{ + int r, fd; + struct stat finfo, finfo2; + + /* If the file exists and is a regular file, return an error + immediately. */ + r = stat (filename, &finfo); + if (r == 0 && (S_ISREG (finfo.st_mode))) + return (NOCLOBBER_REDIRECT); + + /* If the file was not present (r != 0), make sure we open it + exclusively so that if it is created before we open it, our open + will fail. Make sure that we do not truncate an existing file. + Note that we don't turn on O_EXCL unless the stat failed -- if + the file was not a regular file, we leave O_EXCL off. */ + flags &= ~O_TRUNC; + if (r != 0) + { + fd = open (filename, flags|O_EXCL, mode); + return ((fd < 0 && errno == EEXIST) ? NOCLOBBER_REDIRECT : fd); + } + fd = open (filename, flags, mode); + + /* If the open failed, return the file descriptor right away. */ + if (fd < 0) + return (errno == EEXIST ? NOCLOBBER_REDIRECT : fd); + + /* OK, the open succeeded, but the file may have been changed from a + non-regular file to a regular file between the stat and the open. + We are assuming that the O_EXCL open handles the case where FILENAME + did not exist and is symlinked to an existing file between the stat + and open. */ + + /* If we can open it and fstat the file descriptor, and neither check + revealed that it was a regular file, and the file has not been replaced, + return the file descriptor. */ + if ((fstat (fd, &finfo2) == 0) && (S_ISREG (finfo2.st_mode) == 0) && + r == 0 && (S_ISREG (finfo.st_mode) == 0) && + same_file (filename, filename, &finfo, &finfo2)) + return fd; + + /* The file has been replaced. badness. */ + close (fd); + errno = EEXIST; + return (NOCLOBBER_REDIRECT); +} + +static int +redir_open (filename, flags, mode, ri) + char *filename; + int flags, mode; + enum r_instruction ri; +{ + int fd, r; + + r = find_string_in_alist (filename, _redir_special_filenames, 1); + if (r >= 0) + return (redir_special_open (r, filename, flags, mode, ri)); + + /* If we are in noclobber mode, you are not allowed to overwrite + existing files. Check before opening. */ + if (noclobber && CLOBBERING_REDIRECT (ri)) + { + fd = noclobber_open (filename, flags, mode, ri); + if (fd == NOCLOBBER_REDIRECT) + return (NOCLOBBER_REDIRECT); + } + else + { + fd = open (filename, flags, mode); +#if defined (AFS) + if ((fd < 0) && (errno == EACCES)) + { + fd = open (filename, flags & ~O_CREAT, mode); + errno = EACCES; /* restore errno */ + } +#endif /* AFS */ + } + + return fd; +} + +static int +undoablefd (fd) + int fd; +{ + int clexec; + + clexec = fcntl (fd, F_GETFD, 0); + if (clexec == -1 || (fd >= SHELL_FD_BASE && clexec == 1)) + return 0; + return 1; +} + +/* Do the specific redirection requested. Returns errno or one of the + special redirection errors (*_REDIRECT) in case of error, 0 on success. + If flags & RX_ACTIVE is zero, then just do whatever is neccessary to + produce the appropriate side effects. flags & RX_UNDOABLE, if non-zero, + says to remember how to undo each redirection. If flags & RX_CLEXEC is + non-zero, then we set all file descriptors > 2 that we open to be + close-on-exec. */ +static int +do_redirection_internal (redirect, flags) + REDIRECT *redirect; + int flags; +{ + WORD_DESC *redirectee; + int redir_fd, fd, redirector, r, oflags; + intmax_t lfd; + char *redirectee_word; + enum r_instruction ri; + REDIRECT *new_redirect; + REDIRECTEE sd; + + redirectee = redirect->redirectee.filename; + redir_fd = redirect->redirectee.dest; + redirector = redirect->redirector.dest; + ri = redirect->instruction; + + if (redirect->flags & RX_INTERNAL) + flags |= RX_INTERNAL; + + if (TRANSLATE_REDIRECT (ri)) + { + /* 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') + { + sd.dest = redirector; + rd.dest = 0; + new_redirect = make_redirection (sd, r_close_this, rd); + } + else if (all_digits (redirectee_word)) + { + sd.dest = redirector; + if (legal_number (redirectee_word, &lfd) && (int)lfd == lfd) + rd.dest = lfd; + else + rd.dest = -1; /* XXX */ + switch (ri) + { + case r_duplicating_input_word: + new_redirect = make_redirection (sd, r_duplicating_input, rd); + break; + case r_duplicating_output_word: + new_redirect = make_redirection (sd, r_duplicating_output, rd); + break; + case r_move_input_word: + new_redirect = make_redirection (sd, r_move_input, rd); + break; + case r_move_output_word: + new_redirect = make_redirection (sd, r_move_output, rd); + break; + } + } + else if (ri == r_duplicating_output_word && redirector == 1) + { + sd.dest = 1; + rd.filename = make_bare_word (redirectee_word); + new_redirect = make_redirection (sd, r_err_and_out, rd); + } + else + { + free (redirectee_word); + return (AMBIGUOUS_REDIRECT); + } + + free (redirectee_word); + + /* Set up the variables needed by the rest of the function from the + new redirection. */ + if (new_redirect->instruction == r_err_and_out) + { + char *alloca_hack; + + /* Copy the word without allocating any memory that must be + explicitly freed. */ + redirectee = (WORD_DESC *)alloca (sizeof (WORD_DESC)); + xbcopy ((char *)new_redirect->redirectee.filename, + (char *)redirectee, sizeof (WORD_DESC)); + + alloca_hack = (char *) + alloca (1 + strlen (new_redirect->redirectee.filename->word)); + redirectee->word = alloca_hack; + strcpy (redirectee->word, new_redirect->redirectee.filename->word); + } + else + /* It's guaranteed to be an integer, and shouldn't be freed. */ + redirectee = new_redirect->redirectee.filename; + + redir_fd = new_redirect->redirectee.dest; + redirector = new_redirect->redirector.dest; + ri = new_redirect->instruction; + + /* Overwrite the flags element of the old redirect with the new value. */ + redirect->flags = new_redirect->flags; + dispose_redirects (new_redirect); + } + + switch (ri) + { + case r_output_direction: + case r_appending_to: + case r_input_direction: + case r_inputa_direction: + case r_err_and_out: /* command &>filename */ + case r_append_err_and_out: /* command &>> filename */ + case r_input_output: + case r_output_force: + if (posixly_correct && interactive_shell == 0) + { + oflags = redirectee->flags; + redirectee->flags |= W_NOGLOB; + } + redirectee_word = redirection_expand (redirectee); + if (posixly_correct && interactive_shell == 0) + redirectee->flags = oflags; + + if (redirectee_word == 0) + return (AMBIGUOUS_REDIRECT); + +#if defined (RESTRICTED_SHELL) + if (restricted && (WRITE_REDIRECT (ri))) + { + free (redirectee_word); + return (RESTRICTED_REDIRECT); + } +#endif /* RESTRICTED_SHELL */ + + fd = redir_open (redirectee_word, redirect->flags, 0666, ri); + free (redirectee_word); + + if (fd == NOCLOBBER_REDIRECT) + return (fd); + + if (fd < 0) + return (errno); + + if (flags & RX_ACTIVE) + { + if (flags & RX_UNDOABLE) + { + /* 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, ri, -1); + else + add_undo_close_redirect (redirector); + } + +#if defined (BUFFERED_INPUT) + check_bash_input (redirector); +#endif + + /* Make sure there is no pending output before we change the state + of the underlying file descriptor, since the builtins use stdio + for output. */ + if (redirector == 1 && fileno (stdout) == redirector) + { + fflush (stdout); + fpurge (stdout); + } + else if (redirector == 2 && fileno (stderr) == redirector) + { + fflush (stderr); + fpurge (stderr); + } + + if ((fd != redirector) && (dup2 (fd, redirector) < 0)) + return (errno); + +#if defined (BUFFERED_INPUT) + /* Do not change the buffered stream for an implicit redirection + of /dev/null to fd 0 for asynchronous commands without job + control (r_inputa_direction). */ + if (ri == r_input_direction || ri == r_input_output) + duplicate_buffered_stream (fd, redirector); +#endif /* BUFFERED_INPUT */ + + /* + * If we're remembering, then this is the result of a while, for + * or until loop with a loop redirection, or a function/builtin + * executing in the parent shell with a redirection. In the + * function/builtin case, we want to set all file descriptors > 2 + * to be close-on-exec to duplicate the effect of the old + * for i = 3 to NOFILE close(i) loop. In the case of the loops, + * both sh and ksh leave the file descriptors open across execs. + * The Posix standard mentions only the exec builtin. + */ + if ((flags & RX_CLEXEC) && (redirector > 2)) + SET_CLOSE_ON_EXEC (redirector); + } + + if (fd != redirector) + { +#if defined (BUFFERED_INPUT) + if (INPUT_REDIRECT (ri)) + close_buffered_fd (fd); + else +#endif /* !BUFFERED_INPUT */ + close (fd); /* Don't close what we just opened! */ + } + + /* If we are hacking both stdout and stderr, do the stderr + redirection here. */ + if (ri == r_err_and_out || ri == r_append_err_and_out) + { + if (flags & RX_ACTIVE) + { + if (flags & RX_UNDOABLE) + add_undo_redirect (2, ri, -1); + if (dup2 (1, 2) < 0) + return (errno); + } + } + break; + + 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, ri); + + if (fd < 0) + { + heredoc_errno = errno; + return (HEREDOC_REDIRECT); + } + + if (flags & RX_ACTIVE) + { + if (flags & RX_UNDOABLE) + { + /* 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, ri, -1); + else + add_undo_close_redirect (redirector); + } + +#if defined (BUFFERED_INPUT) + check_bash_input (redirector); +#endif + if (fd != redirector && dup2 (fd, redirector) < 0) + { + r = errno; + close (fd); + return (r); + } + +#if defined (BUFFERED_INPUT) + duplicate_buffered_stream (fd, redirector); +#endif + + if ((flags & RX_CLEXEC) && (redirector > 2)) + SET_CLOSE_ON_EXEC (redirector); + } + + if (fd != redirector) +#if defined (BUFFERED_INPUT) + close_buffered_fd (fd); +#else + close (fd); +#endif + } + break; + + case r_duplicating_input: + case r_duplicating_output: + case r_move_input: + case r_move_output: + if ((flags & RX_ACTIVE) && (redir_fd != redirector)) + { + if (flags & RX_UNDOABLE) + { + /* Only setup to undo it if the thing to undo is active. */ + if (fcntl (redirector, F_GETFD, 0) != -1) + add_undo_redirect (redirector, ri, redir_fd); + 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 || ri == r_move_input) + duplicate_buffered_stream (redir_fd, redirector); +#endif /* BUFFERED_INPUT */ + + /* First duplicate the close-on-exec state of redirectee. dup2 + leaves the flag unset on the new descriptor, which means it + stays open. Only set the close-on-exec bit for file descriptors + greater than 2 in any case, since 0-2 should always be open + unless closed by something like `exec 2<&-'. It should always + be safe to set fds > 2 to close-on-exec if they're being used to + save file descriptors < 2, since we don't need to preserve the + state of the close-on-exec flag for those fds -- they should + always be open. */ + /* if ((already_set || set_unconditionally) && (ok_to_set)) + set_it () */ +#if 0 + if (((fcntl (redir_fd, F_GETFD, 0) == 1) || redir_fd < 2 || (flags & RX_CLEXEC)) && + (redirector > 2)) +#else + if (((fcntl (redir_fd, F_GETFD, 0) == 1) || (redir_fd < 2 && (flags & RX_INTERNAL)) || (flags & RX_CLEXEC)) && + (redirector > 2)) +#endif + SET_CLOSE_ON_EXEC (redirector); + + /* When undoing saving of non-standard file descriptors (>=3) using + file descriptors >= SHELL_FD_BASE, we set the saving fd to be + close-on-exec and use a flag to decide how to set close-on-exec + when the fd is restored. */ + if ((redirect->flags & RX_INTERNAL) && (redirect->flags & RX_SAVCLEXEC) && redirector >= 3 && redir_fd >= SHELL_FD_BASE) + SET_OPEN_ON_EXEC (redirector); + + /* dup-and-close redirection */ + if (ri == r_move_input || ri == r_move_output) + { + xtrace_fdchk (redir_fd); + + close (redir_fd); +#if defined (COPROCESS_SUPPORT) + coproc_fdchk (redir_fd); /* XXX - loses coproc fds */ +#endif + } + } + break; + + case r_close_this: + if (flags & RX_ACTIVE) + { + if ((flags & RX_UNDOABLE) && (fcntl (redirector, F_GETFD, 0) != -1)) + add_undo_redirect (redirector, ri, -1); + +#if defined (COPROCESS_SUPPORT) + coproc_fdchk (redirector); +#endif + xtrace_fdchk (redirector); + +#if defined (BUFFERED_INPUT) + check_bash_input (redirector); + close_buffered_fd (redirector); +#else /* !BUFFERED_INPUT */ + close (redirector); +#endif /* !BUFFERED_INPUT */ + } + break; + + case r_duplicating_input_word: + case r_duplicating_output_word: + break; + } + return (0); +} + +/* Remember the file descriptor associated with the slot FD, + on REDIRECTION_UNDO_LIST. Note that the list will be reversed + before it is executed. Any redirections that need to be undone + even if REDIRECTION_UNDO_LIST is discarded by the exec builtin + are also saved on EXEC_REDIRECTION_UNDO_LIST. FDBASE says where to + start the duplicating. If it's less than SHELL_FD_BASE, we're ok, + and can use SHELL_FD_BASE (-1 == don't care). If it's >= SHELL_FD_BASE, + we have to make sure we don't use fdbase to save a file descriptor, + since we're going to use it later (e.g., make sure we don't save fd 0 + to fd 10 if we have a redirection like 0<&10). If the value of fdbase + puts the process over its fd limit, causing fcntl to fail, we try + again with SHELL_FD_BASE. */ +static int +add_undo_redirect (fd, ri, fdbase) + int fd; + enum r_instruction ri; + int fdbase; +{ + int new_fd, clexec_flag; + REDIRECT *new_redirect, *closer, *dummy_redirect; + REDIRECTEE sd; + + new_fd = fcntl (fd, F_DUPFD, (fdbase < SHELL_FD_BASE) ? SHELL_FD_BASE : fdbase+1); + if (new_fd < 0) + new_fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE); + + if (new_fd < 0) + { + sys_error (_("redirection error: cannot duplicate fd")); + return (-1); + } + + clexec_flag = fcntl (fd, F_GETFD, 0); + + sd.dest = new_fd; + rd.dest = 0; + closer = make_redirection (sd, r_close_this, rd); + closer->flags |= RX_INTERNAL; + dummy_redirect = copy_redirects (closer); + + sd.dest = fd; + rd.dest = new_fd; + if (fd == 0) + new_redirect = make_redirection (sd, r_duplicating_input, rd); + else + new_redirect = make_redirection (sd, r_duplicating_output, rd); + new_redirect->flags |= RX_INTERNAL; + if (clexec_flag == 0 && fd >= 3 && new_fd >= SHELL_FD_BASE) + new_redirect->flags |= RX_SAVCLEXEC; + new_redirect->next = closer; + + closer->next = redirection_undo_list; + redirection_undo_list = new_redirect; + + /* Save redirections that need to be undone even if the undo list + is thrown away by the `exec' builtin. */ + add_exec_redirect (dummy_redirect); + + /* experimental: if we're saving a redirection to undo for a file descriptor + above SHELL_FD_BASE, add a redirection to be undone if the exec builtin + causes redirections to be discarded. There needs to be a difference + between fds that are used to save other fds and then are the target of + user redirctions and fds that are just the target of user redirections. + We use the close-on-exec flag to tell the difference; fds > SHELL_FD_BASE + that have the close-on-exec flag set are assumed to be fds used internally + to save others. */ + if (fd >= SHELL_FD_BASE && ri != r_close_this && clexec_flag) + { + sd.dest = fd; + rd.dest = new_fd; + new_redirect = make_redirection (sd, r_duplicating_output, rd); + new_redirect->flags |= RX_INTERNAL; + + add_exec_redirect (new_redirect); + } + + /* File descriptors used only for saving others should always be + marked close-on-exec. Unfortunately, we have to preserve the + close-on-exec state of the file descriptor we are saving, since + fcntl (F_DUPFD) sets the new file descriptor to remain open + across execs. If, however, the file descriptor whose state we + are saving is <= 2, we can just set the close-on-exec flag, + because file descriptors 0-2 should always be open-on-exec, + and the restore above in do_redirection() will take care of it. */ + if (clexec_flag || fd < 3) + SET_CLOSE_ON_EXEC (new_fd); + else if (redirection_undo_list->flags & RX_SAVCLEXEC) + SET_CLOSE_ON_EXEC (new_fd); + + return (0); +} + +/* Set up to close FD when we are finished with the current command + and its redirections. */ +static void +add_undo_close_redirect (fd) + int fd; +{ + REDIRECT *closer; + REDIRECTEE sd; + + sd.dest = fd; + rd.dest = 0; + closer = make_redirection (sd, r_close_this, rd); + closer->flags |= RX_INTERNAL; + closer->next = redirection_undo_list; + redirection_undo_list = closer; +} + +static void +add_exec_redirect (dummy_redirect) + REDIRECT *dummy_redirect; +{ + dummy_redirect->next = exec_redirection_undo_list; + exec_redirection_undo_list = dummy_redirect; +} + +/* Return 1 if the redirection specified by RI and REDIRECTOR alters the + standard input. */ +static int +stdin_redirection (ri, redirector) + enum r_instruction ri; + int redirector; +{ + switch (ri) + { + case r_input_direction: + case r_inputa_direction: + 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: + case r_close_this: + return (redirector == 0); + case r_output_direction: + case r_appending_to: + case r_duplicating_output: + case r_err_and_out: + case r_append_err_and_out: + case r_output_force: + case r_duplicating_output_word: + return (0); + } + return (0); +} + +/* Return non-zero if any of the redirections in REDIRS alter the standard + input. */ +int +stdin_redirects (redirs) + REDIRECT *redirs; +{ + REDIRECT *rp; + int n; + + for (n = 0, rp = redirs; rp; rp = rp->next) + n += stdin_redirection (rp->instruction, rp->redirector.dest); + return n; +} |