xref: /minix3/usr.bin/tail/forward.c (revision a967d739abf6b9e18d75e99925030c41b4f075ca)
1*a967d739SClaudio Martella /*	$NetBSD: forward.c,v 1.32 2013/10/18 20:47:07 christos Exp $	*/
2*a967d739SClaudio Martella 
3*a967d739SClaudio Martella /*-
4*a967d739SClaudio Martella  * Copyright (c) 1991, 1993
5*a967d739SClaudio Martella  *	The Regents of the University of California.  All rights reserved.
6*a967d739SClaudio Martella  *
7*a967d739SClaudio Martella  * This code is derived from software contributed to Berkeley by
8*a967d739SClaudio Martella  * Edward Sze-Tyan Wang.
9*a967d739SClaudio Martella  *
10*a967d739SClaudio Martella  * Redistribution and use in source and binary forms, with or without
11*a967d739SClaudio Martella  * modification, are permitted provided that the following conditions
12*a967d739SClaudio Martella  * are met:
13*a967d739SClaudio Martella  * 1. Redistributions of source code must retain the above copyright
14*a967d739SClaudio Martella  *    notice, this list of conditions and the following disclaimer.
15*a967d739SClaudio Martella  * 2. Redistributions in binary form must reproduce the above copyright
16*a967d739SClaudio Martella  *    notice, this list of conditions and the following disclaimer in the
17*a967d739SClaudio Martella  *    documentation and/or other materials provided with the distribution.
18*a967d739SClaudio Martella  * 3. Neither the name of the University nor the names of its contributors
19*a967d739SClaudio Martella  *    may be used to endorse or promote products derived from this software
20*a967d739SClaudio Martella  *    without specific prior written permission.
21*a967d739SClaudio Martella  *
22*a967d739SClaudio Martella  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23*a967d739SClaudio Martella  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24*a967d739SClaudio Martella  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*a967d739SClaudio Martella  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26*a967d739SClaudio Martella  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27*a967d739SClaudio Martella  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28*a967d739SClaudio Martella  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29*a967d739SClaudio Martella  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30*a967d739SClaudio Martella  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31*a967d739SClaudio Martella  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*a967d739SClaudio Martella  * SUCH DAMAGE.
33*a967d739SClaudio Martella  */
34*a967d739SClaudio Martella 
35*a967d739SClaudio Martella #include <sys/cdefs.h>
36*a967d739SClaudio Martella #ifndef lint
37*a967d739SClaudio Martella #if 0
38*a967d739SClaudio Martella static char sccsid[] = "@(#)forward.c	8.1 (Berkeley) 6/6/93";
39*a967d739SClaudio Martella #endif
40*a967d739SClaudio Martella __RCSID("$NetBSD: forward.c,v 1.32 2013/10/18 20:47:07 christos Exp $");
41*a967d739SClaudio Martella #endif /* not lint */
42*a967d739SClaudio Martella 
43*a967d739SClaudio Martella #include <sys/types.h>
44*a967d739SClaudio Martella #include <sys/stat.h>
45*a967d739SClaudio Martella #include <sys/time.h>
46*a967d739SClaudio Martella #include <sys/mman.h>
47*a967d739SClaudio Martella #include <sys/event.h>
48*a967d739SClaudio Martella 
49*a967d739SClaudio Martella #include <limits.h>
50*a967d739SClaudio Martella #include <fcntl.h>
51*a967d739SClaudio Martella #include <errno.h>
52*a967d739SClaudio Martella #include <unistd.h>
53*a967d739SClaudio Martella #include <stdio.h>
54*a967d739SClaudio Martella #include <stdlib.h>
55*a967d739SClaudio Martella #include <string.h>
56*a967d739SClaudio Martella #include "extern.h"
57*a967d739SClaudio Martella 
58*a967d739SClaudio Martella static int rlines(FILE *, off_t, struct stat *);
59*a967d739SClaudio Martella 
60*a967d739SClaudio Martella /* defines for inner loop actions */
61*a967d739SClaudio Martella #define	USE_SLEEP	0
62*a967d739SClaudio Martella #define	USE_KQUEUE	1
63*a967d739SClaudio Martella #define	ADD_EVENTS	2
64*a967d739SClaudio Martella 
65*a967d739SClaudio Martella /*
66*a967d739SClaudio Martella  * forward -- display the file, from an offset, forward.
67*a967d739SClaudio Martella  *
68*a967d739SClaudio Martella  * There are eight separate cases for this -- regular and non-regular
69*a967d739SClaudio Martella  * files, by bytes or lines and from the beginning or end of the file.
70*a967d739SClaudio Martella  *
71*a967d739SClaudio Martella  * FBYTES	byte offset from the beginning of the file
72*a967d739SClaudio Martella  *	REG	seek
73*a967d739SClaudio Martella  *	NOREG	read, counting bytes
74*a967d739SClaudio Martella  *
75*a967d739SClaudio Martella  * FLINES	line offset from the beginning of the file
76*a967d739SClaudio Martella  *	REG	read, counting lines
77*a967d739SClaudio Martella  *	NOREG	read, counting lines
78*a967d739SClaudio Martella  *
79*a967d739SClaudio Martella  * RBYTES	byte offset from the end of the file
80*a967d739SClaudio Martella  *	REG	seek
81*a967d739SClaudio Martella  *	NOREG	cyclically read characters into a wrap-around buffer
82*a967d739SClaudio Martella  *
83*a967d739SClaudio Martella  * RLINES
84*a967d739SClaudio Martella  *	REG	mmap the file and step back until reach the correct offset.
85*a967d739SClaudio Martella  *	NOREG	cyclically read lines into a wrap-around array of buffers
86*a967d739SClaudio Martella  */
87*a967d739SClaudio Martella void
88*a967d739SClaudio Martella forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
89*a967d739SClaudio Martella {
90*a967d739SClaudio Martella #ifndef __minix
91*a967d739SClaudio Martella 	int ch, n;
92*a967d739SClaudio Martella #else
93*a967d739SClaudio Martella 	int ch;
94*a967d739SClaudio Martella #endif
95*a967d739SClaudio Martella 	int kq=-1, action=USE_SLEEP;
96*a967d739SClaudio Martella 	struct stat statbuf;
97*a967d739SClaudio Martella #ifndef __minix
98*a967d739SClaudio Martella 	struct kevent ev[2];
99*a967d739SClaudio Martella #endif
100*a967d739SClaudio Martella 
101*a967d739SClaudio Martella 	switch(style) {
102*a967d739SClaudio Martella 	case FBYTES:
103*a967d739SClaudio Martella 		if (off == 0)
104*a967d739SClaudio Martella 			break;
105*a967d739SClaudio Martella 		if (S_ISREG(sbp->st_mode)) {
106*a967d739SClaudio Martella 			if (sbp->st_size < off)
107*a967d739SClaudio Martella 				off = sbp->st_size;
108*a967d739SClaudio Martella 			if (fseeko(fp, off, SEEK_SET) == -1) {
109*a967d739SClaudio Martella 				ierr();
110*a967d739SClaudio Martella 				return;
111*a967d739SClaudio Martella 			}
112*a967d739SClaudio Martella 		} else while (off--)
113*a967d739SClaudio Martella 			if ((ch = getc(fp)) == EOF) {
114*a967d739SClaudio Martella 				if (ferror(fp)) {
115*a967d739SClaudio Martella 					ierr();
116*a967d739SClaudio Martella 					return;
117*a967d739SClaudio Martella 				}
118*a967d739SClaudio Martella 				break;
119*a967d739SClaudio Martella 			}
120*a967d739SClaudio Martella 		break;
121*a967d739SClaudio Martella 	case FLINES:
122*a967d739SClaudio Martella 		if (off == 0)
123*a967d739SClaudio Martella 			break;
124*a967d739SClaudio Martella 		for (;;) {
125*a967d739SClaudio Martella 			if ((ch = getc(fp)) == EOF) {
126*a967d739SClaudio Martella 				if (ferror(fp)) {
127*a967d739SClaudio Martella 					ierr();
128*a967d739SClaudio Martella 					return;
129*a967d739SClaudio Martella 				}
130*a967d739SClaudio Martella 				break;
131*a967d739SClaudio Martella 			}
132*a967d739SClaudio Martella 			if (ch == '\n' && !--off)
133*a967d739SClaudio Martella 				break;
134*a967d739SClaudio Martella 		}
135*a967d739SClaudio Martella 		break;
136*a967d739SClaudio Martella 	case RBYTES:
137*a967d739SClaudio Martella 		if (S_ISREG(sbp->st_mode)) {
138*a967d739SClaudio Martella 			if (sbp->st_size >= off &&
139*a967d739SClaudio Martella 			    fseeko(fp, -off, SEEK_END) == -1) {
140*a967d739SClaudio Martella 				ierr();
141*a967d739SClaudio Martella 				return;
142*a967d739SClaudio Martella 			}
143*a967d739SClaudio Martella 		} else if (off == 0) {
144*a967d739SClaudio Martella 			while (getc(fp) != EOF);
145*a967d739SClaudio Martella 			if (ferror(fp)) {
146*a967d739SClaudio Martella 				ierr();
147*a967d739SClaudio Martella 				return;
148*a967d739SClaudio Martella 			}
149*a967d739SClaudio Martella 		} else {
150*a967d739SClaudio Martella 			if (displaybytes(fp, off))
151*a967d739SClaudio Martella 				return;
152*a967d739SClaudio Martella 		}
153*a967d739SClaudio Martella 		break;
154*a967d739SClaudio Martella 	case RLINES:
155*a967d739SClaudio Martella 		if (S_ISREG(sbp->st_mode)) {
156*a967d739SClaudio Martella 			if (!off) {
157*a967d739SClaudio Martella 				if (fseek(fp, 0L, SEEK_END) == -1) {
158*a967d739SClaudio Martella 					ierr();
159*a967d739SClaudio Martella 					return;
160*a967d739SClaudio Martella 				}
161*a967d739SClaudio Martella 			} else {
162*a967d739SClaudio Martella 				if (rlines(fp, off, sbp))
163*a967d739SClaudio Martella 					return;
164*a967d739SClaudio Martella 			}
165*a967d739SClaudio Martella 		} else if (off == 0) {
166*a967d739SClaudio Martella 			while (getc(fp) != EOF);
167*a967d739SClaudio Martella 			if (ferror(fp)) {
168*a967d739SClaudio Martella 				ierr();
169*a967d739SClaudio Martella 				return;
170*a967d739SClaudio Martella 			}
171*a967d739SClaudio Martella 		} else {
172*a967d739SClaudio Martella 			if (displaylines(fp, off))
173*a967d739SClaudio Martella 				return;
174*a967d739SClaudio Martella 		}
175*a967d739SClaudio Martella 		break;
176*a967d739SClaudio Martella 	default:
177*a967d739SClaudio Martella 		break;
178*a967d739SClaudio Martella 	}
179*a967d739SClaudio Martella 
180*a967d739SClaudio Martella 	if (fflag) {
181*a967d739SClaudio Martella #ifndef __minix
182*a967d739SClaudio Martella 		kq = kqueue();
183*a967d739SClaudio Martella 		if (kq < 0)
184*a967d739SClaudio Martella 			xerr(1, "kqueue");
185*a967d739SClaudio Martella 		action = ADD_EVENTS;
186*a967d739SClaudio Martella #else
187*a967d739SClaudio Martella         action = USE_SLEEP;
188*a967d739SClaudio Martella #endif
189*a967d739SClaudio Martella 	}
190*a967d739SClaudio Martella 
191*a967d739SClaudio Martella 	for (;;) {
192*a967d739SClaudio Martella 		while ((ch = getc(fp)) != EOF)  {
193*a967d739SClaudio Martella 			if (putchar(ch) == EOF)
194*a967d739SClaudio Martella 				oerr();
195*a967d739SClaudio Martella 		}
196*a967d739SClaudio Martella 		if (ferror(fp)) {
197*a967d739SClaudio Martella 			ierr();
198*a967d739SClaudio Martella 			return;
199*a967d739SClaudio Martella 		}
200*a967d739SClaudio Martella 		(void)fflush(stdout);
201*a967d739SClaudio Martella 		if (!fflag)
202*a967d739SClaudio Martella 			break;
203*a967d739SClaudio Martella 
204*a967d739SClaudio Martella 		clearerr(fp);
205*a967d739SClaudio Martella 
206*a967d739SClaudio Martella 		switch (action) {
207*a967d739SClaudio Martella #ifndef __minix
208*a967d739SClaudio Martella 		case ADD_EVENTS:
209*a967d739SClaudio Martella 			n = 0;
210*a967d739SClaudio Martella 
211*a967d739SClaudio Martella 			memset(ev, 0, sizeof(ev));
212*a967d739SClaudio Martella 			if (fflag == 2 && fileno(fp) != STDIN_FILENO) {
213*a967d739SClaudio Martella 				EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
214*a967d739SClaudio Martella 				    EV_ADD | EV_ENABLE | EV_CLEAR,
215*a967d739SClaudio Martella 				    NOTE_DELETE | NOTE_RENAME, 0, 0);
216*a967d739SClaudio Martella 				n++;
217*a967d739SClaudio Martella 			}
218*a967d739SClaudio Martella 			EV_SET(&ev[n], fileno(fp), EVFILT_READ,
219*a967d739SClaudio Martella 			    EV_ADD | EV_ENABLE, 0, 0, 0);
220*a967d739SClaudio Martella 			n++;
221*a967d739SClaudio Martella 
222*a967d739SClaudio Martella 			if (kevent(kq, ev, n, NULL, 0, NULL) == -1) {
223*a967d739SClaudio Martella 				close(kq);
224*a967d739SClaudio Martella 				kq = -1;
225*a967d739SClaudio Martella 				action = USE_SLEEP;
226*a967d739SClaudio Martella 			} else {
227*a967d739SClaudio Martella 				action = USE_KQUEUE;
228*a967d739SClaudio Martella 			}
229*a967d739SClaudio Martella 			break;
230*a967d739SClaudio Martella 
231*a967d739SClaudio Martella 		case USE_KQUEUE:
232*a967d739SClaudio Martella 			if (kevent(kq, NULL, 0, ev, 1, NULL) == -1)
233*a967d739SClaudio Martella 				xerr(1, "kevent");
234*a967d739SClaudio Martella 
235*a967d739SClaudio Martella 			if (ev[0].filter == EVFILT_VNODE) {
236*a967d739SClaudio Martella 				/* file was rotated, wait until it reappears */
237*a967d739SClaudio Martella 				action = USE_SLEEP;
238*a967d739SClaudio Martella 			} else if (ev[0].data < 0) {
239*a967d739SClaudio Martella 				/* file shrank, reposition to end */
240*a967d739SClaudio Martella 				if (fseek(fp, 0L, SEEK_END) == -1) {
241*a967d739SClaudio Martella 					ierr();
242*a967d739SClaudio Martella 					return;
243*a967d739SClaudio Martella 				}
244*a967d739SClaudio Martella 			}
245*a967d739SClaudio Martella 			break;
246*a967d739SClaudio Martella #endif
247*a967d739SClaudio Martella 
248*a967d739SClaudio Martella 		case USE_SLEEP:
249*a967d739SClaudio Martella 			/*
250*a967d739SClaudio Martella 			 * We pause for one second after displaying any data
251*a967d739SClaudio Martella 			 * that has accumulated since we read the file.
252*a967d739SClaudio Martella 			 */
253*a967d739SClaudio Martella                 	(void) sleep(1);
254*a967d739SClaudio Martella 
255*a967d739SClaudio Martella 			if (fflag == 2 && fileno(fp) != STDIN_FILENO &&
256*a967d739SClaudio Martella 			    stat(fname, &statbuf) != -1) {
257*a967d739SClaudio Martella 				if (statbuf.st_ino != sbp->st_ino ||
258*a967d739SClaudio Martella 				    statbuf.st_dev != sbp->st_dev ||
259*a967d739SClaudio Martella 				    statbuf.st_rdev != sbp->st_rdev ||
260*a967d739SClaudio Martella 				    statbuf.st_nlink == 0) {
261*a967d739SClaudio Martella 					fp = freopen(fname, "r", fp);
262*a967d739SClaudio Martella 					if (fp == NULL) {
263*a967d739SClaudio Martella 						ierr();
264*a967d739SClaudio Martella 						goto out;
265*a967d739SClaudio Martella 					}
266*a967d739SClaudio Martella 					*sbp = statbuf;
267*a967d739SClaudio Martella 					if (kq != -1)
268*a967d739SClaudio Martella 						action = ADD_EVENTS;
269*a967d739SClaudio Martella 				} else if (kq != -1)
270*a967d739SClaudio Martella 					action = USE_KQUEUE;
271*a967d739SClaudio Martella 			}
272*a967d739SClaudio Martella 			break;
273*a967d739SClaudio Martella 		}
274*a967d739SClaudio Martella 	}
275*a967d739SClaudio Martella out:
276*a967d739SClaudio Martella 	if (fflag && kq != -1)
277*a967d739SClaudio Martella 		close(kq);
278*a967d739SClaudio Martella }
279*a967d739SClaudio Martella 
280*a967d739SClaudio Martella /*
281*a967d739SClaudio Martella  * rlines -- display the last offset lines of the file.
282*a967d739SClaudio Martella  *
283*a967d739SClaudio Martella  * Non-zero return means than a (non-fatal) error occurred.
284*a967d739SClaudio Martella  */
285*a967d739SClaudio Martella static int
286*a967d739SClaudio Martella rlines(FILE *fp, off_t off, struct stat *sbp)
287*a967d739SClaudio Martella {
288*a967d739SClaudio Martella 	off_t file_size;
289*a967d739SClaudio Martella 	off_t file_remaining;
290*a967d739SClaudio Martella 	char *p = NULL;
291*a967d739SClaudio Martella 	char *start = NULL;
292*a967d739SClaudio Martella 	off_t mmap_size;
293*a967d739SClaudio Martella 	off_t mmap_offset;
294*a967d739SClaudio Martella 	off_t mmap_remaining = 0;
295*a967d739SClaudio Martella 
296*a967d739SClaudio Martella #define MMAP_MAXSIZE  (10 * 1024 * 1024)
297*a967d739SClaudio Martella 
298*a967d739SClaudio Martella 	if (!(file_size = sbp->st_size))
299*a967d739SClaudio Martella 		return 0;
300*a967d739SClaudio Martella 	file_remaining = file_size;
301*a967d739SClaudio Martella 
302*a967d739SClaudio Martella 	if (file_remaining > MMAP_MAXSIZE) {
303*a967d739SClaudio Martella 		mmap_size = MMAP_MAXSIZE;
304*a967d739SClaudio Martella 		mmap_offset = file_remaining - MMAP_MAXSIZE;
305*a967d739SClaudio Martella 	} else {
306*a967d739SClaudio Martella 		mmap_size = file_remaining;
307*a967d739SClaudio Martella 		mmap_offset = 0;
308*a967d739SClaudio Martella 	}
309*a967d739SClaudio Martella 
310*a967d739SClaudio Martella 	while (off) {
311*a967d739SClaudio Martella 		start = mmap(NULL, (size_t)mmap_size, PROT_READ,
312*a967d739SClaudio Martella 			     MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset);
313*a967d739SClaudio Martella 		if (start == MAP_FAILED) {
314*a967d739SClaudio Martella 			xerr(0, "%s", fname);
315*a967d739SClaudio Martella 			return 1;
316*a967d739SClaudio Martella 		}
317*a967d739SClaudio Martella 
318*a967d739SClaudio Martella 		mmap_remaining = mmap_size;
319*a967d739SClaudio Martella 		/* Last char is special, ignore whether newline or not. */
320*a967d739SClaudio Martella 		for (p = start + mmap_remaining - 1 ; --mmap_remaining ; )
321*a967d739SClaudio Martella 			if (*--p == '\n' && !--off) {
322*a967d739SClaudio Martella 				++p;
323*a967d739SClaudio Martella 				break;
324*a967d739SClaudio Martella 			}
325*a967d739SClaudio Martella 
326*a967d739SClaudio Martella 		file_remaining -= mmap_size - mmap_remaining;
327*a967d739SClaudio Martella 
328*a967d739SClaudio Martella 		if (off == 0)
329*a967d739SClaudio Martella 			break;
330*a967d739SClaudio Martella 
331*a967d739SClaudio Martella 		if (file_remaining == 0)
332*a967d739SClaudio Martella 			break;
333*a967d739SClaudio Martella 
334*a967d739SClaudio Martella 		if (munmap(start, mmap_size)) {
335*a967d739SClaudio Martella 			xerr(0, "%s", fname);
336*a967d739SClaudio Martella 			return 1;
337*a967d739SClaudio Martella 		}
338*a967d739SClaudio Martella 
339*a967d739SClaudio Martella 		if (mmap_offset >= MMAP_MAXSIZE) {
340*a967d739SClaudio Martella 			mmap_offset -= MMAP_MAXSIZE;
341*a967d739SClaudio Martella 		} else {
342*a967d739SClaudio Martella 			mmap_offset = 0;
343*a967d739SClaudio Martella 			mmap_size = file_remaining;
344*a967d739SClaudio Martella 		}
345*a967d739SClaudio Martella 	}
346*a967d739SClaudio Martella 
347*a967d739SClaudio Martella 	/*
348*a967d739SClaudio Martella 	 * Output the (perhaps partial) data in this mmap'd block.
349*a967d739SClaudio Martella 	 */
350*a967d739SClaudio Martella 	WR(p, mmap_size - mmap_remaining);
351*a967d739SClaudio Martella 	file_remaining += mmap_size - mmap_remaining;
352*a967d739SClaudio Martella 	if (munmap(start, mmap_size)) {
353*a967d739SClaudio Martella 		xerr(0, "%s", fname);
354*a967d739SClaudio Martella 		return 1;
355*a967d739SClaudio Martella 	}
356*a967d739SClaudio Martella 
357*a967d739SClaudio Martella 	/*
358*a967d739SClaudio Martella 	 * Set the file pointer to reflect the length displayed.
359*a967d739SClaudio Martella 	 * This will cause the caller to redisplay the data if/when
360*a967d739SClaudio Martella 	 * needed.
361*a967d739SClaudio Martella 	 */
362*a967d739SClaudio Martella 	if (fseeko(fp, file_remaining, SEEK_SET) == -1) {
363*a967d739SClaudio Martella 		ierr();
364*a967d739SClaudio Martella 		return 1;
365*a967d739SClaudio Martella 	}
366*a967d739SClaudio Martella 	return 0;
367*a967d739SClaudio Martella }
368