forward.c revision 1.31 1 1.31 christos /* $NetBSD: forward.c,v 1.31 2011/09/03 10:59:10 christos 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.25 agc * 3. Neither the name of the University nor the names of its contributors
19 1.1 glass * may be used to endorse or promote products derived from this software
20 1.1 glass * without specific prior written permission.
21 1.1 glass *
22 1.1 glass * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 1.1 glass * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 1.1 glass * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 1.1 glass * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 1.1 glass * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 1.1 glass * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 1.1 glass * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 1.1 glass * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 1.1 glass * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 1.1 glass * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 1.1 glass * SUCH DAMAGE.
33 1.1 glass */
34 1.1 glass
35 1.8 lukem #include <sys/cdefs.h>
36 1.1 glass #ifndef lint
37 1.6 jtc #if 0
38 1.6 jtc static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
39 1.6 jtc #endif
40 1.31 christos __RCSID("$NetBSD: forward.c,v 1.31 2011/09/03 10:59:10 christos Exp $");
41 1.1 glass #endif /* not lint */
42 1.1 glass
43 1.1 glass #include <sys/types.h>
44 1.1 glass #include <sys/stat.h>
45 1.1 glass #include <sys/time.h>
46 1.1 glass #include <sys/mman.h>
47 1.23 jdolecek #include <sys/event.h>
48 1.6 jtc
49 1.6 jtc #include <limits.h>
50 1.1 glass #include <fcntl.h>
51 1.1 glass #include <errno.h>
52 1.1 glass #include <unistd.h>
53 1.1 glass #include <stdio.h>
54 1.1 glass #include <stdlib.h>
55 1.1 glass #include <string.h>
56 1.1 glass #include "extern.h"
57 1.1 glass
58 1.26 itojun static int rlines(FILE *, off_t, struct stat *);
59 1.1 glass
60 1.23 jdolecek /* defines for inner loop actions */
61 1.23 jdolecek #define USE_SLEEP 0
62 1.23 jdolecek #define USE_KQUEUE 1
63 1.23 jdolecek #define ADD_EVENTS 2
64 1.23 jdolecek
65 1.1 glass /*
66 1.1 glass * forward -- display the file, from an offset, forward.
67 1.1 glass *
68 1.1 glass * There are eight separate cases for this -- regular and non-regular
69 1.1 glass * files, by bytes or lines and from the beginning or end of the file.
70 1.1 glass *
71 1.1 glass * FBYTES byte offset from the beginning of the file
72 1.1 glass * REG seek
73 1.1 glass * NOREG read, counting bytes
74 1.1 glass *
75 1.1 glass * FLINES line offset from the beginning of the file
76 1.1 glass * REG read, counting lines
77 1.1 glass * NOREG read, counting lines
78 1.1 glass *
79 1.1 glass * RBYTES byte offset from the end of the file
80 1.1 glass * REG seek
81 1.1 glass * NOREG cyclically read characters into a wrap-around buffer
82 1.1 glass *
83 1.1 glass * RLINES
84 1.1 glass * REG mmap the file and step back until reach the correct offset.
85 1.1 glass * NOREG cyclically read lines into a wrap-around array of buffers
86 1.1 glass */
87 1.1 glass void
88 1.26 itojun forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
89 1.1 glass {
90 1.23 jdolecek int ch, n;
91 1.23 jdolecek int kq=-1, action=USE_SLEEP;
92 1.9 cjs struct stat statbuf;
93 1.9 cjs dev_t lastdev;
94 1.9 cjs ino_t lastino;
95 1.23 jdolecek struct kevent ev[2];
96 1.9 cjs
97 1.9 cjs /* Keep track of file's previous incarnation. */
98 1.9 cjs lastdev = sbp->st_dev;
99 1.9 cjs lastino = sbp->st_ino;
100 1.1 glass
101 1.1 glass switch(style) {
102 1.1 glass case FBYTES:
103 1.1 glass if (off == 0)
104 1.1 glass break;
105 1.1 glass if (S_ISREG(sbp->st_mode)) {
106 1.1 glass if (sbp->st_size < off)
107 1.1 glass off = sbp->st_size;
108 1.26 itojun if (fseeko(fp, off, SEEK_SET) == -1) {
109 1.1 glass ierr();
110 1.6 jtc return;
111 1.6 jtc }
112 1.1 glass } else while (off--)
113 1.1 glass if ((ch = getc(fp)) == EOF) {
114 1.6 jtc if (ferror(fp)) {
115 1.1 glass ierr();
116 1.6 jtc return;
117 1.1 glass }
118 1.6 jtc break;
119 1.6 jtc }
120 1.1 glass break;
121 1.1 glass case FLINES:
122 1.1 glass if (off == 0)
123 1.1 glass break;
124 1.1 glass for (;;) {
125 1.1 glass if ((ch = getc(fp)) == EOF) {
126 1.6 jtc if (ferror(fp)) {
127 1.1 glass ierr();
128 1.6 jtc return;
129 1.6 jtc }
130 1.1 glass break;
131 1.1 glass }
132 1.1 glass if (ch == '\n' && !--off)
133 1.1 glass break;
134 1.1 glass }
135 1.1 glass break;
136 1.1 glass case RBYTES:
137 1.1 glass if (S_ISREG(sbp->st_mode)) {
138 1.1 glass if (sbp->st_size >= off &&
139 1.26 itojun fseeko(fp, -off, SEEK_END) == -1) {
140 1.1 glass ierr();
141 1.6 jtc return;
142 1.6 jtc }
143 1.1 glass } else if (off == 0) {
144 1.1 glass while (getc(fp) != EOF);
145 1.6 jtc if (ferror(fp)) {
146 1.1 glass ierr();
147 1.6 jtc return;
148 1.6 jtc }
149 1.16 cgd } else {
150 1.29 lukem if (displaybytes(fp, off))
151 1.16 cgd return;
152 1.16 cgd }
153 1.1 glass break;
154 1.1 glass case RLINES:
155 1.14 christos if (S_ISREG(sbp->st_mode)) {
156 1.1 glass if (!off) {
157 1.6 jtc if (fseek(fp, 0L, SEEK_END) == -1) {
158 1.1 glass ierr();
159 1.6 jtc return;
160 1.6 jtc }
161 1.16 cgd } else {
162 1.16 cgd if (rlines(fp, off, sbp))
163 1.16 cgd return;
164 1.16 cgd }
165 1.15 christos } else if (off == 0) {
166 1.1 glass while (getc(fp) != EOF);
167 1.6 jtc if (ferror(fp)) {
168 1.1 glass ierr();
169 1.6 jtc return;
170 1.6 jtc }
171 1.16 cgd } else {
172 1.29 lukem if (displaylines(fp, off))
173 1.16 cgd return;
174 1.16 cgd }
175 1.1 glass break;
176 1.8 lukem default:
177 1.8 lukem break;
178 1.1 glass }
179 1.1 glass
180 1.23 jdolecek if (fflag) {
181 1.23 jdolecek kq = kqueue();
182 1.23 jdolecek if (kq < 0)
183 1.30 christos xerr(1, "kqueue");
184 1.23 jdolecek action = ADD_EVENTS;
185 1.23 jdolecek }
186 1.23 jdolecek
187 1.1 glass for (;;) {
188 1.9 cjs while ((ch = getc(fp)) != EOF) {
189 1.1 glass if (putchar(ch) == EOF)
190 1.1 glass oerr();
191 1.9 cjs }
192 1.6 jtc if (ferror(fp)) {
193 1.1 glass ierr();
194 1.6 jtc return;
195 1.6 jtc }
196 1.1 glass (void)fflush(stdout);
197 1.1 glass if (!fflag)
198 1.1 glass break;
199 1.23 jdolecek
200 1.1 glass clearerr(fp);
201 1.9 cjs
202 1.23 jdolecek switch (action) {
203 1.23 jdolecek case ADD_EVENTS:
204 1.23 jdolecek n = 0;
205 1.23 jdolecek
206 1.23 jdolecek memset(ev, 0, sizeof(ev));
207 1.23 jdolecek if (fflag == 2 && fileno(fp) != STDIN_FILENO) {
208 1.23 jdolecek EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
209 1.31 christos EV_ADD | EV_ENABLE | EV_CLEAR,
210 1.31 christos NOTE_DELETE | NOTE_RENAME, 0, 0);
211 1.23 jdolecek n++;
212 1.23 jdolecek }
213 1.23 jdolecek EV_SET(&ev[n], fileno(fp), EVFILT_READ,
214 1.31 christos EV_ADD | EV_ENABLE, 0, 0, 0);
215 1.23 jdolecek n++;
216 1.23 jdolecek
217 1.31 christos if (kevent(kq, ev, n, NULL, 0, NULL) == -1) {
218 1.23 jdolecek close(kq);
219 1.23 jdolecek kq = -1;
220 1.23 jdolecek action = USE_SLEEP;
221 1.23 jdolecek } else {
222 1.23 jdolecek action = USE_KQUEUE;
223 1.23 jdolecek }
224 1.23 jdolecek break;
225 1.23 jdolecek
226 1.23 jdolecek case USE_KQUEUE:
227 1.31 christos if (kevent(kq, NULL, 0, ev, 1, NULL) == -1)
228 1.30 christos xerr(1, "kevent");
229 1.23 jdolecek
230 1.23 jdolecek if (ev[0].filter == EVFILT_VNODE) {
231 1.23 jdolecek /* file was rotated, wait until it reappears */
232 1.23 jdolecek action = USE_SLEEP;
233 1.23 jdolecek } else if (ev[0].data < 0) {
234 1.23 jdolecek /* file shrank, reposition to end */
235 1.23 jdolecek if (fseek(fp, 0L, SEEK_END) == -1) {
236 1.23 jdolecek ierr();
237 1.23 jdolecek return;
238 1.9 cjs }
239 1.9 cjs }
240 1.23 jdolecek break;
241 1.23 jdolecek
242 1.23 jdolecek case USE_SLEEP:
243 1.23 jdolecek /*
244 1.23 jdolecek * We pause for one second after displaying any data
245 1.23 jdolecek * that has accumulated since we read the file.
246 1.23 jdolecek */
247 1.24 lukem (void) sleep(1);
248 1.23 jdolecek
249 1.23 jdolecek if (fflag == 2 && fileno(fp) != STDIN_FILENO &&
250 1.23 jdolecek stat(fname, &statbuf) != -1) {
251 1.23 jdolecek if (statbuf.st_ino != sbp->st_ino ||
252 1.23 jdolecek statbuf.st_dev != sbp->st_dev ||
253 1.23 jdolecek statbuf.st_rdev != sbp->st_rdev ||
254 1.23 jdolecek statbuf.st_nlink == 0) {
255 1.23 jdolecek fp = freopen(fname, "r", fp);
256 1.23 jdolecek if (fp == NULL) {
257 1.23 jdolecek ierr();
258 1.28 christos goto out;
259 1.23 jdolecek }
260 1.23 jdolecek *sbp = statbuf;
261 1.23 jdolecek if (kq != -1)
262 1.23 jdolecek action = ADD_EVENTS;
263 1.23 jdolecek } else if (kq != -1)
264 1.23 jdolecek action = USE_KQUEUE;
265 1.23 jdolecek }
266 1.23 jdolecek break;
267 1.9 cjs }
268 1.1 glass }
269 1.28 christos out:
270 1.23 jdolecek if (fflag && kq != -1)
271 1.23 jdolecek close(kq);
272 1.1 glass }
273 1.1 glass
274 1.1 glass /*
275 1.1 glass * rlines -- display the last offset lines of the file.
276 1.16 cgd *
277 1.16 cgd * Non-zero return means than a (non-fatal) error occurred.
278 1.1 glass */
279 1.16 cgd static int
280 1.26 itojun rlines(FILE *fp, off_t off, struct stat *sbp)
281 1.1 glass {
282 1.17 explorer off_t file_size;
283 1.17 explorer off_t file_remaining;
284 1.27 lukem char *p = NULL;
285 1.27 lukem char *start = NULL;
286 1.17 explorer off_t mmap_size;
287 1.17 explorer off_t mmap_offset;
288 1.27 lukem off_t mmap_remaining = 0;
289 1.1 glass
290 1.17 explorer #define MMAP_MAXSIZE (10 * 1024 * 1024)
291 1.17 explorer
292 1.17 explorer if (!(file_size = sbp->st_size))
293 1.31 christos return 0;
294 1.17 explorer file_remaining = file_size;
295 1.1 glass
296 1.18 explorer if (file_remaining > MMAP_MAXSIZE) {
297 1.17 explorer mmap_size = MMAP_MAXSIZE;
298 1.18 explorer mmap_offset = file_remaining - MMAP_MAXSIZE;
299 1.17 explorer } else {
300 1.18 explorer mmap_size = file_remaining;
301 1.17 explorer mmap_offset = 0;
302 1.6 jtc }
303 1.6 jtc
304 1.17 explorer while (off) {
305 1.17 explorer start = mmap(NULL, (size_t)mmap_size, PROT_READ,
306 1.17 explorer MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset);
307 1.17 explorer if (start == MAP_FAILED) {
308 1.30 christos xerr(0, "%s", fname);
309 1.31 christos return 1;
310 1.17 explorer }
311 1.17 explorer
312 1.17 explorer mmap_remaining = mmap_size;
313 1.17 explorer /* Last char is special, ignore whether newline or not. */
314 1.17 explorer for (p = start + mmap_remaining - 1 ; --mmap_remaining ; )
315 1.17 explorer if (*--p == '\n' && !--off) {
316 1.17 explorer ++p;
317 1.17 explorer break;
318 1.17 explorer }
319 1.17 explorer
320 1.17 explorer file_remaining -= mmap_size - mmap_remaining;
321 1.1 glass
322 1.17 explorer if (off == 0)
323 1.18 explorer break;
324 1.18 explorer
325 1.18 explorer if (file_remaining == 0)
326 1.1 glass break;
327 1.17 explorer
328 1.17 explorer if (munmap(start, mmap_size)) {
329 1.30 christos xerr(0, "%s", fname);
330 1.31 christos return 1;
331 1.1 glass }
332 1.1 glass
333 1.17 explorer if (mmap_offset >= MMAP_MAXSIZE) {
334 1.17 explorer mmap_offset -= MMAP_MAXSIZE;
335 1.17 explorer } else {
336 1.17 explorer mmap_offset = 0;
337 1.17 explorer mmap_size = file_remaining;
338 1.17 explorer }
339 1.17 explorer }
340 1.17 explorer
341 1.17 explorer /*
342 1.17 explorer * Output the (perhaps partial) data in this mmap'd block.
343 1.17 explorer */
344 1.17 explorer WR(p, mmap_size - mmap_remaining);
345 1.17 explorer file_remaining += mmap_size - mmap_remaining;
346 1.17 explorer if (munmap(start, mmap_size)) {
347 1.30 christos xerr(0, "%s", fname);
348 1.31 christos return 1;
349 1.6 jtc }
350 1.17 explorer
351 1.17 explorer /*
352 1.17 explorer * Set the file pointer to reflect the length displayed.
353 1.17 explorer * This will cause the caller to redisplay the data if/when
354 1.17 explorer * needed.
355 1.17 explorer */
356 1.17 explorer if (fseeko(fp, file_remaining, SEEK_SET) == -1) {
357 1.17 explorer ierr();
358 1.31 christos return 1;
359 1.5 jtc }
360 1.31 christos return 0;
361 1.1 glass }
362