19b50d902SRodney W. Grimes /*- 29b50d902SRodney W. Grimes * Copyright (c) 1991, 1993 39b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 49b50d902SRodney W. Grimes * 59b50d902SRodney W. Grimes * This code is derived from software contributed to Berkeley by 69b50d902SRodney W. Grimes * Edward Sze-Tyan Wang. 79b50d902SRodney W. Grimes * 89b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 99b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 109b50d902SRodney W. Grimes * are met: 119b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 129b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 139b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 149b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 159b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 169b50d902SRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 179b50d902SRodney W. Grimes * must display the following acknowledgement: 189b50d902SRodney W. Grimes * This product includes software developed by the University of 199b50d902SRodney W. Grimes * California, Berkeley and its contributors. 209b50d902SRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 219b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 229b50d902SRodney W. Grimes * without specific prior written permission. 239b50d902SRodney W. Grimes * 249b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 259b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 269b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 279b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 289b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 299b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 309b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 319b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 329b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 339b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 349b50d902SRodney W. Grimes * SUCH DAMAGE. 35b446630fSJonathan Lemon * 36b446630fSJonathan Lemon * $FreeBSD$ 379b50d902SRodney W. Grimes */ 389b50d902SRodney W. Grimes 399b50d902SRodney W. Grimes #ifndef lint 409b50d902SRodney W. Grimes static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 419b50d902SRodney W. Grimes #endif /* not lint */ 429b50d902SRodney W. Grimes 439b50d902SRodney W. Grimes #include <sys/types.h> 449b50d902SRodney W. Grimes #include <sys/stat.h> 459b50d902SRodney W. Grimes #include <sys/time.h> 469b50d902SRodney W. Grimes #include <sys/mman.h> 4732462e82SJonathan Lemon #include <sys/event.h> 489b50d902SRodney W. Grimes 499b50d902SRodney W. Grimes #include <limits.h> 509b50d902SRodney W. Grimes #include <fcntl.h> 519b50d902SRodney W. Grimes #include <errno.h> 529b50d902SRodney W. Grimes #include <unistd.h> 539b50d902SRodney W. Grimes #include <stdio.h> 549b50d902SRodney W. Grimes #include <stdlib.h> 559b50d902SRodney W. Grimes #include <string.h> 56c3fd5728SPeter Wemm #include <err.h> 579b50d902SRodney W. Grimes #include "extern.h" 589b50d902SRodney W. Grimes 599b50d902SRodney W. Grimes static void rlines __P((FILE *, long, struct stat *)); 609b50d902SRodney W. Grimes 61ea9bd2dfSJonathan Lemon /* defines for inner loop actions */ 62ea9bd2dfSJonathan Lemon #define USE_SLEEP 0 63ea9bd2dfSJonathan Lemon #define USE_KQUEUE 1 64ea9bd2dfSJonathan Lemon #define ADD_EVENTS 2 65ea9bd2dfSJonathan Lemon 669b50d902SRodney W. Grimes /* 679b50d902SRodney W. Grimes * forward -- display the file, from an offset, forward. 689b50d902SRodney W. Grimes * 699b50d902SRodney W. Grimes * There are eight separate cases for this -- regular and non-regular 709b50d902SRodney W. Grimes * files, by bytes or lines and from the beginning or end of the file. 719b50d902SRodney W. Grimes * 729b50d902SRodney W. Grimes * FBYTES byte offset from the beginning of the file 739b50d902SRodney W. Grimes * REG seek 749b50d902SRodney W. Grimes * NOREG read, counting bytes 759b50d902SRodney W. Grimes * 769b50d902SRodney W. Grimes * FLINES line offset from the beginning of the file 779b50d902SRodney W. Grimes * REG read, counting lines 789b50d902SRodney W. Grimes * NOREG read, counting lines 799b50d902SRodney W. Grimes * 809b50d902SRodney W. Grimes * RBYTES byte offset from the end of the file 819b50d902SRodney W. Grimes * REG seek 829b50d902SRodney W. Grimes * NOREG cyclically read characters into a wrap-around buffer 839b50d902SRodney W. Grimes * 849b50d902SRodney W. Grimes * RLINES 859b50d902SRodney W. Grimes * REG mmap the file and step back until reach the correct offset. 869b50d902SRodney W. Grimes * NOREG cyclically read lines into a wrap-around array of buffers 879b50d902SRodney W. Grimes */ 889b50d902SRodney W. Grimes void 899b50d902SRodney W. Grimes forward(fp, style, off, sbp) 909b50d902SRodney W. Grimes FILE *fp; 919b50d902SRodney W. Grimes enum STYLE style; 929b50d902SRodney W. Grimes long off; 939b50d902SRodney W. Grimes struct stat *sbp; 949b50d902SRodney W. Grimes { 95ea9bd2dfSJonathan Lemon int ch, kq = -1; 96ea9bd2dfSJonathan Lemon int action = USE_SLEEP; 97b446630fSJonathan Lemon struct kevent ev[2]; 98ea9bd2dfSJonathan Lemon struct stat sb2; 999b50d902SRodney W. Grimes 1009b50d902SRodney W. Grimes switch(style) { 1019b50d902SRodney W. Grimes case FBYTES: 1029b50d902SRodney W. Grimes if (off == 0) 1039b50d902SRodney W. Grimes break; 1049b50d902SRodney W. Grimes if (S_ISREG(sbp->st_mode)) { 1059b50d902SRodney W. Grimes if (sbp->st_size < off) 1069b50d902SRodney W. Grimes off = sbp->st_size; 1079b50d902SRodney W. Grimes if (fseek(fp, off, SEEK_SET) == -1) { 1089b50d902SRodney W. Grimes ierr(); 1099b50d902SRodney W. Grimes return; 1109b50d902SRodney W. Grimes } 1119b50d902SRodney W. Grimes } else while (off--) 1129b50d902SRodney W. Grimes if ((ch = getc(fp)) == EOF) { 1139b50d902SRodney W. Grimes if (ferror(fp)) { 1149b50d902SRodney W. Grimes ierr(); 1159b50d902SRodney W. Grimes return; 1169b50d902SRodney W. Grimes } 1179b50d902SRodney W. Grimes break; 1189b50d902SRodney W. Grimes } 1199b50d902SRodney W. Grimes break; 1209b50d902SRodney W. Grimes case FLINES: 1219b50d902SRodney W. Grimes if (off == 0) 1229b50d902SRodney W. Grimes break; 1239b50d902SRodney W. Grimes for (;;) { 1249b50d902SRodney W. Grimes if ((ch = getc(fp)) == EOF) { 1259b50d902SRodney W. Grimes if (ferror(fp)) { 1269b50d902SRodney W. Grimes ierr(); 1279b50d902SRodney W. Grimes return; 1289b50d902SRodney W. Grimes } 1299b50d902SRodney W. Grimes break; 1309b50d902SRodney W. Grimes } 1319b50d902SRodney W. Grimes if (ch == '\n' && !--off) 1329b50d902SRodney W. Grimes break; 1339b50d902SRodney W. Grimes } 1349b50d902SRodney W. Grimes break; 1359b50d902SRodney W. Grimes case RBYTES: 1369b50d902SRodney W. Grimes if (S_ISREG(sbp->st_mode)) { 1379b50d902SRodney W. Grimes if (sbp->st_size >= off && 1389b50d902SRodney W. Grimes fseek(fp, -off, SEEK_END) == -1) { 1399b50d902SRodney W. Grimes ierr(); 1409b50d902SRodney W. Grimes return; 1419b50d902SRodney W. Grimes } 1429b50d902SRodney W. Grimes } else if (off == 0) { 1439b50d902SRodney W. Grimes while (getc(fp) != EOF); 1449b50d902SRodney W. Grimes if (ferror(fp)) { 1459b50d902SRodney W. Grimes ierr(); 1469b50d902SRodney W. Grimes return; 1479b50d902SRodney W. Grimes } 1489b50d902SRodney W. Grimes } else 1496439f56eSAdam David if (bytes(fp, off)) 1506439f56eSAdam David return; 1519b50d902SRodney W. Grimes break; 1529b50d902SRodney W. Grimes case RLINES: 1539b50d902SRodney W. Grimes if (S_ISREG(sbp->st_mode)) 1549b50d902SRodney W. Grimes if (!off) { 1559b50d902SRodney W. Grimes if (fseek(fp, 0L, SEEK_END) == -1) { 1569b50d902SRodney W. Grimes ierr(); 1579b50d902SRodney W. Grimes return; 1589b50d902SRodney W. Grimes } 1599b50d902SRodney W. Grimes } else 1609b50d902SRodney W. Grimes rlines(fp, off, sbp); 1619b50d902SRodney W. Grimes else if (off == 0) { 1629b50d902SRodney W. Grimes while (getc(fp) != EOF); 1639b50d902SRodney W. Grimes if (ferror(fp)) { 1649b50d902SRodney W. Grimes ierr(); 1659b50d902SRodney W. Grimes return; 1669b50d902SRodney W. Grimes } 1679b50d902SRodney W. Grimes } else 1686439f56eSAdam David if (lines(fp, off)) 1696439f56eSAdam David return; 1709b50d902SRodney W. Grimes break; 1719b50d902SRodney W. Grimes } 1729b50d902SRodney W. Grimes 173b446630fSJonathan Lemon if (fflag) { 174b446630fSJonathan Lemon kq = kqueue(); 175b446630fSJonathan Lemon if (kq < 0) 176b446630fSJonathan Lemon err(1, "kqueue"); 177ea9bd2dfSJonathan Lemon action = ADD_EVENTS; 178b446630fSJonathan Lemon } 1799b50d902SRodney W. Grimes 1809b50d902SRodney W. Grimes for (;;) { 1819b50d902SRodney W. Grimes while ((ch = getc(fp)) != EOF) 1829b50d902SRodney W. Grimes if (putchar(ch) == EOF) 1839b50d902SRodney W. Grimes oerr(); 1849b50d902SRodney W. Grimes if (ferror(fp)) { 1859b50d902SRodney W. Grimes ierr(); 1869b50d902SRodney W. Grimes return; 1879b50d902SRodney W. Grimes } 1889b50d902SRodney W. Grimes (void)fflush(stdout); 1899b50d902SRodney W. Grimes if (! fflag) 1909b50d902SRodney W. Grimes break; 1919b50d902SRodney W. Grimes clearerr(fp); 192eb1c9439SPeter Wemm 193ea9bd2dfSJonathan Lemon switch (action) { 194ea9bd2dfSJonathan Lemon case ADD_EVENTS: { 195b446630fSJonathan Lemon int n = 0; 196b446630fSJonathan Lemon struct kevent *evp[2]; 197b446630fSJonathan Lemon struct timespec ts = { 0, 0 }; 198b446630fSJonathan Lemon 199b446630fSJonathan Lemon if (Fflag && fileno(fp) != STDIN_FILENO) { 200b446630fSJonathan Lemon ev[n].ident = fileno(fp); 201b446630fSJonathan Lemon ev[n].filter = EVFILT_VNODE; 202b446630fSJonathan Lemon ev[n].flags = EV_ADD | EV_ENABLE | EV_CLEAR; 203b446630fSJonathan Lemon ev[n].fflags = NOTE_DELETE | NOTE_RENAME; 204b446630fSJonathan Lemon evp[n] = &ev[n]; 205b446630fSJonathan Lemon n++; 206b446630fSJonathan Lemon } 207b446630fSJonathan Lemon ev[n].ident = fileno(fp); 208b446630fSJonathan Lemon ev[n].filter = EVFILT_READ; 209b446630fSJonathan Lemon ev[n].flags = EV_ADD | EV_ENABLE; 210b446630fSJonathan Lemon evp[n] = &ev[n]; 211b446630fSJonathan Lemon n++; 212b446630fSJonathan Lemon 213ea9bd2dfSJonathan Lemon if (kevent(kq, n, evp, 0, NULL, &ts) < 0) { 214ea9bd2dfSJonathan Lemon close(kq); 215ea9bd2dfSJonathan Lemon kq = -1; 216ea9bd2dfSJonathan Lemon action = USE_SLEEP; 217ea9bd2dfSJonathan Lemon } else { 218ea9bd2dfSJonathan Lemon action = USE_KQUEUE; 219ea9bd2dfSJonathan Lemon } 220ea9bd2dfSJonathan Lemon break; 221b446630fSJonathan Lemon } 222b446630fSJonathan Lemon 223ea9bd2dfSJonathan Lemon case USE_KQUEUE: 224b446630fSJonathan Lemon if (kevent(kq, 0, NULL, 1, ev, NULL) < 0) 225b446630fSJonathan Lemon err(1, "kevent"); 226b446630fSJonathan Lemon 227b446630fSJonathan Lemon if (ev->filter == EVFILT_VNODE) { 228ea9bd2dfSJonathan Lemon /* file was rotated, wait until it reappears */ 229ea9bd2dfSJonathan Lemon action = USE_SLEEP; 230b446630fSJonathan Lemon } else if (ev->data < 0) { 231b446630fSJonathan Lemon /* file shrank, reposition to end */ 232b446630fSJonathan Lemon if (fseek(fp, 0L, SEEK_END) == -1) { 233b446630fSJonathan Lemon ierr(); 234b446630fSJonathan Lemon return; 235eb1c9439SPeter Wemm } 236eb1c9439SPeter Wemm } 237ea9bd2dfSJonathan Lemon break; 238ea9bd2dfSJonathan Lemon 239ea9bd2dfSJonathan Lemon case USE_SLEEP: 240ea9bd2dfSJonathan Lemon (void) usleep(250000); 241ea9bd2dfSJonathan Lemon clearerr(fp); 242ea9bd2dfSJonathan Lemon 243ea9bd2dfSJonathan Lemon if (Fflag && fileno(fp) != STDIN_FILENO && 244ea9bd2dfSJonathan Lemon stat(fname, &sb2) != -1) { 245ea9bd2dfSJonathan Lemon if (sb2.st_ino != sbp->st_ino || 246ea9bd2dfSJonathan Lemon sb2.st_dev != sbp->st_dev || 247ea9bd2dfSJonathan Lemon sb2.st_rdev != sbp->st_rdev || 248ea9bd2dfSJonathan Lemon sb2.st_nlink == 0) { 249ea9bd2dfSJonathan Lemon fp = freopen(fname, "r", fp); 250ea9bd2dfSJonathan Lemon if (fp == NULL) { 251ea9bd2dfSJonathan Lemon ierr(); 252ea9bd2dfSJonathan Lemon break; 253ea9bd2dfSJonathan Lemon } 254ea9bd2dfSJonathan Lemon *sbp = sb2; 255ea9bd2dfSJonathan Lemon if (kq != -1) 256ea9bd2dfSJonathan Lemon action = ADD_EVENTS; 257ea9bd2dfSJonathan Lemon } 258ea9bd2dfSJonathan Lemon } 259ea9bd2dfSJonathan Lemon break; 260ea9bd2dfSJonathan Lemon } 2619b50d902SRodney W. Grimes } 2629b50d902SRodney W. Grimes } 2639b50d902SRodney W. Grimes 2649b50d902SRodney W. Grimes /* 2659b50d902SRodney W. Grimes * rlines -- display the last offset lines of the file. 2669b50d902SRodney W. Grimes */ 2679b50d902SRodney W. Grimes static void 2689b50d902SRodney W. Grimes rlines(fp, off, sbp) 2699b50d902SRodney W. Grimes FILE *fp; 2709b50d902SRodney W. Grimes long off; 2719b50d902SRodney W. Grimes struct stat *sbp; 2729b50d902SRodney W. Grimes { 2739b50d902SRodney W. Grimes register off_t size; 2749b50d902SRodney W. Grimes register char *p; 2759b50d902SRodney W. Grimes char *start; 2769b50d902SRodney W. Grimes 2779b50d902SRodney W. Grimes if (!(size = sbp->st_size)) 2789b50d902SRodney W. Grimes return; 2799b50d902SRodney W. Grimes 2809b50d902SRodney W. Grimes if (size > SIZE_T_MAX) { 2810a6f6077SPeter Wemm errno = EFBIG; 28244cf272fSAdam David ierr(); 2839b50d902SRodney W. Grimes return; 2849b50d902SRodney W. Grimes } 2859b50d902SRodney W. Grimes 2869b50d902SRodney W. Grimes if ((start = mmap(NULL, (size_t)size, 2878abdc2ebSAlexander Langer PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) { 28844cf272fSAdam David ierr(); 2899b50d902SRodney W. Grimes return; 2909b50d902SRodney W. Grimes } 2919b50d902SRodney W. Grimes 2929b50d902SRodney W. Grimes /* Last char is special, ignore whether newline or not. */ 2939b50d902SRodney W. Grimes for (p = start + size - 1; --size;) 2949b50d902SRodney W. Grimes if (*--p == '\n' && !--off) { 2959b50d902SRodney W. Grimes ++p; 2969b50d902SRodney W. Grimes break; 2979b50d902SRodney W. Grimes } 2989b50d902SRodney W. Grimes 2999b50d902SRodney W. Grimes /* Set the file pointer to reflect the length displayed. */ 3009b50d902SRodney W. Grimes size = sbp->st_size - size; 3019b50d902SRodney W. Grimes WR(p, size); 3029b50d902SRodney W. Grimes if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) { 3039b50d902SRodney W. Grimes ierr(); 3049b50d902SRodney W. Grimes return; 3059b50d902SRodney W. Grimes } 3069b50d902SRodney W. Grimes if (munmap(start, (size_t)sbp->st_size)) { 30744cf272fSAdam David ierr(); 3089b50d902SRodney W. Grimes return; 3099b50d902SRodney W. Grimes } 3109b50d902SRodney W. Grimes } 311