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