Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > gnu.bash.bug > #16224

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

From worley@alum.mit.edu (Dale R. Worley)
Newsgroups gnu.bash.bug
Subject Proposed new feature for bash: unbuffered pipes, part 3: bash.diff
Date 2020-04-21 20:39 -0400
Message-ID <mailman.920.1587516005.3066.bug-bash@gnu.org> (permalink)
References <87ftcwwdyg.fsf@hobgoblin.ariadne.com>

Show all headers | View raw


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

Back to gnu.bash.bug | Previous | Next | Find similar


Thread

Proposed new feature for bash:  unbuffered pipes, part 3: bash.diff worley@alum.mit.edu (Dale R. Worley) - 2020-04-21 20:39 -0400

csiph-web