X-Git-Url: https://pd.if.org/git/?p=pdutils;a=blobdiff_plain;f=utils%2Ftail%2Ftail.c;fp=utils%2Ftail%2Ftail.c;h=5fa8849b7fd58cc5d70945347924e27605bcf0f8;hp=0000000000000000000000000000000000000000;hb=4daa873acbfc240e9ecc350e74c14bbfd58fabc9;hpb=b6d847ae20c32744d508eced2be3132c3fa8c5b9 diff --git a/utils/tail/tail.c b/utils/tail/tail.c new file mode 100644 index 0000000..5fa8849 --- /dev/null +++ b/utils/tail/tail.c @@ -0,0 +1,446 @@ +/* + * tail.c - copy the last part of a file + * + * Version: 2008-1.01 + * Build: c89 -o tail tail.c + * Source: + * Spec: + * + * This is free and unencumbered software released into the public domain, + * provided "as is", without warranty of any kind, express or implied. See the + * file UNLICENSE and the website for further details. + */ + + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "usage: tail [-f] [-c number] [-n number] [file]\n" +#define NOMALLOC "Unable to allocate memory for read buffer" +#define NOFOLLOW "tail: Option [-f] ignored; can only follow regular files and FIFOs\n" +#define BADBYTES "Option [-c] requires non-zero byte offset" +#define BADLINES "Option [-n] requires integer line offset" +#define CNMUTEXCL "Options [-c] and [-n] are mutually exclusive" +#define FVANISH "Followed file has vanished" +#define FSHRUNK "Followed file has shrunk" +#define SYSERR 1 +#define APPERR 0 + +static int isdecint(char *s); +static void tailfile(int fd, char *fn); +static void tailbyteoffset(int fd, char *fn, int isregfile, ssize_t len); +static void taillineoffset(int fd, char *fn, int isregfile, ssize_t len); +static void copytoeof(int fd, char *fn); +static void fatal(int errtype, char *s); + +static unsigned char* buf; +static ssize_t bufsize, offset; +static int optf, optc, optn; + + +int main(int argc, char **argv) +{ + extern int opterr, optind; + extern char *optarg; + int c, fd; + char *fn; + + setlocale(LC_ALL, ""); + opterr = 0; + + /* + * try to get a buffer _twice_ the size mandated by the standard as in + * later processing we may be paging through the file in _half_ buffer + * increments; this way we guarentee POSIX buffer size conformance + */ + bufsize = sysconf(_SC_LINE_MAX) * 20; /* POSIX says [{LINE_MAX)*10] */ + bufsize = bufsize < 4096 ? 4096 : bufsize; + + if ((buf = malloc(bufsize)) == NULL) + fatal(APPERR, NOMALLOC); + + while ((c = getopt(argc, argv, "fc:n:")) != -1) + switch (c) + { + case 'f': /* follow file */ + optf = 1; + break; + + case 'c': /* offset in bytes */ + optc = 1; + offset = 0; + if (*optarg && isdecint(optarg)) + { + offset = atol(optarg); + offset = isdigit(*optarg) ? -offset : offset; + } + + if (offset == 0) + fatal(APPERR, BADBYTES); + else + break; + + case 'n': /* offset in lines */ + optn = 1; + if (*optarg && isdecint(optarg)) + { + offset = atol(optarg); + offset = isdigit(*optarg) ? -offset : offset; + + /* the standard says [-n 0] is OK, but [-n +0] isn't &_& */ + if (offset != 0 || (offset == 0 && *optarg != '+')) + break; + } + fatal(APPERR, BADLINES); + + default: + fprintf(stderr, USAGE); + exit(1); + } + + if (! optc && ! optn) /* if none, set the default options */ + { + optn = 1; + offset = -10; + } + + if (optc && optn) + fatal(APPERR, CNMUTEXCL); + + if (optind >= argc) + tailfile(STDIN_FILENO, "stdin"); + else + { + fn = argv[optind++]; + + if (strcmp(fn, "-") == 0) + tailfile(STDIN_FILENO, "stdin"); + else + if ((fd = open(fn, O_RDONLY)) == -1) + fatal(SYSERR, fn); + else + { + tailfile(fd, fn); + if (close(fd) == -1) + fatal(SYSERR, fn); + } + } + + return(0); +} + + +int isdecint(char *s) +{ + int badch = 0; + char *c = s; + + if (! (isdigit(*c) || *c == '+' || *c == '-')) + badch = 1; + else + for (c++; *c; c++) + if (! isdigit(*c)) + badch = 1; + + return (! badch); +} + + +void tailfile(int fd, char *fn) +{ + struct stat sbuf; + int isregfile; + ssize_t n, len; + + if (fstat(fd, &sbuf) == -1) + fatal(SYSERR, fn); + + if (S_ISREG(sbuf.st_mode)) + { + isregfile = 1; + len = sbuf.st_size; + } + else + isregfile = len = 0; + + if (offset != 0) /* [-n 0] is permitted; but no point processing that */ + { + if (optc) + tailbyteoffset(fd, fn, isregfile, len); + else + taillineoffset(fd, fn, isregfile, len); + } + + if (optf) + { + if (fd == STDIN_FILENO) /* can't follow; warn then exit no error */ + { + fprintf(stderr, NOFOLLOW); + exit(0); + } + + while(1) /* follow forever */ + { + sleep(1); + + if (isregfile) /* if we can, be polite to the user */ + { + if (stat(fn, &sbuf) == -1) + fatal(APPERR, FVANISH); + else if (sbuf.st_size < len) + fatal(APPERR, FSHRUNK); + + len = sbuf.st_size; + } + + while ((n = read(fd, buf, bufsize)) > 0) + if (write(STDOUT_FILENO, buf, n) != n) + fatal(SYSERR, "stdout"); + + if (n < 0) + fatal(SYSERR, fn); + } + } +} + + +void tailbyteoffset(int fd, char *fn, int isregfile, ssize_t len) +{ + ssize_t n, halfbuf; + + if (isregfile) /* should be seekable, so we'll do so; it's fastest */ + { + if (offset > 0) + offset = (offset - 1) > len ? len : offset - 1; + else + offset = (len + offset) > 0 ? len + offset : 0; + + if (lseek(fd, (off_t)offset, SEEK_SET) == (off_t)-1) + fatal(SYSERR, fn); + + copytoeof(fd, fn); + } + + else /* possibly non-seekable */ + { + if (offset > 0) /* forwards through file */ + { + offset--; + + while(1) + { + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n == 0) + offset = 0; + + if (offset <= n) + break; + else + offset -= n; + } + + if (write(STDOUT_FILENO, buf + offset, n - offset) != n - offset) + fatal(SYSERR, "stdout"); + + copytoeof(fd, fn); + } + + else /* backwards through file; remember that offset is negative */ + { + halfbuf = bufsize / 2; + + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n < bufsize) /* we've got the whole file */ + { + offset = (n + offset) < 0 ? 0 : n + offset; + len = n - offset; + } + + else /* we haven't got the whole file */ + { + while(1) /* page through the file, half a buffer at a time */ + { + memcpy(buf, buf + halfbuf, halfbuf); + + if ((n = read(fd, buf + halfbuf, halfbuf)) < 0) + fatal(SYSERR, fn); + else if (n < halfbuf) + break; + } + + offset = (halfbuf + n + offset) < 0 ? 0 : halfbuf + n + offset; + len = halfbuf + n - offset; + } + + if (write(STDOUT_FILENO, buf + offset, len) != len) + fatal(SYSERR, "stdout"); + } + } +} + + +void taillineoffset(int fd, char *fn, int isregfile, ssize_t len) +{ + ssize_t n, i, halfbuf; + + if (offset > 0) /* forwards through file */ + { + offset--; + + while(1) + { + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n == 0) + { + offset = 0; + break; + } + + for (i = 0; i < n && offset > 0; i++) + if (buf[i] == '\n') + offset--; + + if (offset == 0) + { + offset = i; + break; + } + } + + if (write(STDOUT_FILENO, buf + offset, n - offset) != n - offset) + fatal(SYSERR, "stdout"); + + copytoeof(fd, fn); + } + + else /* backwards through file; remember that offset is negative */ + { + if (isregfile && len > 0) /* should be seekable, so we'll do so */ + { + n = (len - bufsize) < 0 ? 0 : len - bufsize; + + if (lseek(fd, (off_t)n, SEEK_SET) == (off_t)-1) + fatal(SYSERR, fn); + + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (buf[n-1] == '\n') + offset--; + + for (i = n - 1; i >= 0 && offset < 0; i--) + if (buf[i] == '\n') + offset++; + + if (offset == 0) + offset = i + 2; + else + offset = 0; + + len = n - offset; + + if (write(STDOUT_FILENO, buf + offset, len) != len) + fatal(SYSERR, "stdout"); + } + + else + { + halfbuf = bufsize / 2; + + if ((n = read(fd, buf, bufsize)) < 0) + fatal(SYSERR, fn); + + if (n < bufsize) /* we've got the whole file */ + { + if (n == 0) + offset = 0; + else + { + if (buf[n-1] == '\n') + offset--; + + for (i = n - 1; i >= 0 && offset < 0; i--) + if (buf[i] == '\n') + offset++; + + if (offset == 0) + offset = i + 2; + else + offset = 0; + } + + len = n - offset; + } + + else /* we haven't got the whole file */ + { + while(1) /* page through the file, half a buffer at a time */ + { + memcpy(buf, buf + halfbuf, halfbuf); + + if ((n = read(fd, buf + halfbuf, halfbuf)) < 0) + fatal(SYSERR, fn); + else if (n < halfbuf) + break; + } + + if (buf[halfbuf+n-1] == '\n') + offset--; + + for (i = halfbuf + n - 1; i >= 0 && offset < 0; i--) + if (buf[i] == '\n') + offset++; + + if (offset == 0) + offset = i + 2; + else + offset = 0; + + len = halfbuf + n - offset; + } + + if (write(STDOUT_FILENO, buf + offset, len) != len) + fatal(SYSERR, "stdout"); + } + } +} + + +void copytoeof(int fd, char *fn) +{ + ssize_t n; + + while ((n = read(fd, buf, bufsize)) > 0) + if (write(STDOUT_FILENO, buf, n) != n) + fatal(SYSERR, "stdout"); + + if (n < 0) + fatal(SYSERR, fn); +} + + +void fatal(int errtype, char *s) +{ + if (errtype == SYSERR) + fprintf(stderr, "tail: %s: %s\n", s, strerror(errno)); + else + fprintf(stderr, "tail: %s\n", s); + + exit(1); +}