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