bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Proposed new feature for bash: unbuffered pipes, part 3: bash.diff


From: Dale R. Worley
Subject: Proposed new feature for bash: unbuffered pipes, part 3: bash.diff
Date: Tue, 21 Apr 2020 20:39:51 -0400

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)
                {



reply via email to

[Prev in Thread] Current Thread [Next in Thread]