1 /*- 2 * Copyright (c) 1991, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Edward Sze-Tyan Wang. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $FreeBSD$ 37 */ 38 39 #ifndef lint 40 static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 41 #endif /* not lint */ 42 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/time.h> 46 #include <sys/mman.h> 47 #include <sys/event.h> 48 49 #include <limits.h> 50 #include <fcntl.h> 51 #include <errno.h> 52 #include <unistd.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <err.h> 57 #include "extern.h" 58 59 static void rlines __P((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 mmap the file and step back until reach the correct offset. 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, add_events = 0, kq = -1; 91 struct kevent ev[2]; 92 93 switch(style) { 94 case FBYTES: 95 if (off == 0) 96 break; 97 if (S_ISREG(sbp->st_mode)) { 98 if (sbp->st_size < off) 99 off = sbp->st_size; 100 if (fseek(fp, off, SEEK_SET) == -1) { 101 ierr(); 102 return; 103 } 104 } else while (off--) 105 if ((ch = getc(fp)) == EOF) { 106 if (ferror(fp)) { 107 ierr(); 108 return; 109 } 110 break; 111 } 112 break; 113 case FLINES: 114 if (off == 0) 115 break; 116 for (;;) { 117 if ((ch = getc(fp)) == EOF) { 118 if (ferror(fp)) { 119 ierr(); 120 return; 121 } 122 break; 123 } 124 if (ch == '\n' && !--off) 125 break; 126 } 127 break; 128 case RBYTES: 129 if (S_ISREG(sbp->st_mode)) { 130 if (sbp->st_size >= off && 131 fseek(fp, -off, SEEK_END) == -1) { 132 ierr(); 133 return; 134 } 135 } else if (off == 0) { 136 while (getc(fp) != EOF); 137 if (ferror(fp)) { 138 ierr(); 139 return; 140 } 141 } else 142 if (bytes(fp, off)) 143 return; 144 break; 145 case RLINES: 146 if (S_ISREG(sbp->st_mode)) 147 if (!off) { 148 if (fseek(fp, 0L, SEEK_END) == -1) { 149 ierr(); 150 return; 151 } 152 } else 153 rlines(fp, off, sbp); 154 else if (off == 0) { 155 while (getc(fp) != EOF); 156 if (ferror(fp)) { 157 ierr(); 158 return; 159 } 160 } else 161 if (lines(fp, off)) 162 return; 163 break; 164 } 165 166 if (fflag) { 167 kq = kqueue(); 168 if (kq < 0) 169 err(1, "kqueue"); 170 add_events = 1; 171 } 172 173 for (;;) { 174 while ((ch = getc(fp)) != EOF) 175 if (putchar(ch) == EOF) 176 oerr(); 177 if (ferror(fp)) { 178 ierr(); 179 return; 180 } 181 (void)fflush(stdout); 182 if (! fflag) 183 break; 184 clearerr(fp); 185 186 if (add_events) { 187 int n = 0; 188 struct kevent *evp[2]; 189 struct timespec ts = { 0, 0 }; 190 191 if (Fflag && fileno(fp) != STDIN_FILENO) { 192 ev[n].ident = fileno(fp); 193 ev[n].filter = EVFILT_VNODE; 194 ev[n].flags = EV_ADD | EV_ENABLE | EV_CLEAR; 195 ev[n].fflags = NOTE_DELETE | NOTE_RENAME; 196 evp[n] = &ev[n]; 197 n++; 198 } 199 ev[n].ident = fileno(fp); 200 ev[n].filter = EVFILT_READ; 201 ev[n].flags = EV_ADD | EV_ENABLE; 202 evp[n] = &ev[n]; 203 n++; 204 205 if (kevent(kq, n, evp, 0, NULL, &ts) < 0) 206 err(1, "kevent"); 207 add_events = 0; 208 } 209 210 if (kevent(kq, 0, NULL, 1, ev, NULL) < 0) 211 err(1, "kevent"); 212 213 if (ev->filter == EVFILT_VNODE) { 214 fp = freopen(fname, "r", fp); 215 if (fp == NULL) { 216 ierr(); 217 break; 218 } 219 add_events = 1; 220 } else if (ev->data < 0) { 221 /* file shrank, reposition to end */ 222 if (fseek(fp, 0L, SEEK_END) == -1) { 223 ierr(); 224 return; 225 } 226 } 227 } 228 } 229 230 /* 231 * rlines -- display the last offset lines of the file. 232 */ 233 static void 234 rlines(fp, off, sbp) 235 FILE *fp; 236 long off; 237 struct stat *sbp; 238 { 239 register off_t size; 240 register char *p; 241 char *start; 242 243 if (!(size = sbp->st_size)) 244 return; 245 246 if (size > SIZE_T_MAX) { 247 errno = EFBIG; 248 ierr(); 249 return; 250 } 251 252 if ((start = mmap(NULL, (size_t)size, 253 PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) { 254 ierr(); 255 return; 256 } 257 258 /* Last char is special, ignore whether newline or not. */ 259 for (p = start + size - 1; --size;) 260 if (*--p == '\n' && !--off) { 261 ++p; 262 break; 263 } 264 265 /* Set the file pointer to reflect the length displayed. */ 266 size = sbp->st_size - size; 267 WR(p, size); 268 if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) { 269 ierr(); 270 return; 271 } 272 if (munmap(start, (size_t)sbp->st_size)) { 273 ierr(); 274 return; 275 } 276 } 277