xref: /original-bsd/usr.bin/tail/forward.c (revision 68d9582f)
1 /*-
2  * Copyright (c) 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Edward Sze-Tyan Wang.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)forward.c	5.6 (Berkeley) 05/14/92";
13 #endif /* not lint */
14 
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/time.h>
18 #include <sys/mman.h>
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "extern.h"
26 
27 static void rlines __P((FILE *, long, struct stat *));
28 
29 /*
30  * forward -- display the file, from an offset, forward.
31  *
32  * There are eight separate cases for this -- regular and non-regular
33  * files, by bytes or lines and from the beginning or end of the file.
34  *
35  * FBYTES	byte offset from the beginning of the file
36  *	REG	seek
37  *	NOREG	read, counting bytes
38  *
39  * FLINES	line offset from the beginning of the file
40  *	REG	read, counting lines
41  *	NOREG	read, counting lines
42  *
43  * RBYTES	byte offset from the end of the file
44  *	REG	seek
45  *	NOREG	cyclically read characters into a wrap-around buffer
46  *
47  * RLINES
48  *	REG	mmap the file and step back until reach the correct offset.
49  *	NOREG	cyclically read lines into a wrap-around array of buffers
50  */
51 void
52 forward(fp, style, off, sbp)
53 	FILE *fp;
54 	enum STYLE style;
55 	long off;
56 	struct stat *sbp;
57 {
58 	register int ch;
59 	struct timeval second;
60 	fd_set zero;
61 
62 	switch(style) {
63 	case FBYTES:
64 		if (off == 0)
65 			break;
66 		if (S_ISREG(sbp->st_mode)) {
67 			if (sbp->st_size < off)
68 				off = sbp->st_size;
69 			if (fseek(fp, off, SEEK_SET) == -1) {
70 				ierr();
71 				return;
72 			}
73 		} else while (off--)
74 			if ((ch = getc(fp)) == EOF) {
75 				if (ferror(fp)) {
76 					ierr();
77 					return;
78 				}
79 				break;
80 			}
81 		break;
82 	case FLINES:
83 		if (off == 0)
84 			break;
85 		for (;;) {
86 			if ((ch = getc(fp)) == EOF) {
87 				if (ferror(fp)) {
88 					ierr();
89 					return;
90 				}
91 				break;
92 			}
93 			if (ch == '\n' && !--off)
94 				break;
95 		}
96 		break;
97 	case RBYTES:
98 		if (S_ISREG(sbp->st_mode)) {
99 			if (sbp->st_size >= off &&
100 			    fseek(fp, -off, SEEK_END) == -1) {
101 				ierr();
102 				return;
103 			}
104 		} else if (off == 0) {
105 			while (getc(fp) != EOF);
106 			if (ferror(fp)) {
107 				ierr();
108 				return;
109 			}
110 		} else
111 			bytes(fp, off);
112 		break;
113 	case RLINES:
114 		if (S_ISREG(sbp->st_mode))
115 			if (!off) {
116 				if (fseek(fp, 0L, SEEK_END) == -1) {
117 					ierr();
118 					return;
119 				}
120 			} else
121 				rlines(fp, off, sbp);
122 		else if (off == 0) {
123 			while (getc(fp) != EOF);
124 			if (ferror(fp)) {
125 				ierr();
126 				return;
127 			}
128 		} else
129 			lines(fp, off);
130 		break;
131 	}
132 
133 	/*
134 	 * We pause for one second after displaying any data that has
135 	 * accumulated since we read the file.
136 	 */
137 	if (fflag) {
138 		FD_ZERO(&zero);
139 		second.tv_sec = 1;
140 		second.tv_usec = 0;
141 	}
142 
143 	for (;;) {
144 		while ((ch = getc(fp)) != EOF)
145 			if (putchar(ch) == EOF)
146 				oerr();
147 		if (ferror(fp)) {
148 			ierr();
149 			return;
150 		}
151 		(void)fflush(stdout);
152 		if (!fflag)
153 			break;
154 		/* Sleep(3) is eight system calls.  Do it fast. */
155 		if (select(0, &zero, &zero, &zero, &second) == -1)
156 			err(1, "select: %s", strerror(errno));
157 		clearerr(fp);
158 	}
159 }
160 
161 /*
162  * rlines -- display the last offset lines of the file.
163  */
164 static void
165 rlines(fp, off, sbp)
166 	FILE *fp;
167 	long off;
168 	struct stat *sbp;
169 {
170 	register off_t size;
171 	register char *p;
172 	char *start;
173 
174 	if (!(size = sbp->st_size))
175 		return;
176 
177 	if ((start = mmap(NULL,
178 	    size, PROT_READ, MAP_FILE, fileno(fp), (off_t)0)) == (caddr_t)-1) {
179 		err(0, "%s", strerror(errno));
180 		return;
181 	}
182 
183 	/* Last char is special, ignore whether newline or not. */
184 	for (p = start + size - 1; --size;)
185 		if (*--p == '\n' && !--off) {
186 			++p;
187 			break;
188 		}
189 
190 	/* Set the file pointer to reflect the length displayed. */
191 	size = sbp->st_size - size;
192 	WR(p, size);
193 	if (fseek(fp, sbp->st_size, SEEK_SET) == -1) {
194 		ierr();
195 		return;
196 	}
197 	if (munmap(start, size)) {
198 		err(0, "%s", strerror(errno));
199 		return;
200 	}
201 }
202