[v3 PATCH 2/3] input: Use lseek on stdin when possible

Herbert Xu <[email protected]>
Newsgroups org.kernel.vger.dash
Message-ID <9a8cc9e122ed6673281e21fc2581dd72eb6034c6.1717291846.git.herbert@gondor.apana.org.au>
For files that can be sought, use lseek instead of reading one
byte at a time.

Signed-off-by: Herbert Xu <[email protected]>
---
 src/eval.c    |   3 ++
 src/init.h    |   1 +
 src/input.c   | 108 +++++++++++++++++++++++++++++++++++---------------
 src/input.h   |   4 ++
 src/jobs.c    |   3 ++
 src/mkinit.c  |   6 +++
 src/options.c |   2 +-
 src/redir.c   |  26 +++++++-----
 src/trap.c    |   1 +
 9 files changed, 111 insertions(+), 43 deletions(-)

diff --git a/src/eval.c b/src/eval.c
index 140a734..ad2aa03 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -907,6 +907,8 @@ bail:
 		goto bail;
 
 	default:
+		flush_input();
+
 		/* Fork off a child process if necessary. */
 		if (!(flags & EV_EXIT) || have_traps()) {
 			INTOFF;
@@ -1142,6 +1144,7 @@ execcmd(int argc, char **argv)
 		iflag = 0;		/* exit on error */
 		mflag = 0;
 		optschanged();
+		flush_input();
 		shellexec(argv + 1, pathval(), 0);
 	}
 	return 0;
diff --git a/src/init.h b/src/init.h
index 4f98b5d..e117895 100644
--- a/src/init.h
+++ b/src/init.h
@@ -39,4 +39,5 @@ union node;
 void init(void);
 void exitreset(void);
 void forkreset(union node *);
+void postexitreset(void);
 void reset(void);
diff --git a/src/input.c b/src/input.c
index 193235d..b84ecec 100644
--- a/src/input.c
+++ b/src/input.c
@@ -32,11 +32,13 @@
  * SUCH DAMAGE.
  */
 
-#include <stdio.h>	/* defines BUFSIZ */
 #include <fcntl.h>
-#include <unistd.h>
+#include <stdbool.h>
+#include <stdio.h>	/* defines BUFSIZ */
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
+#include <unistd.h>
 
 /*
  * This file implements the input routines used by the parser.
@@ -59,12 +61,21 @@
 
 #define IBUFSIZ (BUFSIZ + PUNGETC_MAX + 1)
 
+struct stdin_state {
+	tcflag_t canon;
+	off_t seekable;
+	struct termios tios;
+};
 
 MKINIT struct parsefile basepf;	/* top level input file */
 MKINIT char basebuf[IBUFSIZ];	/* buffer for top level input file */
 MKINIT struct parsefile *toppf = &basepf;
+MKINIT struct stdin_state stdin_state;
 struct parsefile *parsefile = &basepf;	/* current input file */
 int whichprompt;		/* 1 == PS1, 2 == PS2 */
+int stdin_istty;
+
+MKINIT void input_init(void);
 
 STATIC void pushfile(void);
 static void popstring(void);
@@ -74,6 +85,7 @@ static int preadbuffer(void);
 
 #ifdef mkinit
 INCLUDE <stdio.h>
+INCLUDE <termios.h>
 INCLUDE <unistd.h>
 INCLUDE "input.h"
 INCLUDE "error.h"
@@ -82,6 +94,8 @@ INCLUDE "syntax.h"
 INIT {
 	basepf.nextc = basepf.buf = basebuf;
 	basepf.linno = 1;
+
+	input_init();
 }
 
 RESET {
@@ -104,8 +118,32 @@ FORKRESET {
 		parsefile->fd = 0;
 	}
 }
+
+POSTEXITRESET {
+	flush_input();
+}
 #endif
 
+void input_init(void)
+{
+	struct stdin_state *st = &stdin_state;
+	int istty;
+
+	istty = tcgetattr(0, &st->tios) + 1;
+	st->seekable = istty ? 0 : lseek(0, 0, SEEK_CUR) + 1;
+	st->canon = istty ? st->tios.c_lflag & ICANON : 0;
+	stdin_istty = istty;
+}
+
+static bool stdin_bufferable(void)
+{
+	struct stdin_state *st = &stdin_state;
+
+	if (stdin_istty < 0)
+		input_init();
+
+	return st->canon || st->seekable;
+}
 
 static void freestrings(struct strpush *sp)
 {
@@ -191,6 +229,7 @@ static int
 preadfd(void)
 {
 	char *buf = parsefile->buf;
+	int fd = parsefile->fd;
 	int unget;
 	int pnr;
 	int nr;
@@ -214,7 +253,7 @@ preadfd(void)
 retry:
 	nr = pnr;
 #ifndef SMALL
-	if (parsefile->fd == 0 && el) {
+	if (fd == 0 && el) {
 		static const char *rl_cp;
 		static int el_len;
 
@@ -237,38 +276,23 @@ retry:
 				rl_cp = 0;
 		}
 
-	} else
-#endif
-	if (parsefile->fd)
-		nr = read(parsefile->fd, buf, nr);
-	else {
-		nr = 0;
-
-		do {
-			int err;
-
-			err = read(0, buf, 1);
-			if (err <= 0) {
-				if (nr)
-					break;
-
-				nr = err;
-				if (errno != EWOULDBLOCK)
-					break;
-				if (stdin_clear_nonblock() < 0)
-					break;
-
-				out2str("sh: turning off NDELAY mode\n");
-				goto retry;
-			}
-
-			nr++;
-		} while (0);
+		return nr;
 	}
+#endif
+
+	if (!fd && !stdin_bufferable())
+		nr = 1;
+
+	nr = read(fd, buf, nr);
 
 	if (nr < 0) {
 		if (errno == EINTR && !(basepf.prev && pending_sig))
 			goto retry;
+		if (fd == 0 && errno == EWOULDBLOCK &&
+		    stdin_clear_nonblock() >= 0) {
+			out2str("sh: turning off NDELAY mode\n");
+			goto retry;
+		}
 	}
 	return nr;
 }
@@ -302,6 +326,8 @@ static int preadbuffer(void)
 	something = !first;
 
 	more = input_get_lleft(parsefile);
+
+	INTOFF;
 	if (more <= 0) {
 		int nr;
 
@@ -313,6 +339,7 @@ again:
 			input_set_lleft(parsefile, parsefile->nleft = 0);
 			if (!IS_DEFINED_SMALL && nr > 0)
 				goto save;
+			INTON;
 			return PEOF;
 		}
 	}
@@ -365,11 +392,10 @@ save:
 
 	if (parsefile->fd == 0 && hist && something) {
 		HistEvent he;
-		INTOFF;
 		history(hist, &he, first ? H_ENTER : H_APPEND,
 			parsefile->nextc);
-		INTON;
 	}
+	INTON;
 
 	if (vflag) {
 		out2str(parsefile->nextc);
@@ -590,3 +616,21 @@ popallfiles(void)
 {
 	unwindfiles(toppf);
 }
+
+void __attribute__((noinline)) flush_input(void)
+{
+	int left = basepf.nleft + input_get_lleft(&basepf);
+
+	if (stdin_state.seekable && left) {
+		INTOFF;
+		lseek(0, -left, SEEK_CUR);
+		input_set_lleft(&basepf, basepf.nleft = 0);
+		INTON;
+	}
+}
+
+void reset_input(void)
+{
+	flush_input();
+	stdin_istty = -1;
+}
diff --git a/src/input.h b/src/input.h
index c59d784..af1c1be 100644
--- a/src/input.h
+++ b/src/input.h
@@ -35,6 +35,7 @@
  */
 
 #include <limits.h>
+#include <stdbool.h>
 
 #ifdef SMALL
 #define IS_DEFINED_SMALL 1
@@ -94,6 +95,7 @@ struct parsefile {
 };
 
 extern struct parsefile *parsefile;
+extern int stdin_istty;
 
 /*
  * The input line number.  Input.c just defines this variable, and saves
@@ -113,6 +115,8 @@ void pushstdin(void);
 void popfile(void);
 void unwindfiles(struct parsefile *);
 void popallfiles(void);
+void flush_input(void);
+void reset_input(void);
 
 static inline int input_get_lleft(struct parsefile *pf)
 {
diff --git a/src/jobs.c b/src/jobs.c
index 02ec6f4..247fab5 100644
--- a/src/jobs.c
+++ b/src/jobs.c
@@ -969,6 +969,9 @@ forkshell(struct job *jp, union node *n, int mode)
 	int pid;
 
 	TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+
+	flush_input();
+
 	pid = fork();
 	if (pid == 0)
 		forkchild(jp, n, mode);
diff --git a/src/mkinit.c b/src/mkinit.c
index 870b64d..2514ebf 100644
--- a/src/mkinit.c
+++ b/src/mkinit.c
@@ -119,6 +119,11 @@ char forkreset[] = "\
  * This routine is called when we enter a subshell.\n\
  */\n";
 
+char postexitreset[] = "\
+/*\n\
+ * This routine is called in exitshell.\n\
+ */\n";
+
 char reset[] = "\
 /*\n\
  * This routine is called when an error or an interrupt occurs in an\n\
@@ -130,6 +135,7 @@ struct event event[] = {
 	{"INIT", "init", init},
 	{"EXITRESET", "exitreset", exitreset},
 	{"FORKRESET", "forkreset", forkreset, "union node *n"},
+	{"POSTEXITRESET", "postexitreset", postexitreset},
 	{"RESET", "reset", reset},
 	{NULL, NULL}
 };
diff --git a/src/options.c b/src/options.c
index 34e500a..c74e4fe 100644
--- a/src/options.c
+++ b/src/options.c
@@ -142,7 +142,7 @@ procargs(int argc, char **argv)
 			sh_error("-c requires an argument");
 		sflag = 1;
 	}
-	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
+	if (iflag == 2 && sflag == 1 && stdin_istty && isatty(1))
 		iflag = 1;
 	if (mflag == 2)
 		mflag = iflag;
diff --git a/src/redir.c b/src/redir.c
index c57d745..8d1c8f6 100644
--- a/src/redir.c
+++ b/src/redir.c
@@ -46,16 +46,17 @@
  * Code for dealing with input/output redirection.
  */
 
-#include "main.h"
-#include "shell.h"
-#include "nodes.h"
-#include "jobs.h"
-#include "options.h"
-#include "expand.h"
-#include "redir.h"
-#include "output.h"
-#include "memalloc.h"
 #include "error.h"
+#include "expand.h"
+#include "input.h"
+#include "jobs.h"
+#include "main.h"
+#include "memalloc.h"
+#include "nodes.h"
+#include "options.h"
+#include "output.h"
+#include "redir.h"
+#include "shell.h"
 #include "system.h"
 #include "trap.h"
 
@@ -142,6 +143,8 @@ redirect(union node *redir, int flags)
 			continue;
 
 		fd = n->nfile.fd;
+		if (fd == 0)
+			reset_input();
 
 		if (sv) {
 			int closed;
@@ -415,8 +418,11 @@ popredir(int drop)
 				close(i);
 			break;
 		default:
-			if (!drop)
+			if (!drop) {
+				if (i == 0)
+					reset_input();
 				dup2(rp->renamed[i], i);
+			}
 			close(rp->renamed[i]);
 			break;
 		}
diff --git a/src/trap.c b/src/trap.c
index 4eb9849..aebffa0 100644
--- a/src/trap.c
+++ b/src/trap.c
@@ -426,6 +426,7 @@ exitshell(void)
 	}
 out:
 	exitreset();
+	postexitreset();
 	/*
 	 * Disable job control so that whoever had the foreground before we
 	 * started can get it back.
-- 
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.