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 * %sccs.include.redist.c% 9 */ 10 11 #ifndef lint 12 static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 06/06/93"; 13 #endif /* not lint */ 14 15 #include <sys/types.h> 16 #include <sys/stat.h> 17 #include <sys/time.h> 18 #include <sys/mman.h> 19 20 #include <limits.h> 21 #include <fcntl.h> 22 #include <errno.h> 23 #include <unistd.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include "extern.h" 28 29 static void rlines __P((FILE *, long, struct stat *)); 30 31 /* 32 * forward -- display the file, from an offset, forward. 33 * 34 * There are eight separate cases for this -- regular and non-regular 35 * files, by bytes or lines and from the beginning or end of the file. 36 * 37 * FBYTES byte offset from the beginning of the file 38 * REG seek 39 * NOREG read, counting bytes 40 * 41 * FLINES line offset from the beginning of the file 42 * REG read, counting lines 43 * NOREG read, counting lines 44 * 45 * RBYTES byte offset from the end of the file 46 * REG seek 47 * NOREG cyclically read characters into a wrap-around buffer 48 * 49 * RLINES 50 * REG mmap the file and step back until reach the correct offset. 51 * NOREG cyclically read lines into a wrap-around array of buffers 52 */ 53 void 54 forward(fp, style, off, sbp) 55 FILE *fp; 56 enum STYLE style; 57 long off; 58 struct stat *sbp; 59 { 60 register int ch; 61 struct timeval second; 62 fd_set zero; 63 64 switch(style) { 65 case FBYTES: 66 if (off == 0) 67 break; 68 if (S_ISREG(sbp->st_mode)) { 69 if (sbp->st_size < off) 70 off = sbp->st_size; 71 if (fseek(fp, off, SEEK_SET) == -1) { 72 ierr(); 73 return; 74 } 75 } else while (off--) 76 if ((ch = getc(fp)) == EOF) { 77 if (ferror(fp)) { 78 ierr(); 79 return; 80 } 81 break; 82 } 83 break; 84 case FLINES: 85 if (off == 0) 86 break; 87 for (;;) { 88 if ((ch = getc(fp)) == EOF) { 89 if (ferror(fp)) { 90 ierr(); 91 return; 92 } 93 break; 94 } 95 if (ch == '\n' && !--off) 96 break; 97 } 98 break; 99 case RBYTES: 100 if (S_ISREG(sbp->st_mode)) { 101 if (sbp->st_size >= off && 102 fseek(fp, -off, SEEK_END) == -1) { 103 ierr(); 104 return; 105 } 106 } else if (off == 0) { 107 while (getc(fp) != EOF); 108 if (ferror(fp)) { 109 ierr(); 110 return; 111 } 112 } else 113 bytes(fp, off); 114 break; 115 case RLINES: 116 if (S_ISREG(sbp->st_mode)) 117 if (!off) { 118 if (fseek(fp, 0L, SEEK_END) == -1) { 119 ierr(); 120 return; 121 } 122 } else 123 rlines(fp, off, sbp); 124 else if (off == 0) { 125 while (getc(fp) != EOF); 126 if (ferror(fp)) { 127 ierr(); 128 return; 129 } 130 } else 131 lines(fp, off); 132 break; 133 } 134 135 /* 136 * We pause for one second after displaying any data that has 137 * accumulated since we read the file. 138 */ 139 if (fflag) { 140 FD_ZERO(&zero); 141 second.tv_sec = 1; 142 second.tv_usec = 0; 143 } 144 145 for (;;) { 146 while ((ch = getc(fp)) != EOF) 147 if (putchar(ch) == EOF) 148 oerr(); 149 if (ferror(fp)) { 150 ierr(); 151 return; 152 } 153 (void)fflush(stdout); 154 if (!fflag) 155 break; 156 /* Sleep(3) is eight system calls. Do it fast. */ 157 if (select(0, &zero, &zero, &zero, &second) == -1) 158 err(1, "select: %s", strerror(errno)); 159 clearerr(fp); 160 } 161 } 162 163 /* 164 * rlines -- display the last offset lines of the file. 165 */ 166 static void 167 rlines(fp, off, sbp) 168 FILE *fp; 169 long off; 170 struct stat *sbp; 171 { 172 register off_t size; 173 register char *p; 174 char *start; 175 176 if (!(size = sbp->st_size)) 177 return; 178 179 if (size > SIZE_T_MAX) { 180 err(0, "%s: %s", fname, strerror(EFBIG)); 181 return; 182 } 183 184 if ((start = mmap(NULL, (size_t)size, 185 PROT_READ, 0, fileno(fp), (off_t)0)) == (caddr_t)-1) { 186 err(0, "%s: %s", fname, strerror(EFBIG)); 187 return; 188 } 189 190 /* Last char is special, ignore whether newline or not. */ 191 for (p = start + size - 1; --size;) 192 if (*--p == '\n' && !--off) { 193 ++p; 194 break; 195 } 196 197 /* Set the file pointer to reflect the length displayed. */ 198 size = sbp->st_size - size; 199 WR(p, size); 200 if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) { 201 ierr(); 202 return; 203 } 204 if (munmap(start, (size_t)sbp->st_size)) { 205 err(0, "%s: %s", fname, strerror(errno)); 206 return; 207 } 208 } 209