1 /* $OpenBSD: forward.c,v 1.11 2000/11/21 22:01:47 art 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. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93"; 43 #endif 44 static char rcsid[] = "$OpenBSD: forward.c,v 1.11 2000/11/21 22:01:47 art Exp $"; 45 #endif /* not lint */ 46 47 #include <sys/types.h> 48 #include <sys/stat.h> 49 #include <sys/mman.h> 50 51 #include <err.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <limits.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #include "extern.h" 61 62 static int rlines __P((FILE *, long, struct stat *)); 63 64 /* 65 * forward -- display the file, from an offset, forward. 66 * 67 * There are eight separate cases for this -- regular and non-regular 68 * files, by bytes or lines and from the beginning or end of the file. 69 * 70 * FBYTES byte offset from the beginning of the file 71 * REG seek 72 * NOREG read, counting bytes 73 * 74 * FLINES line offset from the beginning of the file 75 * REG read, counting lines 76 * NOREG read, counting lines 77 * 78 * RBYTES byte offset from the end of the file 79 * REG seek 80 * NOREG cyclically read characters into a wrap-around buffer 81 * 82 * RLINES 83 * REG mmap the file and step back until reach the correct offset. 84 * NOREG cyclically read lines into a wrap-around array of buffers 85 */ 86 void 87 forward(fp, style, off, sbp) 88 FILE *fp; 89 enum STYLE style; 90 long off; 91 struct stat *sbp; 92 { 93 register int ch; 94 struct stat nsb; 95 96 switch(style) { 97 case FBYTES: 98 if (off == 0) 99 break; 100 if (S_ISREG(sbp->st_mode)) { 101 if (sbp->st_size < off) 102 off = sbp->st_size; 103 if (fseek(fp, off, SEEK_SET) == -1) { 104 ierr(); 105 return; 106 } 107 } else while (off--) 108 if ((ch = getc(fp)) == EOF) { 109 if (ferror(fp)) { 110 ierr(); 111 return; 112 } 113 break; 114 } 115 break; 116 case FLINES: 117 if (off == 0) 118 break; 119 for (;;) { 120 if ((ch = getc(fp)) == EOF) { 121 if (ferror(fp)) { 122 ierr(); 123 return; 124 } 125 break; 126 } 127 if (ch == '\n' && !--off) 128 break; 129 } 130 break; 131 case RBYTES: 132 if (S_ISREG(sbp->st_mode)) { 133 if (sbp->st_size >= off && 134 fseek(fp, -off, SEEK_END) == -1) { 135 ierr(); 136 return; 137 } 138 } else if (off == 0) { 139 while (getc(fp) != EOF) 140 ; 141 if (ferror(fp)) { 142 ierr(); 143 return; 144 } 145 } else { 146 if (bytes(fp, off)) 147 return; 148 } 149 break; 150 case RLINES: 151 if (S_ISREG(sbp->st_mode)) { 152 if (!off) { 153 if (fseek(fp, 0L, SEEK_END) == -1) { 154 ierr(); 155 return; 156 } 157 } else if (rlines(fp, off, sbp) != 0) 158 lines(fp, off); 159 } else if (off == 0) { 160 while (getc(fp) != EOF) 161 ; 162 if (ferror(fp)) { 163 ierr(); 164 return; 165 } 166 } else { 167 if (lines(fp, off)) 168 return; 169 } 170 break; 171 } 172 173 for (;;) { 174 while (!feof(fp) && (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 sleep(1); 185 clearerr(fp); 186 187 if (is_stdin || stat(fname, &nsb) != 0) 188 continue; 189 /* Reopen file if the inode changes or file was truncated */ 190 if (nsb.st_ino != sbp->st_ino) { 191 warnx("%s has been replaced, reopening.", fname); 192 if ((fp = freopen(fname, "r", fp)) == NULL) { 193 ierr(); 194 return; 195 } 196 (void)memcpy(sbp, &nsb, sizeof(nsb)); 197 } else if (nsb.st_size < sbp->st_size) { 198 warnx("%s has been truncated, resetting.", fname); 199 rewind(fp); 200 (void)memcpy(sbp, &nsb, sizeof(nsb)); 201 } 202 } 203 } 204 205 /* 206 * rlines -- display the last offset lines of the file. 207 */ 208 static int 209 rlines(fp, off, sbp) 210 FILE *fp; 211 long off; 212 struct stat *sbp; 213 { 214 register off_t size; 215 register char *p; 216 char *start; 217 218 if (!(size = sbp->st_size)) 219 return (0); 220 221 if (size > SIZE_T_MAX) 222 return (1); 223 224 if ((start = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE, 225 fileno(fp), (off_t)0)) == MAP_FAILED) 226 return (1); 227 228 /* Last char is special, ignore whether newline or not. */ 229 for (p = start + size - 1; --size;) 230 if (*--p == '\n' && !--off) { 231 ++p; 232 break; 233 } 234 235 /* Set the file pointer to reflect the length displayed. */ 236 size = sbp->st_size - size; 237 WR(p, size); 238 if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) { 239 ierr(); 240 return (1); 241 } 242 if (munmap(start, (size_t)sbp->st_size)) { 243 ierr(); 244 return (1); 245 } 246 247 return (0); 248 } 249