1 /* $OpenBSD: forward.c,v 1.13 2001/11/19 19:02:16 mpech Exp $ */ 2 /* $NetBSD: forward.c,v 1.7 1996/02/13 16:49:10 ghudson Exp $ */ 3 4 /*- 5 * Copyright (c) 1991, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Edward Sze-Tyan Wang. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 43 #endif 44 static char rcsid[] = "$OpenBSD: forward.c,v 1.13 2001/11/19 19:02:16 mpech Exp $"; 45 #endif /* not lint */ 46 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 #include <sys/mman.h> 50 #include <sys/event.h> 51 52 #include <err.h> 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <limits.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 61 #include "extern.h" 62 63 static int rlines __P((FILE *, long, struct stat *)); 64 65 /* 66 * forward -- display the file, from an offset, forward. 67 * 68 * There are eight separate cases for this -- regular and non-regular 69 * files, by bytes or lines and from the beginning or end of the file. 70 * 71 * FBYTES byte offset from the beginning of the file 72 * REG seek 73 * NOREG read, counting bytes 74 * 75 * FLINES line offset from the beginning of the file 76 * REG read, counting lines 77 * NOREG read, counting lines 78 * 79 * RBYTES byte offset from the end of the file 80 * REG seek 81 * NOREG cyclically read characters into a wrap-around buffer 82 * 83 * RLINES 84 * REG mmap the file and step back until reach the correct offset. 85 * NOREG cyclically read lines into a wrap-around array of buffers 86 */ 87 void 88 forward(fp, style, off, sbp) 89 FILE *fp; 90 enum STYLE style; 91 long off; 92 struct stat *sbp; 93 { 94 int ch; 95 struct stat nsb; 96 int kq; 97 struct kevent ke; 98 99 switch(style) { 100 case FBYTES: 101 if (off == 0) 102 break; 103 if (S_ISREG(sbp->st_mode)) { 104 if (sbp->st_size < off) 105 off = sbp->st_size; 106 if (fseek(fp, off, SEEK_SET) == -1) { 107 ierr(); 108 return; 109 } 110 } else while (off--) 111 if ((ch = getc(fp)) == EOF) { 112 if (ferror(fp)) { 113 ierr(); 114 return; 115 } 116 break; 117 } 118 break; 119 case FLINES: 120 if (off == 0) 121 break; 122 for (;;) { 123 if ((ch = getc(fp)) == EOF) { 124 if (ferror(fp)) { 125 ierr(); 126 return; 127 } 128 break; 129 } 130 if (ch == '\n' && !--off) 131 break; 132 } 133 break; 134 case RBYTES: 135 if (S_ISREG(sbp->st_mode)) { 136 if (sbp->st_size >= off && 137 fseek(fp, -off, SEEK_END) == -1) { 138 ierr(); 139 return; 140 } 141 } else if (off == 0) { 142 while (getc(fp) != EOF) 143 ; 144 if (ferror(fp)) { 145 ierr(); 146 return; 147 } 148 } else { 149 if (bytes(fp, off)) 150 return; 151 } 152 break; 153 case RLINES: 154 if (S_ISREG(sbp->st_mode)) { 155 if (!off) { 156 if (fseek(fp, 0L, SEEK_END) == -1) { 157 ierr(); 158 return; 159 } 160 } else if (rlines(fp, off, sbp) != 0) 161 lines(fp, off); 162 } else if (off == 0) { 163 while (getc(fp) != EOF) 164 ; 165 if (ferror(fp)) { 166 ierr(); 167 return; 168 } 169 } else { 170 if (lines(fp, off)) 171 return; 172 } 173 break; 174 } 175 176 kq = -1; 177 kq_retry: 178 if (fflag && ((kq = kqueue()) >= 0)) { 179 ke.ident = fileno(fp); 180 ke.flags = EV_ENABLE|EV_ADD|EV_CLEAR; 181 ke.filter = EVFILT_READ; 182 ke.fflags = ke.data = 0; 183 ke.udata = NULL; 184 if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { 185 close(kq); 186 kq = -1; 187 } else if (S_ISREG(sbp->st_mode)) { 188 ke.ident = fileno(fp); 189 ke.flags = EV_ENABLE|EV_ADD|EV_CLEAR; 190 ke.filter = EVFILT_VNODE; 191 ke.fflags = NOTE_DELETE | NOTE_RENAME; 192 if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { 193 close(kq); 194 kq = -1; 195 } 196 } 197 } 198 199 for (;;) { 200 while (!feof(fp) && (ch = getc(fp)) != EOF) 201 if (putchar(ch) == EOF) 202 oerr(); 203 if (ferror(fp)) { 204 ierr(); 205 if (kq != -1) 206 close(kq); 207 return; 208 } 209 (void)fflush(stdout); 210 if (!fflag) 211 break; 212 clearerr(fp); 213 if (kq < 0 || kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) { 214 sleep(1); 215 } else if (ke.filter == EVFILT_READ) { 216 continue; 217 } else { 218 /* 219 * File was renamed or deleted. 220 * 221 * Continue to look at it until a new file reappears 222 * with the same name. 223 * Fall back to the old algorithm for that. 224 */ 225 close(kq); 226 kq = -1; 227 } 228 229 if (is_stdin || stat(fname, &nsb) != 0) 230 continue; 231 /* Reopen file if the inode changes or file was truncated */ 232 if (nsb.st_ino != sbp->st_ino) { 233 warnx("%s has been replaced, reopening.", fname); 234 if ((fp = freopen(fname, "r", fp)) == NULL) { 235 ierr(); 236 if (kq >= 0) 237 close(kq); 238 return; 239 } 240 (void)memcpy(sbp, &nsb, sizeof(nsb)); 241 goto kq_retry; 242 } else if (nsb.st_size < sbp->st_size) { 243 warnx("%s has been truncated, resetting.", fname); 244 rewind(fp); 245 (void)memcpy(sbp, &nsb, sizeof(nsb)); 246 } 247 } 248 if (kq >= 0) 249 close(kq); 250 } 251 252 /* 253 * rlines -- display the last offset lines of the file. 254 */ 255 static int 256 rlines(fp, off, sbp) 257 FILE *fp; 258 long off; 259 struct stat *sbp; 260 { 261 off_t size; 262 char *p; 263 char *start; 264 265 if (!(size = sbp->st_size)) 266 return (0); 267 268 if (size > SIZE_T_MAX) 269 return (1); 270 271 if ((start = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE, 272 fileno(fp), (off_t)0)) == MAP_FAILED) 273 return (1); 274 275 /* Last char is special, ignore whether newline or not. */ 276 for (p = start + size - 1; --size;) 277 if (*--p == '\n' && !--off) { 278 ++p; 279 break; 280 } 281 282 /* Set the file pointer to reflect the length displayed. */ 283 size = sbp->st_size - size; 284 WR(p, size); 285 if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) { 286 ierr(); 287 return (1); 288 } 289 if (munmap(start, (size_t)sbp->st_size)) { 290 ierr(); 291 return (1); 292 } 293 294 return (0); 295 } 296