From eb0e9e79b9a35c3093f6e2e90d9fbd175a2305ef Mon Sep 17 00:00:00 2001 From: shdown Date: Mon, 6 Feb 2017 13:14:17 +0300 Subject: [PATCH] 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. --- lemonbar.c | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/lemonbar.c b/lemonbar.c index 43aab8c..415b20b 100644 --- a/lemonbar.c +++ b/lemonbar.c @@ -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 (permanent) pollin[0].fd = -1; // ...null the fd and continue polling :D - else break; // ...bail out - } - if (pollin[0].revents & POLLIN) { // New input, process it - if (fgets(input, sizeof(input), stdin) == NULL) - break; // EOF received + 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; + } + 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; - parse(input); - 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))) {