/* See LICENSE file for copyright and license details. */ #include <sys/stat.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "text.h" #include "utf.h" #include "util.h" static int fflag = 0; static size_t num = 10; static char mode = 'n'; static void dropinit(FILE *fp, const char *str) { Rune r; char *buf = NULL; size_t size = 0, i = 0; ssize_t len; if (mode == 'n') { while (i < num && (len = getline(&buf, &size, fp)) >= 0) if (len > 0 && buf[len - 1] == '\n') i++; } else { while (i < num && (len = efgetrune(&r, fp, str))) i++; } free(buf); concat(fp, str, stdout, "<stdout>"); } static void taketail(FILE *fp, const char *str) { Rune *r = NULL; char **ring = NULL; size_t i, j, *size = NULL; if (mode == 'n') { ring = ecalloc(num, sizeof *ring); size = ecalloc(num, sizeof *size); for (i = j = 0; getline(&ring[i], &size[i], fp) != -1; ) i = j = (i + 1) % num; } else { r = ecalloc(num, sizeof *r); for (i = j = 0; efgetrune(&r[i], fp, str); ) i = j = (i + 1) % num; } if (ferror(fp)) eprintf("%s: read error:", str); do { if (ring && ring[j]) { fputs(ring[j], stdout); free(ring[j]); } else if (r) { efputrune(&r[j], stdout, "<stdout>"); } } while ((j = (j + 1) % num) != i); free(ring); free(size); free(r); } static void usage(void) { eprintf("usage: %s [-f] [-c num | -n num | -num] [file ...]\n", argv0); } int main(int argc, char *argv[]) { struct stat st1, st2; FILE *fp; size_t tmpsize; int ret = 0, newline, many; char *numstr, *tmp; void (*tail)(FILE *, const char *) = taketail; ARGBEGIN { case 'f': fflag = 1; break; case 'c': case 'n': mode = ARGC(); numstr = EARGF(usage()); num = MIN(llabs(estrtonum(numstr, LLONG_MIN + 1, MIN(LLONG_MAX, SIZE_MAX))), SIZE_MAX); if (strchr(numstr, '+')) tail = dropinit; break; ARGNUM: num = ARGNUMF(); break; default: usage(); } ARGEND; if (!argc) tail(stdin, "<stdin>"); else { if ((many = argc > 1) && fflag) usage(); for (newline = 0; *argv; argc--, argv++) { if (!(fp = fopen(*argv, "r"))) { weprintf("fopen %s:", *argv); ret = 1; continue; } if (many) printf("%s==> %s <==\n", newline ? "\n" : "", *argv); if (stat(*argv, &st1) < 0) eprintf("stat %s:", *argv); if (!(S_ISFIFO(st1.st_mode) || S_ISREG(st1.st_mode))) fflag = 0; newline = 1; tail(fp, *argv); if (!fflag) { fclose(fp); continue; } for (tmp = NULL, tmpsize = 0;;) { while (getline(&tmp, &tmpsize, fp) >= 0) { fputs(tmp, stdout); fflush(stdout); } if (ferror(fp)) eprintf("readline %s:", *argv); clearerr(fp); /* ignore error in case file was removed, we continue * tracking the existing open file descriptor */ if (!stat(*argv, &st2)) { if (st2.st_size < st1.st_size) { fprintf(stderr, "%s: file truncated\n", *argv); rewind(fp); } st1 = st2; } sleep(1); } } } return ret; }