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
forward(fp,style,off,sbp)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
rlines(fp,off,sbp)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