For files that can be sought, use lseek instead of reading one byte at a time. Signed-off-by: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx> --- 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