diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2022-04-27 20:46:46 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2022-04-27 20:46:46 -0700 |
commit | 7226354813fe0b8475cc99b01968cf8054d448eb (patch) | |
tree | 3df866aec175f893f68e660c768d02483d9ea5da | |
parent | dd43ac0303a1e435650eda722af236761e730cf6 (diff) | |
download | pw-7226354813fe0b8475cc99b01968cf8054d448eb.tar.gz pw-7226354813fe0b8475cc99b01968cf8054d448eb.tar.bz2 pw-7226354813fe0b8475cc99b01968cf8054d448eb.zip |
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.
-rw-r--r-- | pw.c | 141 |
1 files changed, 85 insertions, 56 deletions
@@ -11,6 +11,8 @@ #include <termios.h> #include <sys/ioctl.h> #include <sys/time.h> +#include <fcntl.h> +#include <errno.h> #include <regex.h> 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); |