1*a967d739SClaudio Martella /* $NetBSD: forward.c,v 1.32 2013/10/18 20:47:07 christos Exp $ */ 2*a967d739SClaudio Martella 3*a967d739SClaudio Martella /*- 4*a967d739SClaudio Martella * Copyright (c) 1991, 1993 5*a967d739SClaudio Martella * The Regents of the University of California. All rights reserved. 6*a967d739SClaudio Martella * 7*a967d739SClaudio Martella * This code is derived from software contributed to Berkeley by 8*a967d739SClaudio Martella * Edward Sze-Tyan Wang. 9*a967d739SClaudio Martella * 10*a967d739SClaudio Martella * Redistribution and use in source and binary forms, with or without 11*a967d739SClaudio Martella * modification, are permitted provided that the following conditions 12*a967d739SClaudio Martella * are met: 13*a967d739SClaudio Martella * 1. Redistributions of source code must retain the above copyright 14*a967d739SClaudio Martella * notice, this list of conditions and the following disclaimer. 15*a967d739SClaudio Martella * 2. Redistributions in binary form must reproduce the above copyright 16*a967d739SClaudio Martella * notice, this list of conditions and the following disclaimer in the 17*a967d739SClaudio Martella * documentation and/or other materials provided with the distribution. 18*a967d739SClaudio Martella * 3. Neither the name of the University nor the names of its contributors 19*a967d739SClaudio Martella * may be used to endorse or promote products derived from this software 20*a967d739SClaudio Martella * without specific prior written permission. 21*a967d739SClaudio Martella * 22*a967d739SClaudio Martella * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23*a967d739SClaudio Martella * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24*a967d739SClaudio Martella * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25*a967d739SClaudio Martella * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26*a967d739SClaudio Martella * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27*a967d739SClaudio Martella * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28*a967d739SClaudio Martella * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29*a967d739SClaudio Martella * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30*a967d739SClaudio Martella * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31*a967d739SClaudio Martella * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32*a967d739SClaudio Martella * SUCH DAMAGE. 33*a967d739SClaudio Martella */ 34*a967d739SClaudio Martella 35*a967d739SClaudio Martella #include <sys/cdefs.h> 36*a967d739SClaudio Martella #ifndef lint 37*a967d739SClaudio Martella #if 0 38*a967d739SClaudio Martella static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 39*a967d739SClaudio Martella #endif 40*a967d739SClaudio Martella __RCSID("$NetBSD: forward.c,v 1.32 2013/10/18 20:47:07 christos Exp $"); 41*a967d739SClaudio Martella #endif /* not lint */ 42*a967d739SClaudio Martella 43*a967d739SClaudio Martella #include <sys/types.h> 44*a967d739SClaudio Martella #include <sys/stat.h> 45*a967d739SClaudio Martella #include <sys/time.h> 46*a967d739SClaudio Martella #include <sys/mman.h> 47*a967d739SClaudio Martella #include <sys/event.h> 48*a967d739SClaudio Martella 49*a967d739SClaudio Martella #include <limits.h> 50*a967d739SClaudio Martella #include <fcntl.h> 51*a967d739SClaudio Martella #include <errno.h> 52*a967d739SClaudio Martella #include <unistd.h> 53*a967d739SClaudio Martella #include <stdio.h> 54*a967d739SClaudio Martella #include <stdlib.h> 55*a967d739SClaudio Martella #include <string.h> 56*a967d739SClaudio Martella #include "extern.h" 57*a967d739SClaudio Martella 58*a967d739SClaudio Martella static int rlines(FILE *, off_t, struct stat *); 59*a967d739SClaudio Martella 60*a967d739SClaudio Martella /* defines for inner loop actions */ 61*a967d739SClaudio Martella #define USE_SLEEP 0 62*a967d739SClaudio Martella #define USE_KQUEUE 1 63*a967d739SClaudio Martella #define ADD_EVENTS 2 64*a967d739SClaudio Martella 65*a967d739SClaudio Martella /* 66*a967d739SClaudio Martella * forward -- display the file, from an offset, forward. 67*a967d739SClaudio Martella * 68*a967d739SClaudio Martella * There are eight separate cases for this -- regular and non-regular 69*a967d739SClaudio Martella * files, by bytes or lines and from the beginning or end of the file. 70*a967d739SClaudio Martella * 71*a967d739SClaudio Martella * FBYTES byte offset from the beginning of the file 72*a967d739SClaudio Martella * REG seek 73*a967d739SClaudio Martella * NOREG read, counting bytes 74*a967d739SClaudio Martella * 75*a967d739SClaudio Martella * FLINES line offset from the beginning of the file 76*a967d739SClaudio Martella * REG read, counting lines 77*a967d739SClaudio Martella * NOREG read, counting lines 78*a967d739SClaudio Martella * 79*a967d739SClaudio Martella * RBYTES byte offset from the end of the file 80*a967d739SClaudio Martella * REG seek 81*a967d739SClaudio Martella * NOREG cyclically read characters into a wrap-around buffer 82*a967d739SClaudio Martella * 83*a967d739SClaudio Martella * RLINES 84*a967d739SClaudio Martella * REG mmap the file and step back until reach the correct offset. 85*a967d739SClaudio Martella * NOREG cyclically read lines into a wrap-around array of buffers 86*a967d739SClaudio Martella */ 87*a967d739SClaudio Martella void 88*a967d739SClaudio Martella forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) 89*a967d739SClaudio Martella { 90*a967d739SClaudio Martella #ifndef __minix 91*a967d739SClaudio Martella int ch, n; 92*a967d739SClaudio Martella #else 93*a967d739SClaudio Martella int ch; 94*a967d739SClaudio Martella #endif 95*a967d739SClaudio Martella int kq=-1, action=USE_SLEEP; 96*a967d739SClaudio Martella struct stat statbuf; 97*a967d739SClaudio Martella #ifndef __minix 98*a967d739SClaudio Martella struct kevent ev[2]; 99*a967d739SClaudio Martella #endif 100*a967d739SClaudio Martella 101*a967d739SClaudio Martella switch(style) { 102*a967d739SClaudio Martella case FBYTES: 103*a967d739SClaudio Martella if (off == 0) 104*a967d739SClaudio Martella break; 105*a967d739SClaudio Martella if (S_ISREG(sbp->st_mode)) { 106*a967d739SClaudio Martella if (sbp->st_size < off) 107*a967d739SClaudio Martella off = sbp->st_size; 108*a967d739SClaudio Martella if (fseeko(fp, off, SEEK_SET) == -1) { 109*a967d739SClaudio Martella ierr(); 110*a967d739SClaudio Martella return; 111*a967d739SClaudio Martella } 112*a967d739SClaudio Martella } else while (off--) 113*a967d739SClaudio Martella if ((ch = getc(fp)) == EOF) { 114*a967d739SClaudio Martella if (ferror(fp)) { 115*a967d739SClaudio Martella ierr(); 116*a967d739SClaudio Martella return; 117*a967d739SClaudio Martella } 118*a967d739SClaudio Martella break; 119*a967d739SClaudio Martella } 120*a967d739SClaudio Martella break; 121*a967d739SClaudio Martella case FLINES: 122*a967d739SClaudio Martella if (off == 0) 123*a967d739SClaudio Martella break; 124*a967d739SClaudio Martella for (;;) { 125*a967d739SClaudio Martella if ((ch = getc(fp)) == EOF) { 126*a967d739SClaudio Martella if (ferror(fp)) { 127*a967d739SClaudio Martella ierr(); 128*a967d739SClaudio Martella return; 129*a967d739SClaudio Martella } 130*a967d739SClaudio Martella break; 131*a967d739SClaudio Martella } 132*a967d739SClaudio Martella if (ch == '\n' && !--off) 133*a967d739SClaudio Martella break; 134*a967d739SClaudio Martella } 135*a967d739SClaudio Martella break; 136*a967d739SClaudio Martella case RBYTES: 137*a967d739SClaudio Martella if (S_ISREG(sbp->st_mode)) { 138*a967d739SClaudio Martella if (sbp->st_size >= off && 139*a967d739SClaudio Martella fseeko(fp, -off, SEEK_END) == -1) { 140*a967d739SClaudio Martella ierr(); 141*a967d739SClaudio Martella return; 142*a967d739SClaudio Martella } 143*a967d739SClaudio Martella } else if (off == 0) { 144*a967d739SClaudio Martella while (getc(fp) != EOF); 145*a967d739SClaudio Martella if (ferror(fp)) { 146*a967d739SClaudio Martella ierr(); 147*a967d739SClaudio Martella return; 148*a967d739SClaudio Martella } 149*a967d739SClaudio Martella } else { 150*a967d739SClaudio Martella if (displaybytes(fp, off)) 151*a967d739SClaudio Martella return; 152*a967d739SClaudio Martella } 153*a967d739SClaudio Martella break; 154*a967d739SClaudio Martella case RLINES: 155*a967d739SClaudio Martella if (S_ISREG(sbp->st_mode)) { 156*a967d739SClaudio Martella if (!off) { 157*a967d739SClaudio Martella if (fseek(fp, 0L, SEEK_END) == -1) { 158*a967d739SClaudio Martella ierr(); 159*a967d739SClaudio Martella return; 160*a967d739SClaudio Martella } 161*a967d739SClaudio Martella } else { 162*a967d739SClaudio Martella if (rlines(fp, off, sbp)) 163*a967d739SClaudio Martella return; 164*a967d739SClaudio Martella } 165*a967d739SClaudio Martella } else if (off == 0) { 166*a967d739SClaudio Martella while (getc(fp) != EOF); 167*a967d739SClaudio Martella if (ferror(fp)) { 168*a967d739SClaudio Martella ierr(); 169*a967d739SClaudio Martella return; 170*a967d739SClaudio Martella } 171*a967d739SClaudio Martella } else { 172*a967d739SClaudio Martella if (displaylines(fp, off)) 173*a967d739SClaudio Martella return; 174*a967d739SClaudio Martella } 175*a967d739SClaudio Martella break; 176*a967d739SClaudio Martella default: 177*a967d739SClaudio Martella break; 178*a967d739SClaudio Martella } 179*a967d739SClaudio Martella 180*a967d739SClaudio Martella if (fflag) { 181*a967d739SClaudio Martella #ifndef __minix 182*a967d739SClaudio Martella kq = kqueue(); 183*a967d739SClaudio Martella if (kq < 0) 184*a967d739SClaudio Martella xerr(1, "kqueue"); 185*a967d739SClaudio Martella action = ADD_EVENTS; 186*a967d739SClaudio Martella #else 187*a967d739SClaudio Martella action = USE_SLEEP; 188*a967d739SClaudio Martella #endif 189*a967d739SClaudio Martella } 190*a967d739SClaudio Martella 191*a967d739SClaudio Martella for (;;) { 192*a967d739SClaudio Martella while ((ch = getc(fp)) != EOF) { 193*a967d739SClaudio Martella if (putchar(ch) == EOF) 194*a967d739SClaudio Martella oerr(); 195*a967d739SClaudio Martella } 196*a967d739SClaudio Martella if (ferror(fp)) { 197*a967d739SClaudio Martella ierr(); 198*a967d739SClaudio Martella return; 199*a967d739SClaudio Martella } 200*a967d739SClaudio Martella (void)fflush(stdout); 201*a967d739SClaudio Martella if (!fflag) 202*a967d739SClaudio Martella break; 203*a967d739SClaudio Martella 204*a967d739SClaudio Martella clearerr(fp); 205*a967d739SClaudio Martella 206*a967d739SClaudio Martella switch (action) { 207*a967d739SClaudio Martella #ifndef __minix 208*a967d739SClaudio Martella case ADD_EVENTS: 209*a967d739SClaudio Martella n = 0; 210*a967d739SClaudio Martella 211*a967d739SClaudio Martella memset(ev, 0, sizeof(ev)); 212*a967d739SClaudio Martella if (fflag == 2 && fileno(fp) != STDIN_FILENO) { 213*a967d739SClaudio Martella EV_SET(&ev[n], fileno(fp), EVFILT_VNODE, 214*a967d739SClaudio Martella EV_ADD | EV_ENABLE | EV_CLEAR, 215*a967d739SClaudio Martella NOTE_DELETE | NOTE_RENAME, 0, 0); 216*a967d739SClaudio Martella n++; 217*a967d739SClaudio Martella } 218*a967d739SClaudio Martella EV_SET(&ev[n], fileno(fp), EVFILT_READ, 219*a967d739SClaudio Martella EV_ADD | EV_ENABLE, 0, 0, 0); 220*a967d739SClaudio Martella n++; 221*a967d739SClaudio Martella 222*a967d739SClaudio Martella if (kevent(kq, ev, n, NULL, 0, NULL) == -1) { 223*a967d739SClaudio Martella close(kq); 224*a967d739SClaudio Martella kq = -1; 225*a967d739SClaudio Martella action = USE_SLEEP; 226*a967d739SClaudio Martella } else { 227*a967d739SClaudio Martella action = USE_KQUEUE; 228*a967d739SClaudio Martella } 229*a967d739SClaudio Martella break; 230*a967d739SClaudio Martella 231*a967d739SClaudio Martella case USE_KQUEUE: 232*a967d739SClaudio Martella if (kevent(kq, NULL, 0, ev, 1, NULL) == -1) 233*a967d739SClaudio Martella xerr(1, "kevent"); 234*a967d739SClaudio Martella 235*a967d739SClaudio Martella if (ev[0].filter == EVFILT_VNODE) { 236*a967d739SClaudio Martella /* file was rotated, wait until it reappears */ 237*a967d739SClaudio Martella action = USE_SLEEP; 238*a967d739SClaudio Martella } else if (ev[0].data < 0) { 239*a967d739SClaudio Martella /* file shrank, reposition to end */ 240*a967d739SClaudio Martella if (fseek(fp, 0L, SEEK_END) == -1) { 241*a967d739SClaudio Martella ierr(); 242*a967d739SClaudio Martella return; 243*a967d739SClaudio Martella } 244*a967d739SClaudio Martella } 245*a967d739SClaudio Martella break; 246*a967d739SClaudio Martella #endif 247*a967d739SClaudio Martella 248*a967d739SClaudio Martella case USE_SLEEP: 249*a967d739SClaudio Martella /* 250*a967d739SClaudio Martella * We pause for one second after displaying any data 251*a967d739SClaudio Martella * that has accumulated since we read the file. 252*a967d739SClaudio Martella */ 253*a967d739SClaudio Martella (void) sleep(1); 254*a967d739SClaudio Martella 255*a967d739SClaudio Martella if (fflag == 2 && fileno(fp) != STDIN_FILENO && 256*a967d739SClaudio Martella stat(fname, &statbuf) != -1) { 257*a967d739SClaudio Martella if (statbuf.st_ino != sbp->st_ino || 258*a967d739SClaudio Martella statbuf.st_dev != sbp->st_dev || 259*a967d739SClaudio Martella statbuf.st_rdev != sbp->st_rdev || 260*a967d739SClaudio Martella statbuf.st_nlink == 0) { 261*a967d739SClaudio Martella fp = freopen(fname, "r", fp); 262*a967d739SClaudio Martella if (fp == NULL) { 263*a967d739SClaudio Martella ierr(); 264*a967d739SClaudio Martella goto out; 265*a967d739SClaudio Martella } 266*a967d739SClaudio Martella *sbp = statbuf; 267*a967d739SClaudio Martella if (kq != -1) 268*a967d739SClaudio Martella action = ADD_EVENTS; 269*a967d739SClaudio Martella } else if (kq != -1) 270*a967d739SClaudio Martella action = USE_KQUEUE; 271*a967d739SClaudio Martella } 272*a967d739SClaudio Martella break; 273*a967d739SClaudio Martella } 274*a967d739SClaudio Martella } 275*a967d739SClaudio Martella out: 276*a967d739SClaudio Martella if (fflag && kq != -1) 277*a967d739SClaudio Martella close(kq); 278*a967d739SClaudio Martella } 279*a967d739SClaudio Martella 280*a967d739SClaudio Martella /* 281*a967d739SClaudio Martella * rlines -- display the last offset lines of the file. 282*a967d739SClaudio Martella * 283*a967d739SClaudio Martella * Non-zero return means than a (non-fatal) error occurred. 284*a967d739SClaudio Martella */ 285*a967d739SClaudio Martella static int 286*a967d739SClaudio Martella rlines(FILE *fp, off_t off, struct stat *sbp) 287*a967d739SClaudio Martella { 288*a967d739SClaudio Martella off_t file_size; 289*a967d739SClaudio Martella off_t file_remaining; 290*a967d739SClaudio Martella char *p = NULL; 291*a967d739SClaudio Martella char *start = NULL; 292*a967d739SClaudio Martella off_t mmap_size; 293*a967d739SClaudio Martella off_t mmap_offset; 294*a967d739SClaudio Martella off_t mmap_remaining = 0; 295*a967d739SClaudio Martella 296*a967d739SClaudio Martella #define MMAP_MAXSIZE (10 * 1024 * 1024) 297*a967d739SClaudio Martella 298*a967d739SClaudio Martella if (!(file_size = sbp->st_size)) 299*a967d739SClaudio Martella return 0; 300*a967d739SClaudio Martella file_remaining = file_size; 301*a967d739SClaudio Martella 302*a967d739SClaudio Martella if (file_remaining > MMAP_MAXSIZE) { 303*a967d739SClaudio Martella mmap_size = MMAP_MAXSIZE; 304*a967d739SClaudio Martella mmap_offset = file_remaining - MMAP_MAXSIZE; 305*a967d739SClaudio Martella } else { 306*a967d739SClaudio Martella mmap_size = file_remaining; 307*a967d739SClaudio Martella mmap_offset = 0; 308*a967d739SClaudio Martella } 309*a967d739SClaudio Martella 310*a967d739SClaudio Martella while (off) { 311*a967d739SClaudio Martella start = mmap(NULL, (size_t)mmap_size, PROT_READ, 312*a967d739SClaudio Martella MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset); 313*a967d739SClaudio Martella if (start == MAP_FAILED) { 314*a967d739SClaudio Martella xerr(0, "%s", fname); 315*a967d739SClaudio Martella return 1; 316*a967d739SClaudio Martella } 317*a967d739SClaudio Martella 318*a967d739SClaudio Martella mmap_remaining = mmap_size; 319*a967d739SClaudio Martella /* Last char is special, ignore whether newline or not. */ 320*a967d739SClaudio Martella for (p = start + mmap_remaining - 1 ; --mmap_remaining ; ) 321*a967d739SClaudio Martella if (*--p == '\n' && !--off) { 322*a967d739SClaudio Martella ++p; 323*a967d739SClaudio Martella break; 324*a967d739SClaudio Martella } 325*a967d739SClaudio Martella 326*a967d739SClaudio Martella file_remaining -= mmap_size - mmap_remaining; 327*a967d739SClaudio Martella 328*a967d739SClaudio Martella if (off == 0) 329*a967d739SClaudio Martella break; 330*a967d739SClaudio Martella 331*a967d739SClaudio Martella if (file_remaining == 0) 332*a967d739SClaudio Martella break; 333*a967d739SClaudio Martella 334*a967d739SClaudio Martella if (munmap(start, mmap_size)) { 335*a967d739SClaudio Martella xerr(0, "%s", fname); 336*a967d739SClaudio Martella return 1; 337*a967d739SClaudio Martella } 338*a967d739SClaudio Martella 339*a967d739SClaudio Martella if (mmap_offset >= MMAP_MAXSIZE) { 340*a967d739SClaudio Martella mmap_offset -= MMAP_MAXSIZE; 341*a967d739SClaudio Martella } else { 342*a967d739SClaudio Martella mmap_offset = 0; 343*a967d739SClaudio Martella mmap_size = file_remaining; 344*a967d739SClaudio Martella } 345*a967d739SClaudio Martella } 346*a967d739SClaudio Martella 347*a967d739SClaudio Martella /* 348*a967d739SClaudio Martella * Output the (perhaps partial) data in this mmap'd block. 349*a967d739SClaudio Martella */ 350*a967d739SClaudio Martella WR(p, mmap_size - mmap_remaining); 351*a967d739SClaudio Martella file_remaining += mmap_size - mmap_remaining; 352*a967d739SClaudio Martella if (munmap(start, mmap_size)) { 353*a967d739SClaudio Martella xerr(0, "%s", fname); 354*a967d739SClaudio Martella return 1; 355*a967d739SClaudio Martella } 356*a967d739SClaudio Martella 357*a967d739SClaudio Martella /* 358*a967d739SClaudio Martella * Set the file pointer to reflect the length displayed. 359*a967d739SClaudio Martella * This will cause the caller to redisplay the data if/when 360*a967d739SClaudio Martella * needed. 361*a967d739SClaudio Martella */ 362*a967d739SClaudio Martella if (fseeko(fp, file_remaining, SEEK_SET) == -1) { 363*a967d739SClaudio Martella ierr(); 364*a967d739SClaudio Martella return 1; 365*a967d739SClaudio Martella } 366*a967d739SClaudio Martella return 0; 367*a967d739SClaudio Martella } 368