xref: /original-bsd/usr.bin/tail/forward.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  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	8.1 (Berkeley) 06/06/93";
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 
20 #include <limits.h>
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "extern.h"
28 
29 static void rlines __P((FILE *, long, struct stat *));
30 
31 /*
32  * forward -- display the file, from an offset, forward.
33  *
34  * There are eight separate cases for this -- regular and non-regular
35  * files, by bytes or lines and from the beginning or end of the file.
36  *
37  * FBYTES	byte offset from the beginning of the file
38  *	REG	seek
39  *	NOREG	read, counting bytes
40  *
41  * FLINES	line offset from the beginning of the file
42  *	REG	read, counting lines
43  *	NOREG	read, counting lines
44  *
45  * RBYTES	byte offset from the end of the file
46  *	REG	seek
47  *	NOREG	cyclically read characters into a wrap-around buffer
48  *
49  * RLINES
50  *	REG	mmap the file and step back until reach the correct offset.
51  *	NOREG	cyclically read lines into a wrap-around array of buffers
52  */
53 void
54 forward(fp, style, off, sbp)
55 	FILE *fp;
56 	enum STYLE style;
57 	long off;
58 	struct stat *sbp;
59 {
60 	register int ch;
61 	struct timeval second;
62 	fd_set zero;
63 
64 	switch(style) {
65 	case FBYTES:
66 		if (off == 0)
67 			break;
68 		if (S_ISREG(sbp->st_mode)) {
69 			if (sbp->st_size < off)
70 				off = sbp->st_size;
71 			if (fseek(fp, off, SEEK_SET) == -1) {
72 				ierr();
73 				return;
74 			}
75 		} else while (off--)
76 			if ((ch = getc(fp)) == EOF) {
77 				if (ferror(fp)) {
78 					ierr();
79 					return;
80 				}
81 				break;
82 			}
83 		break;
84 	case FLINES:
85 		if (off == 0)
86 			break;
87 		for (;;) {
88 			if ((ch = getc(fp)) == EOF) {
89 				if (ferror(fp)) {
90 					ierr();
91 					return;
92 				}
93 				break;
94 			}
95 			if (ch == '\n' && !--off)
96 				break;
97 		}
98 		break;
99 	case RBYTES:
100 		if (S_ISREG(sbp->st_mode)) {
101 			if (sbp->st_size >= off &&
102 			    fseek(fp, -off, SEEK_END) == -1) {
103 				ierr();
104 				return;
105 			}
106 		} else if (off == 0) {
107 			while (getc(fp) != EOF);
108 			if (ferror(fp)) {
109 				ierr();
110 				return;
111 			}
112 		} else
113 			bytes(fp, off);
114 		break;
115 	case RLINES:
116 		if (S_ISREG(sbp->st_mode))
117 			if (!off) {
118 				if (fseek(fp, 0L, SEEK_END) == -1) {
119 					ierr();
120 					return;
121 				}
122 			} else
123 				rlines(fp, off, sbp);
124 		else if (off == 0) {
125 			while (getc(fp) != EOF);
126 			if (ferror(fp)) {
127 				ierr();
128 				return;
129 			}
130 		} else
131 			lines(fp, off);
132 		break;
133 	}
134 
135 	/*
136 	 * We pause for one second after displaying any data that has
137 	 * accumulated since we read the file.
138 	 */
139 	if (fflag) {
140 		FD_ZERO(&zero);
141 		second.tv_sec = 1;
142 		second.tv_usec = 0;
143 	}
144 
145 	for (;;) {
146 		while ((ch = getc(fp)) != EOF)
147 			if (putchar(ch) == EOF)
148 				oerr();
149 		if (ferror(fp)) {
150 			ierr();
151 			return;
152 		}
153 		(void)fflush(stdout);
154 		if (!fflag)
155 			break;
156 		/* Sleep(3) is eight system calls.  Do it fast. */
157 		if (select(0, &zero, &zero, &zero, &second) == -1)
158 			err(1, "select: %s", strerror(errno));
159 		clearerr(fp);
160 	}
161 }
162 
163 /*
164  * rlines -- display the last offset lines of the file.
165  */
166 static void
167 rlines(fp, off, sbp)
168 	FILE *fp;
169 	long off;
170 	struct stat *sbp;
171 {
172 	register off_t size;
173 	register char *p;
174 	char *start;
175 
176 	if (!(size = sbp->st_size))
177 		return;
178 
179 	if (size > SIZE_T_MAX) {
180 		err(0, "%s: %s", fname, strerror(EFBIG));
181 		return;
182 	}
183 
184 	if ((start = mmap(NULL, (size_t)size,
185 	    PROT_READ, 0, fileno(fp), (off_t)0)) == (caddr_t)-1) {
186 		err(0, "%s: %s", fname, strerror(EFBIG));
187 		return;
188 	}
189 
190 	/* Last char is special, ignore whether newline or not. */
191 	for (p = start + size - 1; --size;)
192 		if (*--p == '\n' && !--off) {
193 			++p;
194 			break;
195 		}
196 
197 	/* Set the file pointer to reflect the length displayed. */
198 	size = sbp->st_size - size;
199 	WR(p, size);
200 	if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
201 		ierr();
202 		return;
203 	}
204 	if (munmap(start, (size_t)sbp->st_size)) {
205 		err(0, "%s: %s", fname, strerror(errno));
206 		return;
207 	}
208 }
209