xref: /netbsd-src/usr.bin/tail/forward.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: forward.c,v 1.15 1998/12/19 22:27:54 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Edward Sze-Tyan Wang.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)forward.c	8.1 (Berkeley) 6/6/93";
43 #endif
44 __RCSID("$NetBSD: forward.c,v 1.15 1998/12/19 22:27:54 christos Exp $");
45 #endif /* not lint */
46 
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/time.h>
50 #include <sys/mman.h>
51 
52 #include <limits.h>
53 #include <fcntl.h>
54 #include <errno.h>
55 #include <unistd.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include "extern.h"
60 
61 static void rlines __P((FILE *, long, struct stat *));
62 
63 /*
64  * forward -- display the file, from an offset, forward.
65  *
66  * There are eight separate cases for this -- regular and non-regular
67  * files, by bytes or lines and from the beginning or end of the file.
68  *
69  * FBYTES	byte offset from the beginning of the file
70  *	REG	seek
71  *	NOREG	read, counting bytes
72  *
73  * FLINES	line offset from the beginning of the file
74  *	REG	read, counting lines
75  *	NOREG	read, counting lines
76  *
77  * RBYTES	byte offset from the end of the file
78  *	REG	seek
79  *	NOREG	cyclically read characters into a wrap-around buffer
80  *
81  * RLINES
82  *	REG	mmap the file and step back until reach the correct offset.
83  *	NOREG	cyclically read lines into a wrap-around array of buffers
84  */
85 void
86 forward(fp, style, off, sbp)
87 	FILE *fp;
88 	enum STYLE style;
89 	long off;
90 	struct stat *sbp;
91 {
92 	int ch;
93 	struct timeval second;
94 	int dostat = 0;
95 	struct stat statbuf;
96 	off_t lastsize = 0;
97 	dev_t lastdev;
98 	ino_t lastino;
99 
100 	/* Keep track of file's previous incarnation. */
101 	lastdev = sbp->st_dev;
102 	lastino = sbp->st_ino;
103 
104 	switch(style) {
105 	case FBYTES:
106 		if (off == 0)
107 			break;
108 		if (S_ISREG(sbp->st_mode)) {
109 			if (sbp->st_size < off)
110 				off = sbp->st_size;
111 			if (fseek(fp, off, SEEK_SET) == -1) {
112 				ierr();
113 				return;
114 			}
115 		} else while (off--)
116 			if ((ch = getc(fp)) == EOF) {
117 				if (ferror(fp)) {
118 					ierr();
119 					return;
120 				}
121 				break;
122 			}
123 		break;
124 	case FLINES:
125 		if (off == 0)
126 			break;
127 		for (;;) {
128 			if ((ch = getc(fp)) == EOF) {
129 				if (ferror(fp)) {
130 					ierr();
131 					return;
132 				}
133 				break;
134 			}
135 			if (ch == '\n' && !--off)
136 				break;
137 		}
138 		break;
139 	case RBYTES:
140 		if (S_ISREG(sbp->st_mode)) {
141 			if (sbp->st_size >= off &&
142 			    fseek(fp, -off, SEEK_END) == -1) {
143 				ierr();
144 				return;
145 			}
146 		} else if (off == 0) {
147 			while (getc(fp) != EOF);
148 			if (ferror(fp)) {
149 				ierr();
150 				return;
151 			}
152 		} else
153 			bytes(fp, off);
154 		break;
155 	case RLINES:
156 		if (S_ISREG(sbp->st_mode)) {
157 			if (!off) {
158 				if (fseek(fp, 0L, SEEK_END) == -1) {
159 					ierr();
160 					return;
161 				}
162 			} else
163 				rlines(fp, off, sbp);
164 		} else if (off == 0) {
165 			while (getc(fp) != EOF);
166 			if (ferror(fp)) {
167 				ierr();
168 				return;
169 			}
170 		} else
171 			lines(fp, off);
172 		break;
173 	default:
174 		break;
175 	}
176 
177 	for (;;) {
178 		while ((ch = getc(fp)) != EOF)  {
179 			if (putchar(ch) == EOF)
180 				oerr();
181 		}
182 		if (ferror(fp)) {
183 			ierr();
184 			return;
185 		}
186 		(void)fflush(stdout);
187 		if (!fflag)
188 			break;
189 		/*
190 		 * We pause for one second after displaying any data that has
191 		 * accumulated since we read the file.  Since sleep(3) takes
192 		 * eight system calls, use select() instead.
193 		 */
194 		second.tv_sec = 1;
195 		second.tv_usec = 0;
196 		if (select(0, NULL, NULL, NULL, &second) == -1)
197 			err(1, "select: %s", strerror(errno));
198 		clearerr(fp);
199 
200 		if (fflag == 1)
201 			continue;
202 		/*
203 		 * We restat the original filename every five seconds. If
204 		 * the size is ever smaller than the last time we read it,
205 		 * the file has probably been truncated; if the inode or
206 		 * or device number are different, it has been rotated.
207 		 * This causes us to close it, reopen it, and continue
208 		 * the tail -f. If stat returns an error (say, because
209 		 * the file has been removed), just continue with what
210 		 * we've got open now.
211 		 */
212 		if (dostat > 0)  {
213 			dostat -= 1;
214 		} else {
215 			dostat = 5;
216 			if (stat(fname, &statbuf) == 0)  {
217 				if (statbuf.st_dev != lastdev ||
218 				    statbuf.st_ino != lastino ||
219 				    statbuf.st_size < lastsize)  {
220 					lastdev = statbuf.st_dev;
221 					lastino = statbuf.st_ino;
222 					lastsize = 0;
223 					fclose(fp);
224 					if ((fp = fopen(fname, "r")) == NULL)
225 						err(1, "can't reopen %s: %s",
226 						    fname, strerror(errno));
227 				} else {
228 					lastsize = statbuf.st_size;
229 				}
230 			}
231 		}
232 	}
233 }
234 
235 /*
236  * rlines -- display the last offset lines of the file.
237  */
238 static void
239 rlines(fp, off, sbp)
240 	FILE *fp;
241 	long off;
242 	struct stat *sbp;
243 {
244 	off_t size;
245 	char *p;
246 	char *start;
247 
248 	if (!(size = sbp->st_size))
249 		return;
250 
251 	if (size > SIZE_T_MAX) {
252 		err(0, "%s: %s", fname, strerror(EFBIG));
253 		return;
254 	}
255 
256 	if ((start = mmap(NULL, (size_t)size, PROT_READ,
257 	    MAP_FILE|MAP_SHARED, fileno(fp), (off_t)0)) == (caddr_t)-1) {
258 		err(0, "%s: %s", fname, strerror(EFBIG));
259 		return;
260 	}
261 
262 	/* Last char is special, ignore whether newline or not. */
263 	for (p = start + size - 1; --size;)
264 		if (*--p == '\n' && !--off) {
265 			++p;
266 			break;
267 		}
268 
269 	/* Set the file pointer to reflect the length displayed. */
270 	size = sbp->st_size - size;
271 	WR(p, size);
272 	if (fseek(fp, (long)sbp->st_size, SEEK_SET) == -1) {
273 		ierr();
274 		return;
275 	}
276 	if (munmap(start, (size_t)sbp->st_size)) {
277 		err(0, "%s: %s", fname, strerror(errno));
278 		return;
279 	}
280 }
281