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