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