xref: /freebsd/usr.bin/tail/forward.c (revision 817420dc8eac7df799c78f5309b75092b7f7cd40)
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  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * $FreeBSD$
37  */
38 
39 #ifndef lint
40 static char sccsid[] = "@(#)forward.c	8.1 (Berkeley) 6/6/93";
41 #endif /* not lint */
42 
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/time.h>
46 #include <sys/mman.h>
47 #include <sys/event.h>
48 
49 #include <limits.h>
50 #include <fcntl.h>
51 #include <errno.h>
52 #include <unistd.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <err.h>
57 #include "extern.h"
58 
59 static void rlines __P((FILE *, long, struct stat *));
60 
61 /* defines for inner loop actions */
62 #define USE_SLEEP	0
63 #define USE_KQUEUE	1
64 #define ADD_EVENTS	2
65 
66 /*
67  * forward -- display the file, from an offset, forward.
68  *
69  * There are eight separate cases for this -- regular and non-regular
70  * files, by bytes or lines and from the beginning or end of the file.
71  *
72  * FBYTES	byte offset from the beginning of the file
73  *	REG	seek
74  *	NOREG	read, counting bytes
75  *
76  * FLINES	line offset from the beginning of the file
77  *	REG	read, counting lines
78  *	NOREG	read, counting lines
79  *
80  * RBYTES	byte offset from the end of the file
81  *	REG	seek
82  *	NOREG	cyclically read characters into a wrap-around buffer
83  *
84  * RLINES
85  *	REG	mmap the file and step back until reach the correct offset.
86  *	NOREG	cyclically read lines into a wrap-around array of buffers
87  */
88 void
89 forward(fp, style, off, sbp)
90 	FILE *fp;
91 	enum STYLE style;
92 	long off;
93 	struct stat *sbp;
94 {
95 	int ch, kq = -1;
96 	int action = USE_SLEEP;
97 	struct kevent ev[2];
98 	struct stat sb2;
99 
100 	switch(style) {
101 	case FBYTES:
102 		if (off == 0)
103 			break;
104 		if (S_ISREG(sbp->st_mode)) {
105 			if (sbp->st_size < off)
106 				off = sbp->st_size;
107 			if (fseek(fp, off, SEEK_SET) == -1) {
108 				ierr();
109 				return;
110 			}
111 		} else while (off--)
112 			if ((ch = getc(fp)) == EOF) {
113 				if (ferror(fp)) {
114 					ierr();
115 					return;
116 				}
117 				break;
118 			}
119 		break;
120 	case FLINES:
121 		if (off == 0)
122 			break;
123 		for (;;) {
124 			if ((ch = getc(fp)) == EOF) {
125 				if (ferror(fp)) {
126 					ierr();
127 					return;
128 				}
129 				break;
130 			}
131 			if (ch == '\n' && !--off)
132 				break;
133 		}
134 		break;
135 	case RBYTES:
136 		if (S_ISREG(sbp->st_mode)) {
137 			if (sbp->st_size >= off &&
138 			    fseek(fp, -off, SEEK_END) == -1) {
139 				ierr();
140 				return;
141 			}
142 		} else if (off == 0) {
143 			while (getc(fp) != EOF);
144 			if (ferror(fp)) {
145 				ierr();
146 				return;
147 			}
148 		} else
149 			if (bytes(fp, off))
150 				return;
151 		break;
152 	case RLINES:
153 		if (S_ISREG(sbp->st_mode))
154 			if (!off) {
155 				if (fseek(fp, 0L, SEEK_END) == -1) {
156 					ierr();
157 					return;
158 				}
159 			} else
160 				rlines(fp, off, sbp);
161 		else if (off == 0) {
162 			while (getc(fp) != EOF);
163 			if (ferror(fp)) {
164 				ierr();
165 				return;
166 			}
167 		} else
168 			if (lines(fp, off))
169 				return;
170 		break;
171 	}
172 
173 	if (fflag) {
174 		kq = kqueue();
175 		if (kq < 0)
176 			err(1, "kqueue");
177 		action = ADD_EVENTS;
178 	}
179 
180 	for (;;) {
181 		while ((ch = getc(fp)) != EOF)
182 			if (putchar(ch) == EOF)
183 				oerr();
184 		if (ferror(fp)) {
185 			ierr();
186 			return;
187 		}
188 		(void)fflush(stdout);
189 		if (! fflag)
190 			break;
191 		clearerr(fp);
192 
193 		switch (action) {
194 		case ADD_EVENTS: {
195 			int n = 0;
196 			struct timespec ts = { 0, 0 };
197 
198 			if (Fflag && fileno(fp) != STDIN_FILENO) {
199 				ev[n].ident = fileno(fp);
200 				ev[n].filter = EVFILT_VNODE;
201 				ev[n].flags = EV_ADD | EV_ENABLE | EV_CLEAR;
202 				ev[n].fflags = NOTE_DELETE | NOTE_RENAME;
203 				n++;
204 			}
205 			ev[n].ident = fileno(fp);
206 			ev[n].filter = EVFILT_READ;
207 			ev[n].flags = EV_ADD | EV_ENABLE;
208 			n++;
209 
210 			if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
211 				close(kq);
212 				kq = -1;
213 				action = USE_SLEEP;
214 			} else {
215 				action = USE_KQUEUE;
216 			}
217 			break;
218 		}
219 
220 		case USE_KQUEUE:
221 			if (kevent(kq, NULL, 0, ev, 1, NULL) < 0)
222 				err(1, "kevent");
223 
224 			if (ev->filter == EVFILT_VNODE) {
225 				/* file was rotated, wait until it reappears */
226 				action = USE_SLEEP;
227 			} else if (ev->data < 0) {
228 				/* file shrank, reposition to end */
229 				if (fseek(fp, 0L, SEEK_END) == -1) {
230 					ierr();
231 					return;
232 				}
233 			}
234 			break;
235 
236 		case USE_SLEEP:
237                 	(void) usleep(250000);
238 	                clearerr(fp);
239 
240 			if (Fflag && fileno(fp) != STDIN_FILENO &&
241 			    stat(fname, &sb2) != -1) {
242 				if (sb2.st_ino != sbp->st_ino ||
243 				    sb2.st_dev != sbp->st_dev ||
244 				    sb2.st_rdev != sbp->st_rdev ||
245 				    sb2.st_nlink == 0) {
246 					fp = freopen(fname, "r", fp);
247 					if (fp == NULL) {
248 						ierr();
249 						break;
250 					}
251 					*sbp = sb2;
252 					if (kq != -1)
253 						action = ADD_EVENTS;
254 				}
255 			}
256 			break;
257 		}
258 	}
259 }
260 
261 /*
262  * rlines -- display the last offset lines of the file.
263  */
264 static void
265 rlines(fp, off, sbp)
266 	FILE *fp;
267 	long off;
268 	struct stat *sbp;
269 {
270 	register off_t size;
271 	register char *p;
272 	char *start;
273 
274 	if (!(size = sbp->st_size))
275 		return;
276 
277 	if (size > SIZE_T_MAX) {
278 		errno = EFBIG;
279 		ierr();
280 		return;
281 	}
282 
283 	if ((start = mmap(NULL, (size_t)size,
284 	    PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
285 		ierr();
286 		return;
287 	}
288 
289 	/* Last char is special, ignore whether newline or not. */
290 	for (p = start + size - 1; --size;)
291 		if (*--p == '\n' && !--off) {
292 			++p;
293 			break;
294 		}
295 
296 	/* Set the file pointer to reflect the length displayed. */
297 	size = sbp->st_size - size;
298 	WR(p, size);
299 	if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
300 		ierr();
301 		return;
302 	}
303 	if (munmap(start, (size_t)sbp->st_size)) {
304 		ierr();
305 		return;
306 	}
307 }
308