]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/tail/forward.c
This commit was generated by cvs2svn to compensate for changes in r67754,
[FreeBSD/FreeBSD.git] / usr.bin / tail / forward.c
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  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * $FreeBSD$
37  */
38
39 #ifndef lint
40 static char sccsid[] = "@(#)forward.c   8.1 (Berkeley) 6/6/93";
41 #endif /* not lint */
42
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/time.h>
46 #include <sys/mman.h>
47 #include <sys/event.h>
48
49 #include <limits.h>
50 #include <fcntl.h>
51 #include <errno.h>
52 #include <unistd.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <err.h>
57 #include "extern.h"
58
59 static void rlines __P((FILE *, long, struct stat *));
60
61 /* defines for inner loop actions */
62 #define USE_SLEEP       0
63 #define USE_KQUEUE      1
64 #define ADD_EVENTS      2
65
66 /*
67  * forward -- display the file, from an offset, forward.
68  *
69  * There are eight separate cases for this -- regular and non-regular
70  * files, by bytes or lines and from the beginning or end of the file.
71  *
72  * FBYTES       byte offset from the beginning of the file
73  *      REG     seek
74  *      NOREG   read, counting bytes
75  *
76  * FLINES       line offset from the beginning of the file
77  *      REG     read, counting lines
78  *      NOREG   read, counting lines
79  *
80  * RBYTES       byte offset from the end of the file
81  *      REG     seek
82  *      NOREG   cyclically read characters into a wrap-around buffer
83  *
84  * RLINES
85  *      REG     mmap the file and step back until reach the correct offset.
86  *      NOREG   cyclically read lines into a wrap-around array of buffers
87  */
88 void
89 forward(fp, style, off, sbp)
90         FILE *fp;
91         enum STYLE style;
92         long off;
93         struct stat *sbp;
94 {
95         int ch, kq = -1;
96         int action = USE_SLEEP;
97         struct kevent ev[2];
98         struct stat sb2;
99
100         switch(style) {
101         case FBYTES:
102                 if (off == 0)
103                         break;
104                 if (S_ISREG(sbp->st_mode)) {
105                         if (sbp->st_size < off)
106                                 off = sbp->st_size;
107                         if (fseek(fp, off, SEEK_SET) == -1) {
108                                 ierr();
109                                 return;
110                         }
111                 } else while (off--)
112                         if ((ch = getc(fp)) == EOF) {
113                                 if (ferror(fp)) {
114                                         ierr();
115                                         return;
116                                 }
117                                 break;
118                         }
119                 break;
120         case FLINES:
121                 if (off == 0)
122                         break;
123                 for (;;) {
124                         if ((ch = getc(fp)) == EOF) {
125                                 if (ferror(fp)) {
126                                         ierr();
127                                         return;
128                                 }
129                                 break;
130                         }
131                         if (ch == '\n' && !--off)
132                                 break;
133                 }
134                 break;
135         case RBYTES:
136                 if (S_ISREG(sbp->st_mode)) {
137                         if (sbp->st_size >= off &&
138                             fseek(fp, -off, SEEK_END) == -1) {
139                                 ierr();
140                                 return;
141                         }
142                 } else if (off == 0) {
143                         while (getc(fp) != EOF);
144                         if (ferror(fp)) {
145                                 ierr();
146                                 return;
147                         }
148                 } else
149                         if (bytes(fp, off))
150                                 return;
151                 break;
152         case RLINES:
153                 if (S_ISREG(sbp->st_mode))
154                         if (!off) {
155                                 if (fseek(fp, 0L, SEEK_END) == -1) {
156                                         ierr();
157                                         return;
158                                 }
159                         } else
160                                 rlines(fp, off, sbp);
161                 else if (off == 0) {
162                         while (getc(fp) != EOF);
163                         if (ferror(fp)) {
164                                 ierr();
165                                 return;
166                         }
167                 } else
168                         if (lines(fp, off))
169                                 return;
170                 break;
171         }
172
173         if (fflag) {
174                 kq = kqueue();
175                 if (kq < 0)
176                         err(1, "kqueue");
177                 action = ADD_EVENTS;
178         }
179
180         for (;;) {
181                 while ((ch = getc(fp)) != EOF)
182                         if (putchar(ch) == EOF)
183                                 oerr();
184                 if (ferror(fp)) {
185                         ierr();
186                         return;
187                 }
188                 (void)fflush(stdout);
189                 if (! fflag)
190                         break;
191                 clearerr(fp);
192
193                 switch (action) {
194                 case ADD_EVENTS: {
195                         int n = 0;
196                         struct timespec ts = { 0, 0 };
197
198                         if (Fflag && fileno(fp) != STDIN_FILENO) {
199                                 ev[n].ident = fileno(fp);
200                                 ev[n].filter = EVFILT_VNODE;
201                                 ev[n].flags = EV_ADD | EV_ENABLE | EV_CLEAR;
202                                 ev[n].fflags = NOTE_DELETE | NOTE_RENAME;
203                                 n++;
204                         }
205                         ev[n].ident = fileno(fp);
206                         ev[n].filter = EVFILT_READ;
207                         ev[n].flags = EV_ADD | EV_ENABLE;
208                         n++;
209
210                         if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
211                                 close(kq);
212                                 kq = -1;
213                                 action = USE_SLEEP;
214                         } else {
215                                 action = USE_KQUEUE;
216                         }
217                         break;
218                 }
219
220                 case USE_KQUEUE:
221                         if (kevent(kq, NULL, 0, ev, 1, NULL) < 0)
222                                 err(1, "kevent");
223
224                         if (ev->filter == EVFILT_VNODE) {
225                                 /* file was rotated, wait until it reappears */
226                                 action = USE_SLEEP;
227                         } else if (ev->data < 0) {
228                                 /* file shrank, reposition to end */
229                                 if (fseek(fp, 0L, SEEK_END) == -1) {
230                                         ierr();
231                                         return;
232                                 }
233                         }
234                         break;
235
236                 case USE_SLEEP:
237                         (void) usleep(250000);
238                         clearerr(fp);
239
240                         if (Fflag && fileno(fp) != STDIN_FILENO &&
241                             stat(fname, &sb2) != -1) {
242                                 if (sb2.st_ino != sbp->st_ino ||
243                                     sb2.st_dev != sbp->st_dev ||
244                                     sb2.st_rdev != sbp->st_rdev ||
245                                     sb2.st_nlink == 0) {
246                                         fp = freopen(fname, "r", fp);
247                                         if (fp == NULL) {
248                                                 ierr();
249                                                 break;
250                                         }
251                                         *sbp = sb2;
252                                         if (kq != -1)
253                                                 action = ADD_EVENTS;
254                                 }
255                         }
256                         break;
257                 }
258         }
259 }
260
261 /*
262  * rlines -- display the last offset lines of the file.
263  */
264 static void
265 rlines(fp, off, sbp)
266         FILE *fp;
267         long off;
268         struct stat *sbp;
269 {
270         register off_t size;
271         register char *p;
272         char *start;
273
274         if (!(size = sbp->st_size))
275                 return;
276
277         if (size > SIZE_T_MAX) {
278                 errno = EFBIG;
279                 ierr();
280                 return;
281         }
282
283         if ((start = mmap(NULL, (size_t)size,
284             PROT_READ, MAP_SHARED, fileno(fp), (off_t)0)) == MAP_FAILED) {
285                 ierr();
286                 return;
287         }
288
289         /* Last char is special, ignore whether newline or not. */
290         for (p = start + size - 1; --size;)
291                 if (*--p == '\n' && !--off) {
292                         ++p;
293                         break;
294                 }
295
296         /* Set the file pointer to reflect the length displayed. */
297         size = sbp->st_size - size;
298         WR(p, size);
299         if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
300                 ierr();
301                 return;
302         }
303         if (munmap(start, (size_t)sbp->st_size)) {
304                 ierr();
305                 return;
306         }
307 }