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