forward.c revision 74876
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1991, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * This code is derived from software contributed to Berkeley by
61590Srgrimes * Edward Sze-Tyan Wang.
71590Srgrimes *
81590Srgrimes * Redistribution and use in source and binary forms, with or without
91590Srgrimes * modification, are permitted provided that the following conditions
101590Srgrimes * are met:
111590Srgrimes * 1. Redistributions of source code must retain the above copyright
121590Srgrimes *    notice, this list of conditions and the following disclaimer.
131590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141590Srgrimes *    notice, this list of conditions and the following disclaimer in the
151590Srgrimes *    documentation and/or other materials provided with the distribution.
161590Srgrimes * 3. All advertising materials mentioning features or use of this software
171590Srgrimes *    must display the following acknowledgement:
181590Srgrimes *	This product includes software developed by the University of
191590Srgrimes *	California, Berkeley and its contributors.
201590Srgrimes * 4. Neither the name of the University nor the names of its contributors
211590Srgrimes *    may be used to endorse or promote products derived from this software
221590Srgrimes *    without specific prior written permission.
231590Srgrimes *
241590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341590Srgrimes * SUCH DAMAGE.
351590Srgrimes */
361590Srgrimes
371590Srgrimes#ifndef lint
3869528Sasmodai#if 0
391590Srgrimesstatic char sccsid[] = "@(#)forward.c	8.1 (Berkeley) 6/6/93";
4069528Sasmodai#endif
4169528Sasmodaistatic const char rcsid[] =
4269528Sasmodai  "$FreeBSD: head/usr.bin/tail/forward.c 74876 2001-03-27 20:37:34Z dwmalone $";
431590Srgrimes#endif /* not lint */
441590Srgrimes
451590Srgrimes#include <sys/types.h>
461590Srgrimes#include <sys/stat.h>
471590Srgrimes#include <sys/time.h>
481590Srgrimes#include <sys/mman.h>
4959373Sjlemon#include <sys/event.h>
501590Srgrimes
511590Srgrimes#include <limits.h>
521590Srgrimes#include <fcntl.h>
531590Srgrimes#include <errno.h>
541590Srgrimes#include <unistd.h>
551590Srgrimes#include <stdio.h>
561590Srgrimes#include <stdlib.h>
571590Srgrimes#include <string.h>
5817826Speter#include <err.h>
591590Srgrimes#include "extern.h"
601590Srgrimes
611590Srgrimesstatic void rlines __P((FILE *, long, struct stat *));
621590Srgrimes
6361964Sjlemon/* defines for inner loop actions */
6461964Sjlemon#define USE_SLEEP	0
6561964Sjlemon#define USE_KQUEUE	1
6661964Sjlemon#define ADD_EVENTS	2
6761964Sjlemon
681590Srgrimes/*
691590Srgrimes * forward -- display the file, from an offset, forward.
701590Srgrimes *
711590Srgrimes * There are eight separate cases for this -- regular and non-regular
721590Srgrimes * files, by bytes or lines and from the beginning or end of the file.
731590Srgrimes *
741590Srgrimes * FBYTES	byte offset from the beginning of the file
751590Srgrimes *	REG	seek
761590Srgrimes *	NOREG	read, counting bytes
771590Srgrimes *
781590Srgrimes * FLINES	line offset from the beginning of the file
791590Srgrimes *	REG	read, counting lines
801590Srgrimes *	NOREG	read, counting lines
811590Srgrimes *
821590Srgrimes * RBYTES	byte offset from the end of the file
831590Srgrimes *	REG	seek
841590Srgrimes *	NOREG	cyclically read characters into a wrap-around buffer
851590Srgrimes *
861590Srgrimes * RLINES
871590Srgrimes *	REG	mmap the file and step back until reach the correct offset.
881590Srgrimes *	NOREG	cyclically read lines into a wrap-around array of buffers
891590Srgrimes */
901590Srgrimesvoid
911590Srgrimesforward(fp, style, off, sbp)
921590Srgrimes	FILE *fp;
931590Srgrimes	enum STYLE style;
941590Srgrimes	long off;
951590Srgrimes	struct stat *sbp;
961590Srgrimes{
9761964Sjlemon	int ch, kq = -1;
9861964Sjlemon	int action = USE_SLEEP;
9959291Sjlemon	struct kevent ev[2];
10061964Sjlemon	struct stat sb2;
1011590Srgrimes
1021590Srgrimes	switch(style) {
1031590Srgrimes	case FBYTES:
1041590Srgrimes		if (off == 0)
1051590Srgrimes			break;
1061590Srgrimes		if (S_ISREG(sbp->st_mode)) {
1071590Srgrimes			if (sbp->st_size < off)
1081590Srgrimes				off = sbp->st_size;
1091590Srgrimes			if (fseek(fp, off, SEEK_SET) == -1) {
1101590Srgrimes				ierr();
1111590Srgrimes				return;
1121590Srgrimes			}
1131590Srgrimes		} else while (off--)
1141590Srgrimes			if ((ch = getc(fp)) == EOF) {
1151590Srgrimes				if (ferror(fp)) {
1161590Srgrimes					ierr();
1171590Srgrimes					return;
1181590Srgrimes				}
1191590Srgrimes				break;
1201590Srgrimes			}
1211590Srgrimes		break;
1221590Srgrimes	case FLINES:
1231590Srgrimes		if (off == 0)
1241590Srgrimes			break;
1251590Srgrimes		for (;;) {
1261590Srgrimes			if ((ch = getc(fp)) == EOF) {
1271590Srgrimes				if (ferror(fp)) {
1281590Srgrimes					ierr();
1291590Srgrimes					return;
1301590Srgrimes				}
1311590Srgrimes				break;
1321590Srgrimes			}
1331590Srgrimes			if (ch == '\n' && !--off)
1341590Srgrimes				break;
1351590Srgrimes		}
1361590Srgrimes		break;
1371590Srgrimes	case RBYTES:
1381590Srgrimes		if (S_ISREG(sbp->st_mode)) {
1391590Srgrimes			if (sbp->st_size >= off &&
1401590Srgrimes			    fseek(fp, -off, SEEK_END) == -1) {
1411590Srgrimes				ierr();
1421590Srgrimes				return;
1431590Srgrimes			}
1441590Srgrimes		} else if (off == 0) {
1451590Srgrimes			while (getc(fp) != EOF);
1461590Srgrimes			if (ferror(fp)) {
1471590Srgrimes				ierr();
1481590Srgrimes				return;
1491590Srgrimes			}
1501590Srgrimes		} else
15117341Sadam			if (bytes(fp, off))
15217341Sadam				return;
1531590Srgrimes		break;
1541590Srgrimes	case RLINES:
1551590Srgrimes		if (S_ISREG(sbp->st_mode))
1561590Srgrimes			if (!off) {
1571590Srgrimes				if (fseek(fp, 0L, SEEK_END) == -1) {
1581590Srgrimes					ierr();
1591590Srgrimes					return;
1601590Srgrimes				}
1611590Srgrimes			} else
1621590Srgrimes				rlines(fp, off, sbp);
1631590Srgrimes		else if (off == 0) {
1641590Srgrimes			while (getc(fp) != EOF);
1651590Srgrimes			if (ferror(fp)) {
1661590Srgrimes				ierr();
1671590Srgrimes				return;
1681590Srgrimes			}
1691590Srgrimes		} else
17017341Sadam			if (lines(fp, off))
17117341Sadam				return;
1721590Srgrimes		break;
1731590Srgrimes	}
1741590Srgrimes
17559291Sjlemon	if (fflag) {
17659291Sjlemon		kq = kqueue();
17759291Sjlemon		if (kq < 0)
17859291Sjlemon			err(1, "kqueue");
17961964Sjlemon		action = ADD_EVENTS;
18059291Sjlemon	}
1811590Srgrimes
1821590Srgrimes	for (;;) {
1831590Srgrimes		while ((ch = getc(fp)) != EOF)
1841590Srgrimes			if (putchar(ch) == EOF)
1851590Srgrimes				oerr();
1861590Srgrimes		if (ferror(fp)) {
1871590Srgrimes			ierr();
1881590Srgrimes			return;
1891590Srgrimes		}
1901590Srgrimes		(void)fflush(stdout);
19159291Sjlemon		if (! fflag)
1921590Srgrimes			break;
1931590Srgrimes		clearerr(fp);
19435081Speter
19561964Sjlemon		switch (action) {
19661964Sjlemon		case ADD_EVENTS: {
19759291Sjlemon			int n = 0;
19859291Sjlemon			struct timespec ts = { 0, 0 };
19959291Sjlemon
20059291Sjlemon			if (Fflag && fileno(fp) != STDIN_FILENO) {
20173231Sjlemon				EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
20273231Sjlemon				    EV_ADD | EV_ENABLE | EV_CLEAR,
20373231Sjlemon				    NOTE_DELETE | NOTE_RENAME, 0, 0);
20459291Sjlemon				n++;
20535081Speter			}
20673231Sjlemon			EV_SET(&ev[n], fileno(fp), EVFILT_READ,
20773231Sjlemon			    EV_ADD | EV_ENABLE, 0, 0, 0);
20859291Sjlemon			n++;
20959291Sjlemon
21063454Sjlemon			if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
21161964Sjlemon				close(kq);
21261964Sjlemon				kq = -1;
21361964Sjlemon				action = USE_SLEEP;
21461964Sjlemon			} else {
21561964Sjlemon				action = USE_KQUEUE;
21661964Sjlemon			}
21761964Sjlemon			break;
21835081Speter		}
21959291Sjlemon
22061964Sjlemon		case USE_KQUEUE:
22163454Sjlemon			if (kevent(kq, NULL, 0, ev, 1, NULL) < 0)
22261964Sjlemon				err(1, "kevent");
22359291Sjlemon
22461964Sjlemon			if (ev->filter == EVFILT_VNODE) {
22561964Sjlemon				/* file was rotated, wait until it reappears */
22661964Sjlemon				action = USE_SLEEP;
22761964Sjlemon			} else if (ev->data < 0) {
22861964Sjlemon				/* file shrank, reposition to end */
22961964Sjlemon				if (fseek(fp, 0L, SEEK_END) == -1) {
23061964Sjlemon					ierr();
23161964Sjlemon					return;
23261964Sjlemon				}
23359291Sjlemon			}
23461964Sjlemon			break;
23561964Sjlemon
23661964Sjlemon		case USE_SLEEP:
23761964Sjlemon                	(void) usleep(250000);
23861964Sjlemon	                clearerr(fp);
23961964Sjlemon
24061964Sjlemon			if (Fflag && fileno(fp) != STDIN_FILENO &&
24161964Sjlemon			    stat(fname, &sb2) != -1) {
24261964Sjlemon				if (sb2.st_ino != sbp->st_ino ||
24361964Sjlemon				    sb2.st_dev != sbp->st_dev ||
24461964Sjlemon				    sb2.st_rdev != sbp->st_rdev ||
24561964Sjlemon				    sb2.st_nlink == 0) {
24661964Sjlemon					fp = freopen(fname, "r", fp);
24761964Sjlemon					if (fp == NULL) {
24861964Sjlemon						ierr();
24961964Sjlemon						break;
25061964Sjlemon					}
25161964Sjlemon					*sbp = sb2;
25261964Sjlemon					if (kq != -1)
25361964Sjlemon						action = ADD_EVENTS;
25461964Sjlemon				}
25559291Sjlemon			}
25661964Sjlemon			break;
25759291Sjlemon		}
2581590Srgrimes	}
2591590Srgrimes}
2601590Srgrimes
2611590Srgrimes/*
2621590Srgrimes * rlines -- display the last offset lines of the file.
2631590Srgrimes */
2641590Srgrimesstatic void
2651590Srgrimesrlines(fp, off, sbp)
2661590Srgrimes	FILE *fp;
2671590Srgrimes	long off;
2681590Srgrimes	struct stat *sbp;
2691590Srgrimes{
27074876Sdwmalone	struct mapinfo map;
27174876Sdwmalone	off_t curoff, size;
27274876Sdwmalone	int i;
2731590Srgrimes
2741590Srgrimes	if (!(size = sbp->st_size))
2751590Srgrimes		return;
27674876Sdwmalone	map.start = NULL;
27774876Sdwmalone	map.fd = fileno(fp);
27874876Sdwmalone	map.mapoff = map.maxoff = size;
2791590Srgrimes
28074863Sache	/*
28174876Sdwmalone	 * Last char is special, ignore whether newline or not. Note that
28274876Sdwmalone	 * size == 0 is dealt with above, and size == 1 sets curoff to -1.
28374863Sache	 */
28474876Sdwmalone	curoff = size - 2;
28574876Sdwmalone	while (curoff >= 0) {
28674876Sdwmalone		if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
28774876Sdwmalone			ierr();
28874876Sdwmalone			return;
28974876Sdwmalone		}
29074876Sdwmalone		for (i = curoff - map.mapoff; i >= 0; i--)
29174876Sdwmalone			if (map.start[i] == '\n' && --off == 0)
29274876Sdwmalone				break;
29374876Sdwmalone		/* `i' is either the map offset of a '\n', or -1. */
29474876Sdwmalone		curoff = map.mapoff + i;
29574876Sdwmalone		if (i >= 0)
29674876Sdwmalone			break;
2971590Srgrimes	}
29874876Sdwmalone	curoff++;
29974876Sdwmalone	if (mapprint(&map, curoff, size - curoff) != 0) {
30074827Sache		ierr();
30174827Sache		exit(1);
30274827Sache	}
30374827Sache
3041590Srgrimes	/* Set the file pointer to reflect the length displayed. */
30574876Sdwmalone	if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
3061590Srgrimes		ierr();
3071590Srgrimes		return;
3081590Srgrimes	}
30974876Sdwmalone	if (map.start != NULL && munmap(map.start, map.maplen)) {
31017833Sadam		ierr();
3111590Srgrimes		return;
3121590Srgrimes	}
3131590Srgrimes}
314