xref: /csrg-svn/usr.bin/tail/forward.c (revision 62297)
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