150449Sbostic /*-
2*62297Sbostic * Copyright (c) 1991, 1993
3*62297Sbostic * The Regents of the University of California. All rights reserved.
450449Sbostic *
550449Sbostic * This code is derived from software contributed to Berkeley by
650449Sbostic * Edward Sze-Tyan Wang.
750449Sbostic *
850449Sbostic * %sccs.include.redist.c%
950449Sbostic */
1050449Sbostic
1150449Sbostic #ifndef lint
12*62297Sbostic static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 06/06/93";
1350449Sbostic #endif /* not lint */
1450449Sbostic
1550449Sbostic #include <sys/types.h>
1650449Sbostic #include <sys/stat.h>
1750449Sbostic #include <sys/time.h>
1850449Sbostic #include <sys/mman.h>
1954363Sbostic
2054363Sbostic #include <limits.h>
2150449Sbostic #include <fcntl.h>
2250449Sbostic #include <errno.h>
2350449Sbostic #include <unistd.h>
2450449Sbostic #include <stdio.h>
2550449Sbostic #include <stdlib.h>
2650449Sbostic #include <string.h>
2750449Sbostic #include "extern.h"
2850449Sbostic
2950449Sbostic static void rlines __P((FILE *, long, struct stat *));
3050449Sbostic
3150449Sbostic /*
3250449Sbostic * forward -- display the file, from an offset, forward.
3350449Sbostic *
3450449Sbostic * There are eight separate cases for this -- regular and non-regular
3550449Sbostic * files, by bytes or lines and from the beginning or end of the file.
3650449Sbostic *
3750449Sbostic * FBYTES byte offset from the beginning of the file
3850449Sbostic * REG seek
3950449Sbostic * NOREG read, counting bytes
4050449Sbostic *
4150449Sbostic * FLINES line offset from the beginning of the file
4250449Sbostic * REG read, counting lines
4350449Sbostic * NOREG read, counting lines
4450449Sbostic *
4550449Sbostic * RBYTES byte offset from the end of the file
4650449Sbostic * REG seek
4750449Sbostic * NOREG cyclically read characters into a wrap-around buffer
4850449Sbostic *
4950449Sbostic * RLINES
5050449Sbostic * REG mmap the file and step back until reach the correct offset.
5150449Sbostic * NOREG cyclically read lines into a wrap-around array of buffers
5250449Sbostic */
5350449Sbostic void
forward(fp,style,off,sbp)5450449Sbostic forward(fp, style, off, sbp)
5550449Sbostic FILE *fp;
5650449Sbostic enum STYLE style;
5750449Sbostic long off;
5850449Sbostic struct stat *sbp;
5950449Sbostic {
6050449Sbostic register int ch;
6150449Sbostic struct timeval second;
6250449Sbostic fd_set zero;
6350449Sbostic
6450449Sbostic switch(style) {
6550449Sbostic case FBYTES:
6650449Sbostic if (off == 0)
6750449Sbostic break;
6850449Sbostic if (S_ISREG(sbp->st_mode)) {
6950449Sbostic if (sbp->st_size < off)
7050449Sbostic off = sbp->st_size;
7152827Sbostic if (fseek(fp, off, SEEK_SET) == -1) {
7250449Sbostic ierr();
7352827Sbostic return;
7452827Sbostic }
7550449Sbostic } else while (off--)
7650449Sbostic if ((ch = getc(fp)) == EOF) {
7752827Sbostic if (ferror(fp)) {
7850449Sbostic ierr();
7952827Sbostic return;
8050449Sbostic }
8152827Sbostic break;
8252827Sbostic }
8350449Sbostic break;
8450449Sbostic case FLINES:
8550449Sbostic if (off == 0)
8650449Sbostic break;
8750449Sbostic for (;;) {
8850449Sbostic if ((ch = getc(fp)) == EOF) {
8952827Sbostic if (ferror(fp)) {
9050449Sbostic ierr();
9152827Sbostic return;
9252827Sbostic }
9350449Sbostic break;
9450449Sbostic }
9550449Sbostic if (ch == '\n' && !--off)
9650449Sbostic break;
9750449Sbostic }
9850449Sbostic break;
9950449Sbostic case RBYTES:
10050449Sbostic if (S_ISREG(sbp->st_mode)) {
10150449Sbostic if (sbp->st_size >= off &&
10252827Sbostic fseek(fp, -off, SEEK_END) == -1) {
10350449Sbostic ierr();
10452827Sbostic return;
10552827Sbostic }
10650449Sbostic } else if (off == 0) {
10750449Sbostic while (getc(fp) != EOF);
10852827Sbostic if (ferror(fp)) {
10950449Sbostic ierr();
11052827Sbostic return;
11152827Sbostic }
11250449Sbostic } else
11350449Sbostic bytes(fp, off);
11450449Sbostic break;
11550449Sbostic case RLINES:
11650449Sbostic if (S_ISREG(sbp->st_mode))
11750449Sbostic if (!off) {
11852827Sbostic if (fseek(fp, 0L, SEEK_END) == -1) {
11950449Sbostic ierr();
12052827Sbostic return;
12152827Sbostic }
12250449Sbostic } else
12350449Sbostic rlines(fp, off, sbp);
12450449Sbostic else if (off == 0) {
12550449Sbostic while (getc(fp) != EOF);
12652827Sbostic if (ferror(fp)) {
12750449Sbostic ierr();
12852827Sbostic return;
12952827Sbostic }
13050449Sbostic } else
13150449Sbostic lines(fp, off);
13250449Sbostic break;
13350449Sbostic }
13450449Sbostic
13550449Sbostic /*
13650449Sbostic * We pause for one second after displaying any data that has
13750449Sbostic * accumulated since we read the file.
13850449Sbostic */
13950449Sbostic if (fflag) {
14050449Sbostic FD_ZERO(&zero);
14150449Sbostic second.tv_sec = 1;
14250449Sbostic second.tv_usec = 0;
14350449Sbostic }
14450449Sbostic
14550449Sbostic for (;;) {
14650449Sbostic while ((ch = getc(fp)) != EOF)
14750449Sbostic if (putchar(ch) == EOF)
14850449Sbostic oerr();
14952827Sbostic if (ferror(fp)) {
15050449Sbostic ierr();
15152827Sbostic return;
15252827Sbostic }
15350570Sbostic (void)fflush(stdout);
15450449Sbostic if (!fflag)
15550449Sbostic break;
15650449Sbostic /* Sleep(3) is eight system calls. Do it fast. */
15750449Sbostic if (select(0, &zero, &zero, &zero, &second) == -1)
15852827Sbostic err(1, "select: %s", strerror(errno));
15950449Sbostic clearerr(fp);
16050449Sbostic }
16150449Sbostic }
16250449Sbostic
16350449Sbostic /*
16450449Sbostic * rlines -- display the last offset lines of the file.
16550449Sbostic */
16650449Sbostic static void
rlines(fp,off,sbp)16750449Sbostic rlines(fp, off, sbp)
16850449Sbostic FILE *fp;
16950449Sbostic long off;
17050449Sbostic struct stat *sbp;
17150449Sbostic {
17250449Sbostic register off_t size;
17350449Sbostic register char *p;
17453507Sbostic char *start;
17550449Sbostic
17650449Sbostic if (!(size = sbp->st_size))
17750449Sbostic return;
17850449Sbostic
17954363Sbostic if (size > SIZE_T_MAX) {
18054363Sbostic err(0, "%s: %s", fname, strerror(EFBIG));
18154363Sbostic return;
18254363Sbostic }
18354363Sbostic
18454190Sbostic if ((start = mmap(NULL, (size_t)size,
18554363Sbostic PROT_READ, 0, fileno(fp), (off_t)0)) == (caddr_t)-1) {
18654363Sbostic err(0, "%s: %s", fname, strerror(EFBIG));
18752827Sbostic return;
18852827Sbostic }
18950449Sbostic
19050449Sbostic /* Last char is special, ignore whether newline or not. */
19153507Sbostic for (p = start + size - 1; --size;)
19252470Sbostic if (*--p == '\n' && !--off) {
19352470Sbostic ++p;
19450449Sbostic break;
19552470Sbostic }
19650449Sbostic
19750449Sbostic /* Set the file pointer to reflect the length displayed. */
19850449Sbostic size = sbp->st_size - size;
19952470Sbostic WR(p, size);
20054190Sbostic if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
20150449Sbostic ierr();
20252827Sbostic return;
20352827Sbostic }
20458754Sbostic if (munmap(start, (size_t)sbp->st_size)) {
20554363Sbostic err(0, "%s: %s", fname, strerror(errno));
20653507Sbostic return;
20753507Sbostic }
20850449Sbostic }
209