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