forward.c revision 1.19 1 1.19 wiz /* $NetBSD: forward.c,v 1.19 2002/06/14 00:47:41 wiz Exp $ */
2 1.6 jtc
3 1.1 glass /*-
4 1.6 jtc * Copyright (c) 1991, 1993
5 1.6 jtc * The Regents of the University of California. All rights reserved.
6 1.1 glass *
7 1.1 glass * This code is derived from software contributed to Berkeley by
8 1.1 glass * Edward Sze-Tyan Wang.
9 1.1 glass *
10 1.1 glass * Redistribution and use in source and binary forms, with or without
11 1.1 glass * modification, are permitted provided that the following conditions
12 1.1 glass * are met:
13 1.1 glass * 1. Redistributions of source code must retain the above copyright
14 1.1 glass * notice, this list of conditions and the following disclaimer.
15 1.1 glass * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 glass * notice, this list of conditions and the following disclaimer in the
17 1.1 glass * documentation and/or other materials provided with the distribution.
18 1.1 glass * 3. All advertising materials mentioning features or use of this software
19 1.1 glass * must display the following acknowledgement:
20 1.1 glass * This product includes software developed by the University of
21 1.1 glass * California, Berkeley and its contributors.
22 1.1 glass * 4. Neither the name of the University nor the names of its contributors
23 1.1 glass * may be used to endorse or promote products derived from this software
24 1.1 glass * without specific prior written permission.
25 1.1 glass *
26 1.1 glass * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 1.1 glass * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 1.1 glass * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 1.1 glass * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 1.1 glass * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 1.1 glass * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 1.1 glass * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 1.1 glass * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 1.1 glass * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 1.1 glass * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 1.1 glass * SUCH DAMAGE.
37 1.1 glass */
38 1.1 glass
39 1.8 lukem #include <sys/cdefs.h>
40 1.1 glass #ifndef lint
41 1.6 jtc #if 0
42 1.6 jtc static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
43 1.6 jtc #endif
44 1.19 wiz __RCSID("$NetBSD: forward.c,v 1.19 2002/06/14 00:47:41 wiz Exp $");
45 1.1 glass #endif /* not lint */
46 1.1 glass
47 1.1 glass #include <sys/types.h>
48 1.1 glass #include <sys/stat.h>
49 1.1 glass #include <sys/time.h>
50 1.1 glass #include <sys/mman.h>
51 1.6 jtc
52 1.6 jtc #include <limits.h>
53 1.1 glass #include <fcntl.h>
54 1.1 glass #include <errno.h>
55 1.1 glass #include <unistd.h>
56 1.1 glass #include <stdio.h>
57 1.1 glass #include <stdlib.h>
58 1.1 glass #include <string.h>
59 1.1 glass #include "extern.h"
60 1.1 glass
61 1.19 wiz static int rlines(FILE *, long, struct stat *);
62 1.1 glass
63 1.1 glass /*
64 1.1 glass * forward -- display the file, from an offset, forward.
65 1.1 glass *
66 1.1 glass * There are eight separate cases for this -- regular and non-regular
67 1.1 glass * files, by bytes or lines and from the beginning or end of the file.
68 1.1 glass *
69 1.1 glass * FBYTES byte offset from the beginning of the file
70 1.1 glass * REG seek
71 1.1 glass * NOREG read, counting bytes
72 1.1 glass *
73 1.1 glass * FLINES line offset from the beginning of the file
74 1.1 glass * REG read, counting lines
75 1.1 glass * NOREG read, counting lines
76 1.1 glass *
77 1.1 glass * RBYTES byte offset from the end of the file
78 1.1 glass * REG seek
79 1.1 glass * NOREG cyclically read characters into a wrap-around buffer
80 1.1 glass *
81 1.1 glass * RLINES
82 1.1 glass * REG mmap the file and step back until reach the correct offset.
83 1.1 glass * NOREG cyclically read lines into a wrap-around array of buffers
84 1.1 glass */
85 1.1 glass void
86 1.19 wiz forward(FILE *fp, enum STYLE style, long int off, struct stat *sbp)
87 1.1 glass {
88 1.8 lukem int ch;
89 1.1 glass struct timeval second;
90 1.9 cjs int dostat = 0;
91 1.9 cjs struct stat statbuf;
92 1.9 cjs off_t lastsize = 0;
93 1.9 cjs dev_t lastdev;
94 1.9 cjs ino_t lastino;
95 1.9 cjs
96 1.9 cjs /* Keep track of file's previous incarnation. */
97 1.9 cjs lastdev = sbp->st_dev;
98 1.9 cjs lastino = sbp->st_ino;
99 1.1 glass
100 1.1 glass switch(style) {
101 1.1 glass case FBYTES:
102 1.1 glass if (off == 0)
103 1.1 glass break;
104 1.1 glass if (S_ISREG(sbp->st_mode)) {
105 1.1 glass if (sbp->st_size < off)
106 1.1 glass off = sbp->st_size;
107 1.6 jtc if (fseek(fp, off, SEEK_SET) == -1) {
108 1.1 glass ierr();
109 1.6 jtc return;
110 1.6 jtc }
111 1.1 glass } else while (off--)
112 1.1 glass if ((ch = getc(fp)) == EOF) {
113 1.6 jtc if (ferror(fp)) {
114 1.1 glass ierr();
115 1.6 jtc return;
116 1.1 glass }
117 1.6 jtc break;
118 1.6 jtc }
119 1.1 glass break;
120 1.1 glass case FLINES:
121 1.1 glass if (off == 0)
122 1.1 glass break;
123 1.1 glass for (;;) {
124 1.1 glass if ((ch = getc(fp)) == EOF) {
125 1.6 jtc if (ferror(fp)) {
126 1.1 glass ierr();
127 1.6 jtc return;
128 1.6 jtc }
129 1.1 glass break;
130 1.1 glass }
131 1.1 glass if (ch == '\n' && !--off)
132 1.1 glass break;
133 1.1 glass }
134 1.1 glass break;
135 1.1 glass case RBYTES:
136 1.1 glass if (S_ISREG(sbp->st_mode)) {
137 1.1 glass if (sbp->st_size >= off &&
138 1.6 jtc fseek(fp, -off, SEEK_END) == -1) {
139 1.1 glass ierr();
140 1.6 jtc return;
141 1.6 jtc }
142 1.1 glass } else if (off == 0) {
143 1.1 glass while (getc(fp) != EOF);
144 1.6 jtc if (ferror(fp)) {
145 1.1 glass ierr();
146 1.6 jtc return;
147 1.6 jtc }
148 1.16 cgd } else {
149 1.16 cgd if (bytes(fp, off))
150 1.16 cgd return;
151 1.16 cgd }
152 1.1 glass break;
153 1.1 glass case RLINES:
154 1.14 christos if (S_ISREG(sbp->st_mode)) {
155 1.1 glass if (!off) {
156 1.6 jtc if (fseek(fp, 0L, SEEK_END) == -1) {
157 1.1 glass ierr();
158 1.6 jtc return;
159 1.6 jtc }
160 1.16 cgd } else {
161 1.16 cgd if (rlines(fp, off, sbp))
162 1.16 cgd return;
163 1.16 cgd }
164 1.15 christos } else if (off == 0) {
165 1.1 glass while (getc(fp) != EOF);
166 1.6 jtc if (ferror(fp)) {
167 1.1 glass ierr();
168 1.6 jtc return;
169 1.6 jtc }
170 1.16 cgd } else {
171 1.16 cgd if (lines(fp, off))
172 1.16 cgd return;
173 1.16 cgd }
174 1.1 glass break;
175 1.8 lukem default:
176 1.8 lukem break;
177 1.1 glass }
178 1.1 glass
179 1.1 glass for (;;) {
180 1.9 cjs while ((ch = getc(fp)) != EOF) {
181 1.1 glass if (putchar(ch) == EOF)
182 1.1 glass oerr();
183 1.9 cjs }
184 1.6 jtc if (ferror(fp)) {
185 1.1 glass ierr();
186 1.6 jtc return;
187 1.6 jtc }
188 1.1 glass (void)fflush(stdout);
189 1.1 glass if (!fflag)
190 1.1 glass break;
191 1.7 ghudson /*
192 1.7 ghudson * We pause for one second after displaying any data that has
193 1.7 ghudson * accumulated since we read the file. Since sleep(3) takes
194 1.7 ghudson * eight system calls, use select() instead.
195 1.7 ghudson */
196 1.7 ghudson second.tv_sec = 1;
197 1.7 ghudson second.tv_usec = 0;
198 1.7 ghudson if (select(0, NULL, NULL, NULL, &second) == -1)
199 1.6 jtc err(1, "select: %s", strerror(errno));
200 1.1 glass clearerr(fp);
201 1.9 cjs
202 1.9 cjs if (fflag == 1)
203 1.9 cjs continue;
204 1.9 cjs /*
205 1.9 cjs * We restat the original filename every five seconds. If
206 1.9 cjs * the size is ever smaller than the last time we read it,
207 1.9 cjs * the file has probably been truncated; if the inode or
208 1.9 cjs * or device number are different, it has been rotated.
209 1.9 cjs * This causes us to close it, reopen it, and continue
210 1.9 cjs * the tail -f. If stat returns an error (say, because
211 1.9 cjs * the file has been removed), just continue with what
212 1.9 cjs * we've got open now.
213 1.9 cjs */
214 1.9 cjs if (dostat > 0) {
215 1.9 cjs dostat -= 1;
216 1.9 cjs } else {
217 1.9 cjs dostat = 5;
218 1.9 cjs if (stat(fname, &statbuf) == 0) {
219 1.9 cjs if (statbuf.st_dev != lastdev ||
220 1.9 cjs statbuf.st_ino != lastino ||
221 1.9 cjs statbuf.st_size < lastsize) {
222 1.9 cjs lastdev = statbuf.st_dev;
223 1.9 cjs lastino = statbuf.st_ino;
224 1.9 cjs lastsize = 0;
225 1.9 cjs fclose(fp);
226 1.9 cjs if ((fp = fopen(fname, "r")) == NULL)
227 1.9 cjs err(1, "can't reopen %s: %s",
228 1.9 cjs fname, strerror(errno));
229 1.9 cjs } else {
230 1.9 cjs lastsize = statbuf.st_size;
231 1.9 cjs }
232 1.9 cjs }
233 1.9 cjs }
234 1.1 glass }
235 1.1 glass }
236 1.1 glass
237 1.1 glass /*
238 1.1 glass * rlines -- display the last offset lines of the file.
239 1.16 cgd *
240 1.16 cgd * Non-zero return means than a (non-fatal) error occurred.
241 1.1 glass */
242 1.16 cgd static int
243 1.19 wiz rlines(FILE *fp, long int off, struct stat *sbp)
244 1.1 glass {
245 1.17 explorer off_t file_size;
246 1.17 explorer off_t file_remaining;
247 1.8 lukem char *p;
248 1.5 jtc char *start;
249 1.17 explorer off_t mmap_size;
250 1.17 explorer off_t mmap_offset;
251 1.17 explorer off_t mmap_remaining;
252 1.1 glass
253 1.17 explorer #define MMAP_MAXSIZE (10 * 1024 * 1024)
254 1.17 explorer
255 1.17 explorer if (!(file_size = sbp->st_size))
256 1.16 cgd return (0);
257 1.17 explorer file_remaining = file_size;
258 1.1 glass
259 1.18 explorer if (file_remaining > MMAP_MAXSIZE) {
260 1.17 explorer mmap_size = MMAP_MAXSIZE;
261 1.18 explorer mmap_offset = file_remaining - MMAP_MAXSIZE;
262 1.17 explorer } else {
263 1.18 explorer mmap_size = file_remaining;
264 1.17 explorer mmap_offset = 0;
265 1.6 jtc }
266 1.6 jtc
267 1.17 explorer while (off) {
268 1.17 explorer start = mmap(NULL, (size_t)mmap_size, PROT_READ,
269 1.17 explorer MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset);
270 1.17 explorer if (start == MAP_FAILED) {
271 1.17 explorer err(0, "%s: %s", fname, strerror(EFBIG));
272 1.17 explorer return (1);
273 1.17 explorer }
274 1.17 explorer
275 1.17 explorer mmap_remaining = mmap_size;
276 1.17 explorer /* Last char is special, ignore whether newline or not. */
277 1.17 explorer for (p = start + mmap_remaining - 1 ; --mmap_remaining ; )
278 1.17 explorer if (*--p == '\n' && !--off) {
279 1.17 explorer ++p;
280 1.17 explorer break;
281 1.17 explorer }
282 1.17 explorer
283 1.17 explorer file_remaining -= mmap_size - mmap_remaining;
284 1.1 glass
285 1.17 explorer if (off == 0)
286 1.18 explorer break;
287 1.18 explorer
288 1.18 explorer if (file_remaining == 0)
289 1.1 glass break;
290 1.17 explorer
291 1.17 explorer if (munmap(start, mmap_size)) {
292 1.17 explorer err(0, "%s: %s", fname, strerror(errno));
293 1.17 explorer return (1);
294 1.1 glass }
295 1.1 glass
296 1.17 explorer if (mmap_offset >= MMAP_MAXSIZE) {
297 1.17 explorer mmap_offset -= MMAP_MAXSIZE;
298 1.17 explorer } else {
299 1.17 explorer mmap_offset = 0;
300 1.17 explorer mmap_size = file_remaining;
301 1.17 explorer }
302 1.17 explorer }
303 1.17 explorer
304 1.17 explorer /*
305 1.17 explorer * Output the (perhaps partial) data in this mmap'd block.
306 1.17 explorer */
307 1.17 explorer WR(p, mmap_size - mmap_remaining);
308 1.17 explorer file_remaining += mmap_size - mmap_remaining;
309 1.17 explorer if (munmap(start, mmap_size)) {
310 1.17 explorer err(0, "%s: %s", fname, strerror(errno));
311 1.16 cgd return (1);
312 1.6 jtc }
313 1.17 explorer
314 1.17 explorer /*
315 1.17 explorer * Set the file pointer to reflect the length displayed.
316 1.17 explorer * This will cause the caller to redisplay the data if/when
317 1.17 explorer * needed.
318 1.17 explorer */
319 1.17 explorer if (fseeko(fp, file_remaining, SEEK_SET) == -1) {
320 1.17 explorer ierr();
321 1.16 cgd return (1);
322 1.5 jtc }
323 1.16 cgd return (0);
324 1.1 glass }
325