forward.c revision 1.19
1/* $OpenBSD: forward.c,v 1.19 2004/03/01 16:35:05 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 38static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 39#endif 40static char rcsid[] = "$OpenBSD: forward.c,v 1.19 2004/03/01 16:35:05 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 59static int rlines(FILE *, off_t, 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 */ 83void 84forward(fp, style, off, sbp) 85 FILE *fp; 86 enum STYLE style; 87 off_t 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 (fseeko(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 fseeko(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 (fseeko(fp, (off_t)0, 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; 173kq_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 | NOTE_TRUNCATE; 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 if ((ke.fflags & NOTE_TRUNCATE) == 0) { 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 (ke.fflags & NOTE_TRUNCATE) { 239 warnx("%s has been truncated, resetting.", fname); 240 fpurge(fp); 241 rewind(fp); 242 } 243 (void)memcpy(sbp, &nsb, sizeof(nsb)); 244 } 245 if (kq >= 0) 246 close(kq); 247} 248 249/* 250 * rlines -- display the last offset lines of the file. 251 */ 252static int 253rlines(FILE *fp, off_t off, struct stat *sbp) 254{ 255 off_t pos; 256 int ch; 257 258 pos = sbp->st_size; 259 if (pos == 0) 260 return (0); 261 262 /* 263 * Position before char. 264 * Last char is special, ignore it whether newline or not. 265 */ 266 pos -= 2; 267 ch = EOF; 268 for (; off > 0 && pos >= 0; pos--) { 269 /* A seek per char isn't a problem with a smart stdio */ 270 if (fseeko(fp, pos, SEEK_SET) == -1) { 271 ierr(); 272 return (1); 273 } 274 if ((ch = getc(fp)) == '\n') 275 off--; 276 else if (ch == EOF) { 277 if (ferror(fp)) { 278 ierr(); 279 return (1); 280 } 281 break; 282 } 283 } 284 /* If we read until start of file, put back last read char */ 285 if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, fp) == EOF) { 286 ierr(); 287 return (1); 288 } 289 290 while (!feof(fp) && (ch = getc(fp)) != EOF) 291 if (putchar(ch) == EOF) 292 oerr(); 293 if (ferror(fp)) { 294 ierr(); 295 return (1); 296 } 297 298 return (0); 299} 300