aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2022-04-27 20:46:46 -0700
committerKaz Kylheku <kaz@kylheku.com>2022-04-27 20:46:46 -0700
commit7226354813fe0b8475cc99b01968cf8054d448eb (patch)
tree3df866aec175f893f68e660c768d02483d9ea5da
parentdd43ac0303a1e435650eda722af236761e730cf6 (diff)
downloadpw-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.c141
1 files 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 <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);