forward.c revision 74827
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 74827 2001-03-26 19:29:49Z ache $";
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{
27069552Sasmodai	off_t size;
27169552Sasmodai	char *p;
2721590Srgrimes	char *start;
2731590Srgrimes
2741590Srgrimes	if (!(size = sbp->st_size))
2751590Srgrimes		return;
2761590Srgrimes
27774827Sache	if (size > SIZE_T_MAX || size < 0) {
27817821Speter		errno = EFBIG;
27917833Sadam		ierr();
28074827Sache		exit(1);
2811590Srgrimes	}
2821590Srgrimes
28374827Sache	/* XXX: FIXME - mmap() not support files over 2Gb */
28474827Sache	if (size > INT_MAX) {
28574827Sache		errno = EFBIG;
28674827Sache		ierr();
28774827Sache		exit(1);
28874827Sache	}
28974827Sache
2901590Srgrimes	if ((start = mmap(NULL, (size_t)size,
29121786Salex	    PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
29217833Sadam		ierr();
2931590Srgrimes		return;
2941590Srgrimes	}
2951590Srgrimes
2961590Srgrimes	/* Last char is special, ignore whether newline or not. */
2971590Srgrimes	for (p = start + size - 1; --size;)
2981590Srgrimes		if (*--p == '\n' && !--off) {
2991590Srgrimes			++p;
3001590Srgrimes			break;
3011590Srgrimes		}
3021590Srgrimes
3031590Srgrimes	/* Set the file pointer to reflect the length displayed. */
3041590Srgrimes	size = sbp->st_size - size;
3051590Srgrimes	WR(p, size);
30674827Sache	if (fseek(fp, 0L, SEEK_END) == -1) {
3071590Srgrimes		ierr();
3081590Srgrimes		return;
3091590Srgrimes	}
3101590Srgrimes	if (munmap(start, (size_t)sbp->st_size)) {
31117833Sadam		ierr();
3121590Srgrimes		return;
3131590Srgrimes	}
3141590Srgrimes}
315