forward.c revision 1.31.8.1 1 1.31.8.1 tls /* $NetBSD: forward.c,v 1.31.8.1 2014/08/20 00:05:04 tls 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.8.1 tls __RCSID("$NetBSD: forward.c,v 1.31.8.1 2014/08/20 00:05:04 tls 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.23 jdolecek struct kevent ev[2];
94 1.9 cjs
95 1.1 glass switch(style) {
96 1.1 glass case FBYTES:
97 1.1 glass if (off == 0)
98 1.1 glass break;
99 1.1 glass if (S_ISREG(sbp->st_mode)) {
100 1.1 glass if (sbp->st_size < off)
101 1.1 glass off = sbp->st_size;
102 1.26 itojun if (fseeko(fp, off, SEEK_SET) == -1) {
103 1.1 glass ierr();
104 1.6 jtc return;
105 1.6 jtc }
106 1.1 glass } else while (off--)
107 1.1 glass if ((ch = getc(fp)) == EOF) {
108 1.6 jtc if (ferror(fp)) {
109 1.1 glass ierr();
110 1.6 jtc return;
111 1.1 glass }
112 1.6 jtc break;
113 1.6 jtc }
114 1.1 glass break;
115 1.1 glass case FLINES:
116 1.1 glass if (off == 0)
117 1.1 glass break;
118 1.1 glass for (;;) {
119 1.1 glass if ((ch = getc(fp)) == EOF) {
120 1.6 jtc if (ferror(fp)) {
121 1.1 glass ierr();
122 1.6 jtc return;
123 1.6 jtc }
124 1.1 glass break;
125 1.1 glass }
126 1.1 glass if (ch == '\n' && !--off)
127 1.1 glass break;
128 1.1 glass }
129 1.1 glass break;
130 1.1 glass case RBYTES:
131 1.1 glass if (S_ISREG(sbp->st_mode)) {
132 1.1 glass if (sbp->st_size >= off &&
133 1.26 itojun fseeko(fp, -off, SEEK_END) == -1) {
134 1.1 glass ierr();
135 1.6 jtc return;
136 1.6 jtc }
137 1.1 glass } else if (off == 0) {
138 1.1 glass while (getc(fp) != EOF);
139 1.6 jtc if (ferror(fp)) {
140 1.1 glass ierr();
141 1.6 jtc return;
142 1.6 jtc }
143 1.16 cgd } else {
144 1.29 lukem if (displaybytes(fp, off))
145 1.16 cgd return;
146 1.16 cgd }
147 1.1 glass break;
148 1.1 glass case RLINES:
149 1.14 christos if (S_ISREG(sbp->st_mode)) {
150 1.1 glass if (!off) {
151 1.6 jtc if (fseek(fp, 0L, SEEK_END) == -1) {
152 1.1 glass ierr();
153 1.6 jtc return;
154 1.6 jtc }
155 1.16 cgd } else {
156 1.16 cgd if (rlines(fp, off, sbp))
157 1.16 cgd return;
158 1.16 cgd }
159 1.15 christos } else if (off == 0) {
160 1.1 glass while (getc(fp) != EOF);
161 1.6 jtc if (ferror(fp)) {
162 1.1 glass ierr();
163 1.6 jtc return;
164 1.6 jtc }
165 1.16 cgd } else {
166 1.29 lukem if (displaylines(fp, off))
167 1.16 cgd return;
168 1.16 cgd }
169 1.1 glass break;
170 1.8 lukem default:
171 1.8 lukem break;
172 1.1 glass }
173 1.1 glass
174 1.23 jdolecek if (fflag) {
175 1.23 jdolecek kq = kqueue();
176 1.23 jdolecek if (kq < 0)
177 1.30 christos xerr(1, "kqueue");
178 1.23 jdolecek action = ADD_EVENTS;
179 1.23 jdolecek }
180 1.23 jdolecek
181 1.1 glass for (;;) {
182 1.9 cjs while ((ch = getc(fp)) != EOF) {
183 1.1 glass if (putchar(ch) == EOF)
184 1.1 glass oerr();
185 1.9 cjs }
186 1.6 jtc if (ferror(fp)) {
187 1.1 glass ierr();
188 1.6 jtc return;
189 1.6 jtc }
190 1.1 glass (void)fflush(stdout);
191 1.1 glass if (!fflag)
192 1.1 glass break;
193 1.23 jdolecek
194 1.1 glass clearerr(fp);
195 1.9 cjs
196 1.23 jdolecek switch (action) {
197 1.23 jdolecek case ADD_EVENTS:
198 1.23 jdolecek n = 0;
199 1.23 jdolecek
200 1.23 jdolecek memset(ev, 0, sizeof(ev));
201 1.23 jdolecek if (fflag == 2 && fileno(fp) != STDIN_FILENO) {
202 1.23 jdolecek EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
203 1.31 christos EV_ADD | EV_ENABLE | EV_CLEAR,
204 1.31 christos NOTE_DELETE | NOTE_RENAME, 0, 0);
205 1.23 jdolecek n++;
206 1.23 jdolecek }
207 1.23 jdolecek EV_SET(&ev[n], fileno(fp), EVFILT_READ,
208 1.31 christos EV_ADD | EV_ENABLE, 0, 0, 0);
209 1.23 jdolecek n++;
210 1.23 jdolecek
211 1.31 christos if (kevent(kq, ev, n, NULL, 0, NULL) == -1) {
212 1.23 jdolecek close(kq);
213 1.23 jdolecek kq = -1;
214 1.23 jdolecek action = USE_SLEEP;
215 1.23 jdolecek } else {
216 1.23 jdolecek action = USE_KQUEUE;
217 1.23 jdolecek }
218 1.23 jdolecek break;
219 1.23 jdolecek
220 1.23 jdolecek case USE_KQUEUE:
221 1.31 christos if (kevent(kq, NULL, 0, ev, 1, NULL) == -1)
222 1.30 christos xerr(1, "kevent");
223 1.23 jdolecek
224 1.23 jdolecek if (ev[0].filter == EVFILT_VNODE) {
225 1.23 jdolecek /* file was rotated, wait until it reappears */
226 1.23 jdolecek action = USE_SLEEP;
227 1.23 jdolecek } else if (ev[0].data < 0) {
228 1.23 jdolecek /* file shrank, reposition to end */
229 1.23 jdolecek if (fseek(fp, 0L, SEEK_END) == -1) {
230 1.23 jdolecek ierr();
231 1.23 jdolecek return;
232 1.9 cjs }
233 1.9 cjs }
234 1.23 jdolecek break;
235 1.23 jdolecek
236 1.23 jdolecek case USE_SLEEP:
237 1.23 jdolecek /*
238 1.23 jdolecek * We pause for one second after displaying any data
239 1.23 jdolecek * that has accumulated since we read the file.
240 1.23 jdolecek */
241 1.24 lukem (void) sleep(1);
242 1.23 jdolecek
243 1.23 jdolecek if (fflag == 2 && fileno(fp) != STDIN_FILENO &&
244 1.23 jdolecek stat(fname, &statbuf) != -1) {
245 1.23 jdolecek if (statbuf.st_ino != sbp->st_ino ||
246 1.23 jdolecek statbuf.st_dev != sbp->st_dev ||
247 1.23 jdolecek statbuf.st_rdev != sbp->st_rdev ||
248 1.23 jdolecek statbuf.st_nlink == 0) {
249 1.23 jdolecek fp = freopen(fname, "r", fp);
250 1.23 jdolecek if (fp == NULL) {
251 1.23 jdolecek ierr();
252 1.28 christos goto out;
253 1.23 jdolecek }
254 1.23 jdolecek *sbp = statbuf;
255 1.23 jdolecek if (kq != -1)
256 1.23 jdolecek action = ADD_EVENTS;
257 1.23 jdolecek } else if (kq != -1)
258 1.23 jdolecek action = USE_KQUEUE;
259 1.23 jdolecek }
260 1.23 jdolecek break;
261 1.9 cjs }
262 1.1 glass }
263 1.28 christos out:
264 1.23 jdolecek if (fflag && kq != -1)
265 1.23 jdolecek close(kq);
266 1.1 glass }
267 1.1 glass
268 1.1 glass /*
269 1.1 glass * rlines -- display the last offset lines of the file.
270 1.16 cgd *
271 1.16 cgd * Non-zero return means than a (non-fatal) error occurred.
272 1.1 glass */
273 1.16 cgd static int
274 1.26 itojun rlines(FILE *fp, off_t off, struct stat *sbp)
275 1.1 glass {
276 1.17 explorer off_t file_size;
277 1.17 explorer off_t file_remaining;
278 1.27 lukem char *p = NULL;
279 1.27 lukem char *start = NULL;
280 1.17 explorer off_t mmap_size;
281 1.17 explorer off_t mmap_offset;
282 1.27 lukem off_t mmap_remaining = 0;
283 1.1 glass
284 1.17 explorer #define MMAP_MAXSIZE (10 * 1024 * 1024)
285 1.17 explorer
286 1.17 explorer if (!(file_size = sbp->st_size))
287 1.31 christos return 0;
288 1.17 explorer file_remaining = file_size;
289 1.1 glass
290 1.18 explorer if (file_remaining > MMAP_MAXSIZE) {
291 1.17 explorer mmap_size = MMAP_MAXSIZE;
292 1.18 explorer mmap_offset = file_remaining - MMAP_MAXSIZE;
293 1.17 explorer } else {
294 1.18 explorer mmap_size = file_remaining;
295 1.17 explorer mmap_offset = 0;
296 1.6 jtc }
297 1.6 jtc
298 1.17 explorer while (off) {
299 1.17 explorer start = mmap(NULL, (size_t)mmap_size, PROT_READ,
300 1.17 explorer MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset);
301 1.17 explorer if (start == MAP_FAILED) {
302 1.30 christos xerr(0, "%s", fname);
303 1.31 christos return 1;
304 1.17 explorer }
305 1.17 explorer
306 1.17 explorer mmap_remaining = mmap_size;
307 1.17 explorer /* Last char is special, ignore whether newline or not. */
308 1.17 explorer for (p = start + mmap_remaining - 1 ; --mmap_remaining ; )
309 1.17 explorer if (*--p == '\n' && !--off) {
310 1.17 explorer ++p;
311 1.17 explorer break;
312 1.17 explorer }
313 1.17 explorer
314 1.17 explorer file_remaining -= mmap_size - mmap_remaining;
315 1.1 glass
316 1.17 explorer if (off == 0)
317 1.18 explorer break;
318 1.18 explorer
319 1.18 explorer if (file_remaining == 0)
320 1.1 glass break;
321 1.17 explorer
322 1.17 explorer if (munmap(start, mmap_size)) {
323 1.30 christos xerr(0, "%s", fname);
324 1.31 christos return 1;
325 1.1 glass }
326 1.1 glass
327 1.17 explorer if (mmap_offset >= MMAP_MAXSIZE) {
328 1.17 explorer mmap_offset -= MMAP_MAXSIZE;
329 1.17 explorer } else {
330 1.17 explorer mmap_offset = 0;
331 1.17 explorer mmap_size = file_remaining;
332 1.17 explorer }
333 1.17 explorer }
334 1.17 explorer
335 1.17 explorer /*
336 1.17 explorer * Output the (perhaps partial) data in this mmap'd block.
337 1.17 explorer */
338 1.17 explorer WR(p, mmap_size - mmap_remaining);
339 1.17 explorer file_remaining += mmap_size - mmap_remaining;
340 1.17 explorer if (munmap(start, mmap_size)) {
341 1.30 christos xerr(0, "%s", fname);
342 1.31 christos return 1;
343 1.6 jtc }
344 1.17 explorer
345 1.17 explorer /*
346 1.17 explorer * Set the file pointer to reflect the length displayed.
347 1.17 explorer * This will cause the caller to redisplay the data if/when
348 1.17 explorer * needed.
349 1.17 explorer */
350 1.17 explorer if (fseeko(fp, file_remaining, SEEK_SET) == -1) {
351 1.17 explorer ierr();
352 1.31 christos return 1;
353 1.5 jtc }
354 1.31 christos return 0;
355 1.1 glass }
356