diff --git a/command.h b/command.h index 3249516..ef611a4 100644 --- a/command.h +++ b/command.h @@ -186,6 +186,7 @@ typedef struct element { #define CMD_COPROC_SUBSHELL 0x1000 #define CMD_LASTPIPE 0x2000 #define CMD_STDPATH 0x4000 /* use standard path for command lookup */ +#define CMD_STDOUT_UNBUFFERED 0x8000 /* Do not buffer stdout. */ /* What a command looks like. */ typedef struct command { diff --git a/execute_cmd.c b/execute_cmd.c index 8b3c83a..61d3647 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -567,6 +567,11 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, volatile char *ofifo_list; #endif +#ifdef DRW + fprintf(stderr, "execute_command_internal %p %X\n", command, (command ? command->flags : 0)); + fprintf(stderr, "%s\n", make_command_string(command)); +#endif + if (breaking || continuing) return (last_command_exit_value); if (command == 0 || read_but_dont_execute) @@ -836,6 +841,8 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, command->value.Simple->flags |= CMD_IGNORE_RETURN; if (command->flags & CMD_STDIN_REDIR) command->value.Simple->flags |= CMD_STDIN_REDIR; + if (command->flags & CMD_STDOUT_UNBUFFERED) + command->value.Simple->flags |= CMD_STDOUT_UNBUFFERED; line_number_for_err_trap = line_number = command->value.Simple->line; exec_result = @@ -2466,6 +2473,11 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) struct fd_bitmap *fd_bitmap; pid_t lastpid; +#ifdef DRW + fprintf(stderr, "execute_pipeline %p %X\n", command, command->flags); + fprintf(stderr, "%s\n", make_command_string(command)); +#endif + #if defined (JOB_CONTROL) sigset_t set, oset; BLOCK_CHILD (set, oset); @@ -2477,7 +2489,9 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) cmd = command; while (cmd && cmd->type == cm_connection && - cmd->value.Connection && cmd->value.Connection->connector == '|') + cmd->value.Connection && + (cmd->value.Connection->connector == '|' || + cmd->value.Connection->connector == GREATER_BAR_GREATER)) { /* Make a pipeline between the two commands. */ if (pipe (fildes) < 0) @@ -2551,6 +2565,10 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) dispose_fd_bitmap (fd_bitmap); discard_unwind_frame ("pipe-file-descriptors"); + if (cmd->flags & CMD_STDOUT_UNBUFFERED) + /* If cmd should not buffer stdout, then cmd...->second should + not buffer stdout. */ + cmd->value.Connection->second->flags |= CMD_STDOUT_UNBUFFERED; cmd = cmd->value.Connection->second; } @@ -2644,6 +2662,11 @@ execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close) int ignore_return, exec_result, was_error_trap, invert; volatile int save_line_number; +#ifdef DRW + fprintf(stderr, "execute_connection %p %X\n", command, (command ? command->flags : 0)); + fprintf(stderr, "%s\n", make_command_string(command)); +#endif + ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0; switch (command->value.Connection->connector) @@ -4158,6 +4181,16 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) SHELL_VAR *func; volatile int old_builtin, old_command_builtin; +#ifdef DRW + fprintf(stderr, "execute_simple_command %p %X\n", simple_command, (simple_command ? simple_command->flags : 0)); + { + WORD_LIST *w; + for (w = simple_command->words; w; w = w->next) + fprintf(stderr, "%s ", w->word->word); + fprintf(stderr, "\n"); + } +#endif + result = EXECUTION_SUCCESS; special_builtin_failed = builtin_is_special = 0; command_line = (char *)0; @@ -5425,6 +5458,25 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out, exit (EXECUTION_FAILURE); } +#ifdef DRW + fprintf(stderr, "execute_disk_command %X '%s'\n", cmdflags, command_line); +#endif /* DRW */ + if (cmdflags & CMD_STDOUT_UNBUFFERED) + { + /* Set the environment to request unbuffered stdout from the new + program. */ + struct stat buf; + char value[40]; + fstat(1, &buf); + sprintf(value, "STDOUT_UNBUFFERED=%lu:%lu", (unsigned long) buf.st_dev, + (unsigned long) buf.st_ino); + maybe_make_export_env (); + export_env = add_or_supercede_exported_var (value, 1); +#ifdef DRW + fprintf(stderr, "execute_disk_command set '%s'\n", value); +#endif /* DRW */ + } + if (async) interactive = old_interactive; diff --git a/make_cmd.c b/make_cmd.c index ecbbfd6..cd8e4c6 100644 --- a/make_cmd.c +++ b/make_cmd.c @@ -39,6 +39,7 @@ #include "parser.h" #include "flags.h" #include "input.h" +#include "y.tab.h" #if defined (JOB_CONTROL) #include "jobs.h" @@ -189,11 +190,19 @@ command_connect (com1, com2, connector) { CONNECTION *temp; + if (connector == GREATER_BAR_GREATER || connector == GREATER_BAR_GREATER_AND) + com1->flags |= CMD_STDOUT_UNBUFFERED; + temp = (CONNECTION *)xmalloc (sizeof (CONNECTION)); temp->connector = connector; temp->first = com1; temp->second = com2; - return (make_command (cm_connection, (SIMPLE_COM *)temp)); + COMMAND *c = make_command (cm_connection, (SIMPLE_COM *)temp); +#ifdef DRW + fprintf(stderr, "command_connect com1 = %p %X, com2 = %p %X, c = %p %X, connector = %d\n", com1, (com1 ? com1->flags : 0), com2, (com2 ? com2->flags : 0), c, (c ? c->flags : 0), connector); + fprintf(stderr, "%s\n", make_command_string(c)); +#endif + return (c); } static COMMAND * diff --git a/parse.y b/parse.y index 3ff87bc..49c2162 100644 --- a/parse.y +++ b/parse.y @@ -214,6 +214,8 @@ static size_t shell_input_line_propsize = 0; # define set_line_mbstate() #endif +static void redirect_stderr_to_stdout __P((COMMAND *)); + extern int yyerror __P((const char *)); #ifdef DEBUG @@ -352,6 +354,7 @@ static FILE *yyerrstream; %token GREATER_AND SEMI_SEMI SEMI_AND SEMI_SEMI_AND %token LESS_LESS_MINUS AND_GREATER AND_GREATER_GREATER LESS_GREATER %token GREATER_BAR BAR_AND +%token GREATER_BAR_GREATER GREATER_BAR_GREATER_AND /* The types that the various syntactical units return. */ @@ -375,7 +378,7 @@ static FILE *yyerrstream; %left '&' ';' '\n' yacc_EOF %left AND_AND OR_OR -%right '|' BAR_AND +%right '|' BAR_AND GREATER_BAR_GREATER GREATER_BAR_GREATER_AND %% inputunit: simple_list simple_list_terminator @@ -1283,29 +1286,18 @@ pipeline_command: pipeline pipeline: pipeline '|' newline_list pipeline { $$ = command_connect ($1, $4, '|'); } + | pipeline GREATER_BAR_GREATER newline_list pipeline + { $$ = command_connect ($1, $4, GREATER_BAR_GREATER); } | pipeline BAR_AND newline_list pipeline { - /* Make cmd1 |& cmd2 equivalent to cmd1 2>&1 | cmd2 */ - COMMAND *tc; - REDIRECTEE rd, sd; - REDIRECT *r; - - tc = $1->type == cm_simple ? (COMMAND *)$1->value.Simple : $1; - sd.dest = 2; - rd.dest = 1; - r = make_redirection (sd, r_duplicating_output, rd, 0); - if (tc->redirects) - { - register REDIRECT *t; - for (t = tc->redirects; t->next; t = t->next) - ; - t->next = r; - } - else - tc->redirects = r; - + redirect_stderr_to_stdout($1); $$ = command_connect ($1, $4, '|'); } + | pipeline GREATER_BAR_GREATER_AND newline_list pipeline + { + redirect_stderr_to_stdout($1); + $$ = command_connect ($1, $4, GREATER_BAR_GREATER); + } | command { $$ = $1; } ; @@ -1319,6 +1311,31 @@ timespec: TIME ; %% +/* Make cmd1 |& cmd2 equivalent to cmd1 2>&1 | cmd2 + Similarly, cmd1 >|>& cmd2 is equivalent to cmd1 2>&1 >|> cmd2 */ +static void +redirect_stderr_to_stdout (pipeline) + COMMAND *pipeline; +{ + COMMAND *tc; + REDIRECTEE rd, sd; + REDIRECT *r; + + tc = pipeline->type == cm_simple ? (COMMAND *)pipeline->value.Simple : pipeline; + sd.dest = 2; + rd.dest = 1; + r = make_redirection (sd, r_duplicating_output, rd, 0); + if (tc->redirects) + { + register REDIRECT *t; + for (t = tc->redirects; t->next; t = t->next) + ; + t->next = r; + } + else + tc->redirects = r; +} + /* Initial size to allocate for tokens, and the amount to grow them by. */ #define TOKEN_DEFAULT_INITIAL_SIZE 496 @@ -2195,6 +2212,8 @@ STRING_INT_ALIST other_token_alist[] = { { "&>>", AND_GREATER_GREATER }, { "<>", LESS_GREATER }, { ">|", GREATER_BAR }, + { ">|>", GREATER_BAR_GREATER }, + { ">|>&", GREATER_BAR_GREATER_AND }, { "|&", BAR_AND }, { "EOF", yacc_EOF }, /* Tokens whose value is the character itself */ @@ -3375,7 +3394,19 @@ itrace("shell_getc: bash_input.location.string = `%s'", bash_input.location.stri else if MBTEST(character == '<' && peek_char == '>') return (LESS_GREATER); else if MBTEST(character == '>' && peek_char == '|') - return (GREATER_BAR); + { + peek_char = shell_getc (1); + if MBTEST(peek_char == '>') + { + peek_char = shell_getc (1); + if MBTEST(peek_char == '&') + return (GREATER_BAR_GREATER_AND); + shell_ungetc (peek_char); + return (GREATER_BAR_GREATER); + } + shell_ungetc (peek_char); + return (GREATER_BAR); + } else if MBTEST(character == '&' && peek_char == '>') { peek_char = shell_getc (1); @@ -5395,6 +5426,8 @@ reserved_word_acceptable (toksym) case ESAC: case FI: case IF: + case GREATER_BAR_GREATER_AND: + case GREATER_BAR_GREATER: case OR_OR: case SEMI_SEMI: case SEMI_AND: diff --git a/print_cmd.c b/print_cmd.c index 9aa6557..1da3844 100644 --- a/print_cmd.c +++ b/print_cmd.c @@ -277,6 +277,12 @@ make_command_string_internal (command) skip_this_indent++; break; + case GREATER_BAR_GREATER: + print_deferred_heredocs (" >|> "); + if (command->value.Connection->second) + skip_this_indent++; + break; + case ';': if (deferred_heredocs == 0) {