2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Edward Sze-Tyan Wang.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #include <sys/param.h>
38 #include <sys/mount.h>
39 #include <sys/types.h>
43 #include <sys/event.h>
54 #include <libcasper.h>
55 #include <casper/cap_fileargs.h>
59 static void rlines(FILE *, const char *fn, off_t, struct stat *);
60 static int show(file_info_t *);
61 static void set_events(file_info_t *files);
63 /* defines for inner loop actions */
68 static struct kevent *ev;
69 static int action = USE_SLEEP;
72 static const file_info_t *last;
75 * forward -- display the file, from an offset, forward.
77 * There are eight separate cases for this -- regular and non-regular
78 * files, by bytes or lines and from the beginning or end of the file.
80 * FBYTES byte offset from the beginning of the file
82 * NOREG read, counting bytes
84 * FLINES line offset from the beginning of the file
85 * REG read, counting lines
86 * NOREG read, counting lines
88 * RBYTES byte offset from the end of the file
90 * NOREG cyclically read characters into a wrap-around buffer
93 * REG mmap the file and step back until reach the correct offset.
94 * NOREG cyclically read lines into a wrap-around array of buffers
97 forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
105 if (S_ISREG(sbp->st_mode)) {
106 if (sbp->st_size < off)
108 if (fseeko(fp, off, SEEK_SET) == -1) {
113 if ((ch = getc(fp)) == EOF) {
125 if ((ch = getc(fp)) == EOF) {
132 if (ch == '\n' && !--off)
137 if (S_ISREG(sbp->st_mode)) {
138 if (sbp->st_size >= off &&
139 fseeko(fp, -off, SEEK_END) == -1) {
143 } else if (off == 0) {
144 while (getc(fp) != EOF);
150 if (bytes(fp, fn, off))
154 if (S_ISREG(sbp->st_mode))
156 if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
161 rlines(fp, fn, off, sbp);
163 while (getc(fp) != EOF);
169 if (lines(fp, fn, off))
176 while ((ch = getc(fp)) != EOF)
177 if (putchar(ch) == EOF)
183 (void)fflush(stdout);
187 * rlines -- display the last offset lines of the file.
190 rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
196 if (!(size = sbp->st_size))
200 map.mapoff = map.maxoff = size;
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.
207 while (curoff >= 0) {
208 if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
212 for (i = curoff - map.mapoff; i >= 0; i--)
213 if (map.start[i] == '\n' && --off == 0)
215 /* `i' is either the map offset of a '\n', or -1. */
216 curoff = map.mapoff + i;
221 if (mapprint(&map, curoff, size - curoff) != 0) {
226 /* Set the file pointer to reflect the length displayed. */
227 if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
231 if (map.start != NULL && munmap(map.start, map.maplen)) {
238 show(file_info_t *file)
242 while ((ch = getc(file->fp)) != EOF) {
244 if (vflag || (qflag == 0 && no_files > 1))
245 printfn(file->file_name, 1);
248 if (putchar(ch) == EOF)
251 (void)fflush(stdout);
252 if (ferror(file->fp)) {
255 ierr(file->file_name);
263 set_events(file_info_t *files)
274 for (i = 0, file = files; i < no_files; i++, file++) {
278 if (fstatfs(fileno(file->fp), &sf) == 0 &&
279 (sf.f_flags & MNT_LOCAL) == 0) {
284 if (Fflag && fileno(file->fp) != STDIN_FILENO) {
285 EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE,
286 EV_ADD | EV_ENABLE | EV_CLEAR,
287 NOTE_DELETE | NOTE_RENAME, 0, 0);
290 EV_SET(&ev[n], fileno(file->fp), EVFILT_READ,
291 EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0);
295 if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
301 * follow -- display the file, from an offset, forward.
305 follow(file_info_t *files, enum STYLE style, off_t off)
307 int active, ev_change, i, n;
313 /* Position each of the files */
315 for (i = 0, file = files; i < no_files; i++, file++) {
319 if (vflag || (qflag == 0 && no_files > 1))
320 printfn(file->file_name, 1);
321 forward(file->fp, file->file_name, style, off, &file->st);
323 if (!Fflag && !active)
332 * The number of kqueue events we track may vary over time and may
333 * even grow past its initial value in the -F case, but it will
334 * never exceed two per file, so just preallocate that.
336 ev = malloc(no_files * 2 * sizeof(struct kevent));
338 err(1, "failed to allocate memory for kevents");
344 for (i = 0, file = files; i < no_files; i++, file++) {
347 fileargs_fopen(fa, file->file_name,
349 if (file->fp != NULL &&
350 fstat(fileno(file->fp), &file->st)
355 if (file->fp != NULL)
359 if (fileno(file->fp) == STDIN_FILENO)
361 ftmp = fileargs_fopen(fa, file->file_name, "r");
363 fstat(fileno(ftmp), &sb2) == -1) {
365 ierr(file->file_name);
367 if (file->fp != NULL) {
378 if (sb2.st_ino != file->st.st_ino ||
379 sb2.st_dev != file->st.st_dev ||
384 memcpy(&file->st, &sb2,
385 sizeof(struct stat));
393 for (i = 0, file = files; i < no_files; i++, file++)
394 if (file->fp && !show(file))
405 * In the -F case we set a timeout to ensure that
406 * we re-stat the file at least once every second.
407 * If we've received EINTR, ignore it. Both reasons
408 * for its generation are transient.
411 n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL);
412 if (n < 0 && errno != EINTR)
418 } else if (ev->filter == EVFILT_READ && ev->data < 0) {
419 /* file shrank, reposition to end */
420 if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) {
421 ierr(file->file_name);
428 (void) usleep(250000);