1 /* $OpenBSD: forward.c,v 1.25 2008/11/13 18:33:03 landry 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.25 2008/11/13 18:33:03 landry Exp $"; 41 #endif /* not lint */ 42 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/event.h> 46 47 #include <err.h> 48 #include <stdio.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #include "extern.h" 53 54 static int rlines(FILE *, off_t, struct stat *); 55 56 /* 57 * forward -- display the file, from an offset, forward. 58 * 59 * There are eight separate cases for this -- regular and non-regular 60 * files, by bytes or lines and from the beginning or end of the file. 61 * 62 * FBYTES byte offset from the beginning of the file 63 * REG seek 64 * NOREG read, counting bytes 65 * 66 * FLINES line offset from the beginning of the file 67 * REG read, counting lines 68 * NOREG read, counting lines 69 * 70 * RBYTES byte offset from the end of the file 71 * REG seek 72 * NOREG cyclically read characters into a wrap-around buffer 73 * 74 * RLINES 75 * REG step back until the correct offset is reached. 76 * NOREG cyclically read lines into a wrap-around array of buffers 77 */ 78 void 79 forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp) 80 { 81 int ch; 82 struct stat nsb; 83 int kq, queue; 84 struct kevent ke; 85 86 switch(style) { 87 case FBYTES: 88 if (off == 0) 89 break; 90 if (S_ISREG(sbp->st_mode)) { 91 if (sbp->st_size < off) 92 off = sbp->st_size; 93 if (fseeko(fp, off, SEEK_SET) == -1) { 94 ierr(); 95 return; 96 } 97 } else while (off--) 98 if ((ch = getc(fp)) == EOF) { 99 if (ferror(fp)) { 100 ierr(); 101 return; 102 } 103 break; 104 } 105 break; 106 case FLINES: 107 if (off == 0) 108 break; 109 for (;;) { 110 if ((ch = getc(fp)) == EOF) { 111 if (ferror(fp)) { 112 ierr(); 113 return; 114 } 115 break; 116 } 117 if (ch == '\n' && !--off) 118 break; 119 } 120 break; 121 case RBYTES: 122 if (S_ISREG(sbp->st_mode)) { 123 if (sbp->st_size >= off && 124 fseeko(fp, -off, SEEK_END) == -1) { 125 ierr(); 126 return; 127 } 128 } else if (off == 0) { 129 while (getc(fp) != EOF) 130 ; 131 if (ferror(fp)) { 132 ierr(); 133 return; 134 } 135 } else { 136 if (bytes(fp, off)) 137 return; 138 } 139 break; 140 case RLINES: 141 if (S_ISREG(sbp->st_mode)) { 142 if (!off) { 143 if (fseeko(fp, (off_t)0, SEEK_END) == -1) { 144 ierr(); 145 return; 146 } 147 } else if (rlines(fp, off, sbp) != 0) 148 lines(fp, off); 149 } else if (off == 0) { 150 while (getc(fp) != EOF) 151 ; 152 if (ferror(fp)) { 153 ierr(); 154 return; 155 } 156 } else { 157 if (lines(fp, off)) 158 return; 159 } 160 break; 161 } 162 163 kq = -1; 164 kq_retry: 165 if (fflag && ((kq = kqueue()) >= 0)) { 166 EV_SET(&ke, fileno(fp), EVFILT_READ, 167 EV_ENABLE | EV_ADD | EV_CLEAR, 168 0, 169 0, NULL); 170 if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { 171 close(kq); 172 kq = -1; 173 } else if (S_ISREG(sbp->st_mode)) { 174 EV_SET(&ke, fileno(fp), EVFILT_VNODE, 175 EV_ENABLE | EV_ADD | EV_CLEAR, 176 NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE, 177 0, NULL); 178 if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) { 179 close(kq); 180 kq = -1; 181 } 182 } 183 } 184 185 for (;;) { 186 while (!feof(fp) && (ch = getc(fp)) != EOF) 187 if (putchar(ch) == EOF) 188 oerr(); 189 if (ferror(fp)) { 190 ierr(); 191 if (kq != -1) 192 close(kq); 193 return; 194 } 195 (void)fflush(stdout); 196 if (!fflag) 197 break; 198 clearerr(fp); 199 queue = 1; 200 if (kq < 0 || kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) { 201 queue = 0; 202 sleep(1); 203 } else if (ke.filter == EVFILT_READ) { 204 continue; 205 } else if ((ke.fflags & NOTE_TRUNCATE) == 0) { 206 /* 207 * File was renamed or deleted. 208 * 209 * Continue to look at it until a new file reappears 210 * with the same name. 211 * Fall back to the old algorithm for that. 212 */ 213 close(kq); 214 kq = -1; 215 } 216 217 if (is_stdin || stat(fname, &nsb) != 0) 218 continue; 219 /* Reopen file if the inode changes or file was truncated */ 220 if (nsb.st_ino != sbp->st_ino) { 221 warnx("%s has been replaced, reopening.", fname); 222 if ((fp = freopen(fname, "r", fp)) == NULL) { 223 ierr(); 224 if (kq >= 0) 225 close(kq); 226 return; 227 } 228 (void)memcpy(sbp, &nsb, sizeof(nsb)); 229 goto kq_retry; 230 } else if ((queue && (ke.fflags & NOTE_TRUNCATE)) || 231 (!queue && nsb.st_size < sbp->st_size)) { 232 warnx("%s has been truncated, resetting.", fname); 233 fpurge(fp); 234 rewind(fp); 235 } 236 (void)memcpy(sbp, &nsb, sizeof(nsb)); 237 } 238 if (kq >= 0) 239 close(kq); 240 } 241 242 /* 243 * rlines -- display the last offset lines of the file. 244 */ 245 static int 246 rlines(FILE *fp, off_t off, struct stat *sbp) 247 { 248 off_t pos; 249 int ch; 250 251 pos = sbp->st_size; 252 if (pos == 0) 253 return (0); 254 255 /* 256 * Position before char. 257 * Last char is special, ignore it whether newline or not. 258 */ 259 pos -= 2; 260 ch = EOF; 261 for (; off > 0 && pos >= 0; pos--) { 262 /* A seek per char isn't a problem with a smart stdio */ 263 if (fseeko(fp, pos, SEEK_SET) == -1) { 264 ierr(); 265 return (1); 266 } 267 if ((ch = getc(fp)) == '\n') 268 off--; 269 else if (ch == EOF) { 270 if (ferror(fp)) { 271 ierr(); 272 return (1); 273 } 274 break; 275 } 276 } 277 /* If we read until start of file, put back last read char */ 278 if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, fp) == EOF) { 279 ierr(); 280 return (1); 281 } 282 283 while (!feof(fp) && (ch = getc(fp)) != EOF) 284 if (putchar(ch) == EOF) 285 oerr(); 286 if (ferror(fp)) { 287 ierr(); 288 return (1); 289 } 290 291 return (0); 292 } 293