diff options
-rw-r--r-- | Makefile | 3 | ||||
-rwxr-xr-x | input.sh | 40 | ||||
-rw-r--r-- | tailburst.c | 110 |
3 files changed, 114 insertions, 39 deletions
@@ -7,7 +7,6 @@ CFLAGS+=-g -Wall -Werror all: $(MAIN) $(MAIN): $(OBJECTS) - $(CC) $(LDFLAGS) $< -o $@ - + clean: rm -f *.o $(MAIN) diff --git a/input.sh b/input.sh new file mode 100755 index 0000000..00b6dca --- /dev/null +++ b/input.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +TEXT=() + +case "$1" in +1) + t="" + for _ in {1..2000}; do + t+="0123456789" + done + TEXT=("$t" one) + ;; +2) + t="" + for _ in {1..100}; do + t+="0123456789"$'\n' + done + for _ in {1..5}; do + TEXT+=("$t""0123456789") + done + ;; +3) + t="" + for _ in {1..100}; do + t+="0123456789" + done + for _ in {1..5}; do + t="$t"$'\n'"$t" + done + TEXT=("$t" one) + ;; +*) + TEXT=($'one\ntwo\nthree' one two $'three\nfour' five) + ;; +esac + +for t in "${TEXT[@]}"; do + printf "%s\n" "$t" + sleep 1 +done diff --git a/tailburst.c b/tailburst.c index 1c621f2..e150e8c 100644 --- a/tailburst.c +++ b/tailburst.c @@ -6,7 +6,7 @@ #include <errno.h> #include <stdlib.h> -#define BUFLEN 1024 +#define MAXBUFLEN 8096 #define DEBUG 0 #define pr_debug(fmt, args...) if (DEBUG) printf(fmt, ##args) @@ -30,69 +30,101 @@ ssize_t fdgetline(int fd, struct getline_states *line) { return -ENOMEM; line->base[0] = '\0'; line->read = 0; - line->next = NULL; + line->next = line->base; + line->cur = NULL; } - /* Skip after previously returned line */ - if (line->next) - line->cur = line->next; - else - line->cur = line->base; - /* Do we already have a full line in buffer? */ - if ((str = strchr(line->cur, '\n'))) { + if (line->read > 0 && (str = strchr(line->next, '\n'))) { + line->cur = line->next; line->next = str+1; str[0] = '\0'; pr_debug("next already buffered, %p %p: %s\n", str, line->cur, line->cur); - return str - line->cur; + return 1; } /* No such luck, move current to base and read what we can */ - if (line->cur != line->base) { - pr_debug("moving cur: %p, base: %p, read: %zd, moving %zd\n", line->cur, line->base, line->read, line->cur - line->base); + if (line->cur && line->cur != line->base) { + pr_debug("moving cur: %p, base: %p, read: %zd, moving %zd\n", + line->cur, line->base, line->read, line->cur - line->base); line->read -= line->cur - line->base; - memmove(line->base, line->cur, line->read); + line->next -= line->cur - line->base; + memmove(line->base, line->cur, line->read + 1); /* also move trailing \0 */ line->cur = line->base; } - /* clear errno before read */ - errno = 0; - - while ((nread = read(fd, line->base + line->read, line->size - line->read)) > 0 ) { - pr_debug("read %zd\n", nread); + /* search from here */ + while (1) { + size_t toread = line->size - line->read - 1; + /* if there is no current line we need to keep some leeway + * for the next read... */ + if (!line->cur) + toread /= 2; + errno = 0; + nread = read(fd, line->base + line->read, toread); + pr_debug("read %zd/%zd (%d)\n", nread, toread, errno); + if (nread < 0) { + if (errno == EAGAIN) + return 0; + break; + } + if (nread == 0) { + /* EOF */ + return -1; + } line->read += nread; + line->base[line->read] = '\0'; /* Check if we have a full line now */ - if ((str = strchr(line->base, '\n'))) { - line->next = str+1; + if ((str = strchr(line->next, '\n'))) { + line->cur = line->next; + line->next = str + 1; str[0] = '\0'; - pr_debug("found stuff, %p %p %p: %s\n", str, line->base, line->cur, line->cur); - return str - line->base; + pr_debug("found stuff, %p %p %p: %s\n", + str, line->base, line->cur, line->cur); + return 1; } - /* realloc bigger buffer and try again */ + /* realloc bigger buffer and try again if sensible */ + if (line->size - line->read > line->size / 2) + continue; + if (line->size > MAXBUFLEN / 2) { + /* pretend this was a full line.. */ + line->cur = line->next; + line->next = line->base + line->read; + pr_debug("returning full line early, %p %p %p: %s\n", + str, line->base, line->cur, line->cur); + return 1; + } line->size *= 2; - line->base = realloc(line->base, line->size); - if (line->base == NULL) + pr_debug("growing buffer to %zd\n", line->size); + + char *newbase = realloc(line->base, line->size); + if (newbase == NULL) return -ENOMEM; + if (line->cur) { + pr_debug("base-cur was %p-%p, new %p-%p\n", + line->base, line->cur, newbase, + newbase + (line->cur - line->base)); + line->cur = newbase + (line->cur - line->base); + } + line->next = newbase + (line->next - line->base); + line->base = newbase; } - pr_debug("got here? %zd %d\n", nread, errno); - line->next = line->cur; if (errno) return -errno; - return 0; /* no line read */ + return -1; /* unknown error? */ } int main(int argc, char **argv) { int flags, fd = 0; - ssize_t nread; + ssize_t nread = 0; struct pollfd pollfd; struct getline_states line = { 0 }; - char *cur; /* non-buffered output */ setlinebuf(stdout); @@ -105,17 +137,21 @@ int main(int argc, char **argv) pollfd.fd = fd; pollfd.events = POLLIN; while (poll(&pollfd, 1, -1) > 0) { - cur = NULL; - while ((nread = fdgetline(fd, &line)) > 0) - cur = line.cur; + line.cur = NULL; + while ((nread = fdgetline(fd, &line)) > 0); - if (cur) - printf("%s\n", cur); + if (line.cur) + printf("%s\n", line.cur); - /* eof */ - if (nread == 0) + /* eof or error */ + if (nread < 0) break; + + /* throttle in case something tries to wake us up every few ms... */ + sleep(1); } + pr_debug("done, nread: %zd\n", nread); + free(line.base); return 0; } |