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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * Solaris porting notes: the original FreeBSD version made use of the
35 * BSD kqueue event notification framework; this
36 * was changed to use usleep()
37 */
38
39 #include <sys/param.h>
40 #include <sys/mount.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/statfs.h>
44 #include <sys/statvfs.h>
45 #include <sys/time.h>
46 #include <sys/mman.h>
47 #include <sys/poll.h>
48 #include <port.h>
49 #include <err.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <limits.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 #include "extern.h"
59
60 static void rlines(FILE *, const char *fn, off_t, struct stat *);
61 static int show(file_info_t *);
62 static void set_events(file_info_t *files);
63
64 /* defines for inner loop actions */
65 #define USE_SLEEP 0
66 #define ADD_EVENTS 2
67
68 int action = USE_SLEEP;
69
70 static const file_info_t *last;
71
72 /*
73 * forward -- display the file, from an offset, forward.
74 *
75 * There are eight separate cases for this -- regular and non-regular
76 * files, by bytes or lines and from the beginning or end of the file.
77 *
78 * FBYTES byte offset from the beginning of the file
79 * REG seek
80 * NOREG read, counting bytes
81 *
82 * FLINES line offset from the beginning of the file
83 * REG read, counting lines
84 * NOREG read, counting lines
85 *
86 * RBYTES byte offset from the end of the file
87 * REG seek
88 * NOREG cyclically read characters into a wrap-around buffer
89 *
90 * RLINES
91 * REG mmap the file and step back until reach the correct offset.
92 * NOREG cyclically read lines into a wrap-around array of buffers
93 */
94 void
95 forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
96 {
97 int ch;
98
99 switch (style) {
100 case FBYTES:
101 if (off == 0)
102 break;
103 if (S_ISREG(sbp->st_mode)) {
104 if (sbp->st_size < off)
105 off = sbp->st_size;
106 if (fseeko(fp, off, SEEK_SET) == -1) {
107 ierr(fn);
108 return;
109 }
110 } else while (off--)
111 if ((ch = getc(fp)) == EOF) {
112 if (ferror(fp)) {
113 ierr(fn);
114 return;
115 }
116 break;
117 }
118 break;
119 case FLINES:
120 if (off == 0)
121 break;
122 for (;;) {
123 if ((ch = getc(fp)) == EOF) {
124 if (ferror(fp)) {
125 ierr(fn);
126 return;
127 }
128 break;
129 }
130 if (ch == '\n' && !--off)
131 break;
132 }
133 break;
134 case RBYTES:
135 if (S_ISREG(sbp->st_mode)) {
136 if (sbp->st_size >= off &&
137 fseeko(fp, -off, SEEK_END) == -1) {
138 ierr(fn);
139 return;
140 }
141 } else if (off == 0) {
142 while (getc(fp) != EOF)
143 ;
144 if (ferror(fp)) {
145 ierr(fn);
146 return;
147 }
148 } else
149 if (bytes(fp, fn, off))
150 return;
151 break;
152 case RLINES:
153 if (S_ISREG(sbp->st_mode))
154 if (!off) {
155 if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
156 ierr(fn);
157 return;
158 }
159 } else
160 rlines(fp, fn, off, sbp);
161 else if (off == 0) {
162 while (getc(fp) != EOF)
163 ;
164 if (ferror(fp)) {
165 ierr(fn);
166 return;
167 }
168 } else
169 if (lines(fp, fn, off))
170 return;
171 break;
172 default:
173 break;
174 }
175
176 while ((ch = getc(fp)) != EOF)
177 if (putchar(ch) == EOF)
178 oerr();
179 if (ferror(fp)) {
180 ierr(fn);
181 return;
182 }
183 (void) fflush(stdout);
184 }
185
186 /*
187 * rlines -- display the last offset lines of the file.
188 */
189 static void
190 rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
191 {
192 struct mapinfo map;
193 off_t curoff, size;
194 int i;
195
196 if ((size = sbp->st_size) == 0)
197 return;
198 map.start = NULL;
199 map.fd = fileno(fp);
200 map.mapoff = map.maxoff = size;
201
202 /*
203 * Last char is special, ignore whether newline or not. Note that
204 * size == 0 is dealt with above, and size == 1 sets curoff to -1.
205 */
206 curoff = size - 2;
207 while (curoff >= 0) {
208 if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
209 ierr(fn);
210 return;
211 }
212 for (i = curoff - map.mapoff; i >= 0; i--)
213 if (map.start[i] == '\n' && --off == 0)
214 break;
215 /* `i' is either the map offset of a '\n', or -1. */
216 curoff = map.mapoff + i;
217 if (i >= 0)
218 break;
219 }
220 curoff++;
221 if (mapprint(&map, curoff, size - curoff) != 0) {
222 ierr(fn);
223 exit(1);
224 }
225
226 /* Set the file pointer to reflect the length displayed. */
227 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
228 ierr(fn);
229 return;
230 }
231 if (map.start != NULL && munmap(map.start, map.maplen)) {
232 ierr(fn);
233 return;
234 }
235 }
236
237 static int
238 show(file_info_t *file)
239 {
240 int ch;
241
242 while ((ch = getc(file->fp)) != EOF) {
243 if (last != file && no_files > 1) {
244 if (!qflag)
245 (void) printf("\n==> %s <==\n",
246 file->file_name);
247 last = file;
248 }
249 if (putchar(ch) == EOF)
250 oerr();
251 }
252 (void) fflush(stdout);
253 if (ferror(file->fp)) {
254 (void) fclose(file->fp);
255 file->fp = NULL;
256 ierr(file->file_name);
257 return (0);
258 }
259 clearerr(file->fp);
260 return (1);
261 }
262
263 static void
264 set_events(file_info_t *files)
265 {
266 int i;
267 file_info_t *file;
268
269 for (i = 0, file = files; i < no_files; i++, file++) {
270 if (! file->fp)
271 continue;
272
273 (void) fstat(fileno(file->fp), &file->st);
274 }
275 }
276
277 /*
278 * follow -- display the file, from an offset, forward.
279 *
280 */
281 void
282 follow(file_info_t *files, enum STYLE style, off_t off)
283 {
284 int active, ev_change, i, n = -1;
285 struct stat sb2;
286 file_info_t *file;
287
288 /* Position each of the files */
289
290 file = files;
291 active = 0;
292 n = 0;
293 for (i = 0; i < no_files; i++, file++) {
294 if (file->fp) {
295 active = 1;
296 n++;
297 if (no_files > 1 && !qflag)
298 (void) printf("\n==> %s <==\n",
299 file->file_name);
300 forward(file->fp, file->file_name, style, off,
301 &file->st);
302 if (Fflag && fileno(file->fp) != STDIN_FILENO)
303 n++;
304 }
305 }
306 if (!Fflag && !active)
307 return;
308
309 last = --file;
310 set_events(files);
311
312 for (;;) {
313 ev_change = 0;
314 if (Fflag) {
315 for (i = 0, file = files; i < no_files; i++, file++) {
316 if (!file->fp) {
317 file->fp = fopen(file->file_name, "r");
318 if (file->fp != NULL &&
319 fstat(fileno(file->fp), &file->st)
320 == -1) {
321 (void) fclose(file->fp);
322 file->fp = NULL;
323 }
324 if (file->fp != NULL)
325 ev_change++;
326 continue;
327 }
328 if (fileno(file->fp) == STDIN_FILENO)
329 continue;
330 if (stat(file->file_name, &sb2) == -1) {
331 if (errno != ENOENT)
332 ierr(file->file_name);
333 (void) show(file);
334 (void) fclose(file->fp);
335 file->fp = NULL;
336 ev_change++;
337 continue;
338 }
339
340 if (sb2.st_ino != file->st.st_ino ||
341 sb2.st_dev != file->st.st_dev ||
342 sb2.st_nlink == 0) {
343 (void) show(file);
344 file->fp = freopen(file->file_name, "r",
345 file->fp);
346 if (file->fp != NULL)
347 (void) memcpy(&file->st, &sb2,
348 sizeof (struct stat));
349 else if (errno != ENOENT)
350 ierr(file->file_name);
351 ev_change++;
352 }
353 }
354 }
355
356 for (i = 0, file = files; i < no_files; i++, file++)
357 if (file->fp && !show(file))
358 ev_change++;
359
360 if (ev_change)
361 set_events(files);
362
363 switch (action) {
364 case USE_SLEEP:
365 (void) usleep(250000);
366 break;
367 }
368 }
369 }