From 7226354813fe0b8475cc99b01968cf8054d448eb Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Wed, 27 Apr 2022 20:46:46 -0700 Subject: Redesign poll structure to avoid one byte reads from stdin. We keep stdin fully buffered, but switch the descriptor to nonblocking. We then try to accumulate a line of input above the poll() call. We only poll stdin's descriptor if we hit EOF while trying to read a line, and errno indicates EAGAIN or EWOULDBLOCK. In cases when we know that there is more data in stdin, we poll with an interval of 0, because only the TTY is being polled. When we know that there is no more data in stdin (actual EOF), we now poll with an infinite interval. --- pw.c | 141 ++++++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 85 insertions(+), 56 deletions(-) diff --git a/pw.c b/pw.c index 9749933..60cdf13 100644 --- a/pw.c +++ b/pw.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include enum status_flags { @@ -120,6 +122,21 @@ static char *addch(char *line, int ch) abort(); } +static char *addchesc(char *line, int ch) +{ + if (ch == 127) { + line = addch(line, '^'); + line = addch(line, '?'); + } else if (ch < 32) { + line = addch(line, '^'); + line = addch(line, ch + 64); + } else { + line = addch(line, ch); + } + + return line; +} + static char *getln(FILE *stream) { char *line = 0; @@ -136,15 +153,7 @@ static char *getln(FILE *stream) return addch(line, 0); } - if (ch == 127) { - line = addch(line, '^'); - line = addch(line, '?'); - } else if (ch < 32) { - line = addch(line, '^'); - line = addch(line, ch + 64); - } else { - line = addch(line, ch); - } + line = addchesc(line, ch); } } @@ -294,7 +303,7 @@ static void execute(char *cmd) int main(int argc, char **argv) { - char *line; + char *line = 0; FILE *tty = fopen("/dev/tty", "r+"); int maxlines = 15, nlines = 0; int poll_interval = 1000; @@ -383,12 +392,14 @@ int main(int argc, char **argv) panic("unable to set TTY parameters"); setvbuf(tty, NULL, _IONBF, 0); - setvbuf(stdin, NULL, _IONBF, 0); + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + panic("unable to set stdin nonblocking"); for (unsigned stat = stat_dirty, hpos = 0, kbd_state = kbd_cmd, lasttime = ~0U; kbd_state != kbd_exit ;) { - int force = 0; + int force = 0, nfds = 2, pollms = poll_interval; struct pollfd pe[2] = { { .fd = ttyfd, .events = POLLIN | POLLHUP | POLLERR }, { .fd = fd, .events = POLLIN | POLLHUP | POLLERR }, @@ -412,59 +423,77 @@ int main(int argc, char **argv) } } - if (force) { - redraw(circbuf, nlines, hpos, columns, stat, curcmd); - stat &= ~(stat_dirty | stat_trgrd); - } - - if (poll(pe, ((stat & stat_eof)) ? 1 : 2, poll_interval) <= 0) { - if ((stat & stat_dirty) && nlines == maxlines) { - redraw(circbuf, nlines, hpos, columns, stat, curcmd); - stat &= ~stat_dirty; - } - if (kbd_state == kbd_esc || kbd_state == kbd_result) { - kbd_state = kbd_cmd; - curcmd = 0; - clrline(); - drawstatus(stat, curcmd); - } - } else { - if ((stat & stat_eof) == 0 && pe[1].revents) { - if ((line = getln(stdin))) { - if ((stat & stat_htmode)) - if (regexec(&trigex, line, 0, NULL, 0) == 0) - stat |= stat_trgrd; - if (nlines == maxlines) { - dsdrop(circbuf[0]); - memmove(circbuf, circbuf + 1, (nlines - 1) * sizeof *circbuf); - circbuf[nlines - 1] = line; - stat |= stat_dirty; - if ((stat & stat_ttmode)) - if (regexec(&trigex, circbuf[0], 0, NULL, 0) == 0) - stat |= stat_trgrd; - } else { - circbuf[nlines++] = line; - if ((stat & stat_susp) == 0) { - snapshot[snaplines++] = dsref(line); - clrline(); - drawline(line, hpos, columns); - drawstatus(stat, curcmd); - } - } - } else { - if (auto_quit) - kbd_state = kbd_exit; - else + if ((stat & stat_eof) == 0) { + int ch; + while ((ch = getc(stdin)) != EOF && ch != '\n') + line = addchesc(line, ch); + if (ch == EOF) { + if (feof(stdin) || (errno != EAGAIN && errno != EWOULDBLOCK)) { + nfds = 1; + if (!auto_quit) stat |= stat_eof; redraw(circbuf, nlines, hpos, columns, stat, curcmd); stat |= stat_eof; stat &= ~stat_dirty; if (!ferror(stdin)) exit_status = 0; - continue; + if (auto_quit) + break; + } + clearerr(stdin); + } else { + nfds = 1; + line = addch(line, 0); + if ((stat & stat_htmode)) + if (regexec(&trigex, line, 0, NULL, 0) == 0) + stat |= stat_trgrd; + if (nlines == maxlines) { + dsdrop(circbuf[0]); + memmove(circbuf, circbuf + 1, (nlines - 1) * sizeof *circbuf); + circbuf[nlines - 1] = line; + stat |= stat_dirty; + if ((stat & stat_ttmode)) + if (regexec(&trigex, circbuf[0], 0, NULL, 0) == 0) + stat |= stat_trgrd; + } else { + circbuf[nlines++] = line; + if ((stat & stat_susp) == 0) { + snapshot[snaplines++] = dsref(line); + clrline(); + drawline(line, hpos, columns); + drawstatus(stat, curcmd); + } } + line = 0; } + } else { + nfds = 1; + } + + if (force) { + redraw(circbuf, nlines, hpos, columns, stat, curcmd); + stat &= ~(stat_dirty | stat_trgrd); + } + + if ((stat & stat_eof)) + pollms = -1; + else if (nfds < 2) + pollms = 0; + if (poll(pe, nfds, pollms) <= 0) { + if (pollms) { + if ((stat & stat_dirty) && nlines == maxlines) { + redraw(circbuf, nlines, hpos, columns, stat, curcmd); + stat &= ~stat_dirty; + } + if (kbd_state == kbd_esc || kbd_state == kbd_result) { + kbd_state = kbd_cmd; + curcmd = 0; + clrline(); + drawstatus(stat, curcmd); + } + } + } else { if ((pe[0].revents)) { int ch = getc(tty); -- cgit v1.2.3