[v3 PATCH 3/3] input: Use tee(2) for stdin pipe

Herbert Xu <[email protected]>
Newsgroups org.kernel.vger.dash
Message-ID <8dc9e47ba2a151d7c8972d68fa86d450f7eb30c7.1717291846.git.herbert@gondor.apana.org.au>
use tee(2) to peek at pipes in order to avoid reading one byte at
a time.

Signed-off-by: Herbert Xu <[email protected]>
---
 configure.ac |  2 +-
 src/input.c  | 71 ++++++++++++++++++++++++++++++++++++++++++++++------
 src/system.h |  7 ++++++
 3 files changed, 72 insertions(+), 8 deletions(-)

diff --git a/configure.ac b/configure.ac
index 9b4db72..338d5bd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -89,7 +89,7 @@ dnl Checks for library functions.
 AC_CHECK_FUNCS(bsearch faccessat getpwnam getrlimit isalpha killpg \
 	       memfd_create mempcpy \
 	       sigsetmask stpcpy strchrnul strsignal strtod strtoimax \
-	       strtoumax sysconf)
+	       strtoumax sysconf tee)
 
 dnl Check whether it's worth working around FreeBSD PR kern/125009.
 dnl The traditional behavior of access/faccessat is crazy, but
diff --git a/src/input.c b/src/input.c
index b84ecec..8f8c173 100644
--- a/src/input.c
+++ b/src/input.c
@@ -57,14 +57,18 @@
 #include "redir.h"
 #include "shell.h"
 #include "syntax.h"
+#include "system.h"
 #include "trap.h"
 
 #define IBUFSIZ (BUFSIZ + PUNGETC_MAX + 1)
 
+MKINIT
 struct stdin_state {
 	tcflag_t canon;
 	off_t seekable;
 	struct termios tios;
+	int pip[2];
+	int pending;
 };
 
 MKINIT struct parsefile basepf;	/* top level input file */
@@ -85,6 +89,7 @@ static int preadbuffer(void);
 
 #ifdef mkinit
 INCLUDE <stdio.h>
+INCLUDE <string.h>
 INCLUDE <termios.h>
 INCLUDE <unistd.h>
 INCLUDE "input.h"
@@ -117,6 +122,11 @@ FORKRESET {
 		close(parsefile->fd);
 		parsefile->fd = 0;
 	}
+	if (stdin_state.pip[0]) {
+		close(stdin_state.pip[0]);
+		close(stdin_state.pip[1]);
+		memset(stdin_state.pip, 0, sizeof(stdin_state.pip));
+	}
 }
 
 POSTEXITRESET {
@@ -145,6 +155,43 @@ static bool stdin_bufferable(void)
 	return st->canon || st->seekable;
 }
 
+static void flush_tee(void *buf, int nr, int pending)
+{
+	while (pending > 0) {
+		int err;
+
+		err = read(0, buf, nr > pending ? pending : nr);
+		if (err > 0)
+			pending -= err;
+	}
+}
+
+static int stdin_tee(void *buf, int nr)
+{
+	int err;
+
+	if (stdin_istty)
+		return 0;
+
+	if (!stdin_state.pip[0]) {
+		err = pipe(stdin_state.pip);
+		if (err < 0)
+			return err;
+		if (stdin_state.pip[0] < 10)
+			stdin_state.pip[0] = savefd(stdin_state.pip[0],
+						    stdin_state.pip[0]);
+		if (stdin_state.pip[1] < 10)
+			stdin_state.pip[1] = savefd(stdin_state.pip[1],
+						    stdin_state.pip[1]);
+	}
+
+	flush_tee(buf, nr, stdin_state.pending);
+
+	err = tee(0, stdin_state.pip[1], nr, 0);
+	stdin_state.pending = err;
+	return err;
+}
+
 static void freestrings(struct strpush *sp)
 {
 	INTOFF;
@@ -280,10 +327,17 @@ retry:
 	}
 #endif
 
-	if (!fd && !stdin_bufferable())
-		nr = 1;
+	if (!fd && !stdin_bufferable()) {
+		nr = stdin_tee(buf, nr);
+		fd = stdin_state.pip[0];
+		if (nr <= 0) {
+			fd = 0;
+			nr = 1;
+		}
+	}
 
-	nr = read(fd, buf, nr);
+	if (nr >= 0)
+		nr = read(fd, buf, nr);
 
 	if (nr < 0) {
 		if (errno == EINTR && !(basepf.prev && pending_sig))
@@ -621,12 +675,15 @@ void __attribute__((noinline)) flush_input(void)
 {
 	int left = basepf.nleft + input_get_lleft(&basepf);
 
-	if (stdin_state.seekable && left) {
-		INTOFF;
+	INTOFF;
+	if (stdin_state.seekable && left)
 		lseek(0, -left, SEEK_CUR);
-		input_set_lleft(&basepf, basepf.nleft = 0);
-		INTON;
+	else if (stdin_state.pending > left) {
+		flush_tee(basebuf, BUFSIZ, stdin_state.pending - left);
+		stdin_state.pending = 0;
 	}
+	input_set_lleft(&basepf, basepf.nleft = 0);
+	INTON;
 }
 
 void reset_input(void)
diff --git a/src/system.h b/src/system.h
index 6b31d52..e7f968b 100644
--- a/src/system.h
+++ b/src/system.h
@@ -118,6 +118,13 @@ long sysconf(int) __attribute__((__noreturn__));
 int isblank(int c);
 #endif
 
+#ifndef HAVE_TEE
+static inline ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags)
+{
+	return -1;
+}
+#endif
+
 #ifndef HAVE_FNMATCH
 static inline int fnmatch(const char *pattern, const char *string, int flags)
 {
-- 
2.39.2
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.