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