xref: /original-bsd/usr.bin/tail/forward.c (revision bafc759a)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Edward Sze-Tyan Wang.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)forward.c	5.3 (Berkeley) 10/21/91";
13 #endif /* not lint */
14 
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/time.h>
18 #include <sys/mman.h>
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "extern.h"
26 
27 static void rlines __P((FILE *, long, struct stat *));
28 
29 /*
30  * forward -- display the file, from an offset, forward.
31  *
32  * There are eight separate cases for this -- regular and non-regular
33  * files, by bytes or lines and from the beginning or end of the file.
34  *
35  * FBYTES	byte offset from the beginning of the file
36  *	REG	seek
37  *	NOREG	read, counting bytes
38  *
39  * FLINES	line offset from the beginning of the file
40  *	REG	read, counting lines
41  *	NOREG	read, counting lines
42  *
43  * RBYTES	byte offset from the end of the file
44  *	REG	seek
45  *	NOREG	cyclically read characters into a wrap-around buffer
46  *
47  * RLINES
48  *	REG	mmap the file and step back until reach the correct offset.
49  *	NOREG	cyclically read lines into a wrap-around array of buffers
50  */
51 void
52 forward(fp, style, off, sbp)
53 	FILE *fp;
54 	enum STYLE style;
55 	long off;
56 	struct stat *sbp;
57 {
58 	register int ch;
59 	struct timeval second;
60 	fd_set zero;
61 
62 	switch(style) {
63 	case FBYTES:
64 		if (off == 0)
65 			break;
66 		if (S_ISREG(sbp->st_mode)) {
67 			if (sbp->st_size < off)
68 				off = sbp->st_size;
69 			if (fseek(fp, off, SEEK_SET) == -1)
70 				ierr();
71 		} else while (off--)
72 			if ((ch = getc(fp)) == EOF) {
73 				if (ferror(fp))
74 					ierr();
75 					break;
76 				}
77 		break;
78 	case FLINES:
79 		if (off == 0)
80 			break;
81 		for (;;) {
82 			if ((ch = getc(fp)) == EOF) {
83 				if (ferror(fp))
84 					ierr();
85 				break;
86 			}
87 			if (ch == '\n' && !--off)
88 				break;
89 		}
90 		break;
91 	case RBYTES:
92 		if (S_ISREG(sbp->st_mode)) {
93 			if (sbp->st_size >= off &&
94 			    fseek(fp, -off, SEEK_END) == -1)
95 				ierr();
96 		} else if (off == 0) {
97 			while (getc(fp) != EOF);
98 			if (ferror(fp))
99 				ierr();
100 		} else
101 			bytes(fp, off);
102 		break;
103 	case RLINES:
104 		if (S_ISREG(sbp->st_mode))
105 			if (!off) {
106 				if (fseek(fp, 0L, SEEK_END) == -1)
107 					ierr();
108 			} else
109 				rlines(fp, off, sbp);
110 		else if (off == 0) {
111 			while (getc(fp) != EOF);
112 			if (ferror(fp))
113 				ierr();
114 		} else
115 			lines(fp, off);
116 		break;
117 	}
118 
119 	/*
120 	 * We pause for one second after displaying any data that has
121 	 * accumulated since we read the file.
122 	 */
123 	if (fflag) {
124 		FD_ZERO(&zero);
125 		second.tv_sec = 1;
126 		second.tv_usec = 0;
127 	}
128 
129 	for (;;) {
130 		while ((ch = getc(fp)) != EOF)
131 			if (putchar(ch) == EOF)
132 				oerr();
133 		if (ferror(fp))
134 			ierr();
135 		(void)fflush(stdout);
136 		if (!fflag)
137 			break;
138 		/* Sleep(3) is eight system calls.  Do it fast. */
139 		if (select(0, &zero, &zero, &zero, &second) == -1)
140 			err("select: %s", strerror(errno));
141 		clearerr(fp);
142 	}
143 }
144 
145 /*
146  * rlines -- display the last offset lines of the file.
147  */
148 static void
149 rlines(fp, off, sbp)
150 	FILE *fp;
151 	long off;
152 	struct stat *sbp;
153 {
154 	register off_t size;
155 	register char *p;
156 
157 	if (!(size = sbp->st_size))
158 		return;
159 
160 	if ((p = mmap(NULL,
161 	    size, PROT_READ, MAP_FILE, fileno(fp), (off_t)0)) == (caddr_t)-1)
162 		err("%s", strerror(errno));
163 	p += size - 1;
164 
165 	/* Last char is special, ignore whether newline or not. */
166 	while (--size)
167 		if (*--p == '\n' && !--off)
168 			break;
169 
170 	/* Set the file pointer to reflect the length displayed. */
171 	size = sbp->st_size - size;
172 	WR(p + 1, size);
173 	if (fseek(fp, sbp->st_size, SEEK_SET) == -1)
174 		ierr();
175 }
176