/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include #include #include #include "utf.h" #include "util.h" static char mode = 'n'; static int dropinit(int fd, const char *fname, size_t count) { Rune r; char buf[BUFSIZ], *p; ssize_t n; int nr; if (count < 2) goto copy; count--; /* numbering starts at 1 */ while (count && (n = read(fd, buf, sizeof(buf))) > 0) { switch (mode) { case 'n': /* lines */ for (p = buf; count && n > 0; p++, n--) { if (*p == '\n') count--; } break; case 'c': /* bytes */ if (count > n) { count -= n; } else { p = buf + count; n -= count; count = 0; } break; case 'm': /* runes */ for (p = buf; count && n > 0; p += nr, n -= nr, count--) { nr = charntorune(&r, p, n); if (!nr) { /* we don't have a full rune, move * remaining data to beginning and read * again */ memmove(buf, p, n); break; } } break; } } if (count) { if (n < 0) weprintf("read %s:", fname); if (n <= 0) return n; } /* write the rest of the buffer */ if (writeall(1, p, n) < 0) eprintf("write:"); copy: switch (concat(fd, fname, 1, "")) { case -1: /* read error */ return -1; case -2: /* write error */ exit(1); default: return 0; } } static int taketail(int fd, const char *fname, size_t count) { static char *buf = NULL; static size_t size = 0; char *p; size_t len = 0, left; ssize_t n; if (!count) return 0; for (;;) { if (len + BUFSIZ > size) { /* make sure we have at least BUFSIZ to read */ size += 2 * BUFSIZ; buf = erealloc(buf, size); } n = read(fd, buf + len, size - len); if (n < 0) { weprintf("read %s:", fname); return -1; } if (n == 0) break; len += n; switch (mode) { case 'n': /* lines */ /* ignore the last character; if it is a newline, it * ends the last line */ for (p = buf + len - 2, left = count; p >= buf; p--) { if (*p != '\n') continue; left--; if (!left) { p++; break; } } break; case 'c': /* bytes */ p = count < len ? buf + len - count : buf; break; case 'm': /* runes */ for (p = buf + len - 1, left = count; p >= buf; p--) { /* skip utf-8 continuation bytes */ if ((*p & 0xc0) == 0x80) continue; left--; if (!left) break; } break; } if (p > buf) { len -= p - buf; memmove(buf, p, len); } } if (writeall(1, buf, len) < 0) eprintf("write:"); return 0; } static void usage(void) { eprintf("usage: %s [-f] [-c num | -m num | -n num | -num] [file ...]\n", argv0); } int main(int argc, char *argv[]) { struct stat st1, st2; int fd; size_t n = 10; int fflag = 0, ret = 0, newline = 0, many = 0; char *numstr; int (*tail)(int, const char *, size_t) = taketail; ARGBEGIN { case 'f': fflag = 1; break; case 'c': case 'm': case 'n': mode = ARGC(); numstr = EARGF(usage()); n = MIN(llabs(estrtonum(numstr, LLONG_MIN + 1, MIN(LLONG_MAX, SIZE_MAX))), SIZE_MAX); if (strchr(numstr, '+')) tail = dropinit; break; ARGNUM: n = ARGNUMF(); break; default: usage(); } ARGEND if (!argc) { if (tail(0, "", n) < 0) ret = 1; } else { if ((many = argc > 1) && fflag) usage(); for (newline = 0; *argv; argc--, argv++) { if (!strcmp(*argv, "-")) { *argv = ""; fd = 0; } else if ((fd = open(*argv, O_RDONLY)) < 0) { weprintf("open %s:", *argv); ret = 1; continue; } if (many) printf("%s==> %s <==\n", newline ? "\n" : "", *argv); if (fstat(fd, &st1) < 0) eprintf("fstat %s:", *argv); if (!(S_ISFIFO(st1.st_mode) || S_ISREG(st1.st_mode))) fflag = 0; newline = 1; if (tail(fd, *argv, n) < 0) { ret = 1; fflag = 0; } if (!fflag) { if (fd != 0) close(fd); continue; } for (;;) { if (concat(fd, *argv, 1, "") < 0) exit(1); if (fstat(fd, &st2) < 0) eprintf("fstat %s:", *argv); if (st2.st_size < st1.st_size) { fprintf(stderr, "%s: file truncated\n", *argv); if (lseek(fd, SEEK_SET, 0) < 0) eprintf("lseek:"); } st1 = st2; sleep(1); } } } return ret; }