Fix buffering bug: use read() for reading instead of fgets()

stdin is buffered and using fgets() invokes read(), which could read
multiple lines at one, while reporting only first of them.

Testcase:

    { echo a; echo b; while sleep 1; do :; done; } | lemonbar

only outputs a.
This commit is contained in:
shdown 2017-02-06 13:14:17 +03:00
parent 50fe7d2083
commit eb0e9e79b9

View File

@ -1263,6 +1263,7 @@ main (int argc, char **argv)
xcb_expose_event_t *expose_ev;
xcb_button_press_event_t *press_ev;
char input[4096] = {0, };
size_t input_off = 0;
bool permanent = false;
int geom_v[4] = { -1, -1, 0, 0 };
int ch, areas;
@ -1352,16 +1353,50 @@ main (int argc, char **argv)
break;
if (poll(pollin, 2, -1) > 0) {
if (pollin[0].revents & POLLHUP) { // No more data...
if (pollin[0].revents & (POLLIN | POLLHUP)) { // New input (or no more data)
if (input_off == sizeof(input)) {
input_off = 0;
}
ssize_t r;
while ((r = read(0, input + input_off, sizeof(input) - input_off)) < 0 && errno == EINTR)
;
if (r < 0) {
perror("read");
break;
} else if (r == 0) { // No more data...
if (permanent) pollin[0].fd = -1; // ...null the fd and continue polling :D
else break; // ...bail out
} else {
const size_t end = input_off + r;
// find the last occurence of \n and one before it
size_t last = -1, prelast = -1;
while (1) {
const size_t from = last + 1;
if (from == end) {
break;
}
if (pollin[0].revents & POLLIN) { // New input, process it
if (fgets(input, sizeof(input), stdin) == NULL)
break; // EOF received
parse(input);
const char *t = memchr(input + from, '\n', end - from);
if (!t) {
break;
}
prelast = last;
last = t - input;
}
if (last == -1) {
// no newline found
input_off = end;
} else {
input[last] = '\0';
parse(input + (prelast + 1));
redraw = true;
const size_t n = end - (last + 1);
if (n) {
memmove(input, input + (last + 1), n);
}
input_off = n;
}
}
}
if (pollin[1].revents & POLLIN) { // The event comes from the Xorg server
while ((ev = xcb_poll_for_event(c))) {