Commit | Line | Data |
---|---|---|
48f06ece KB |
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 | |
7542cdf7 | 12 | static char sccsid[] = "@(#)forward.c 5.6 (Berkeley) %G%"; |
48f06ece KB |
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; | |
31919d23 | 69 | if (fseek(fp, off, SEEK_SET) == -1) { |
48f06ece | 70 | ierr(); |
31919d23 KB |
71 | return; |
72 | } | |
48f06ece KB |
73 | } else while (off--) |
74 | if ((ch = getc(fp)) == EOF) { | |
31919d23 | 75 | if (ferror(fp)) { |
48f06ece | 76 | ierr(); |
31919d23 | 77 | return; |
48f06ece | 78 | } |
31919d23 KB |
79 | break; |
80 | } | |
48f06ece KB |
81 | break; |
82 | case FLINES: | |
83 | if (off == 0) | |
84 | break; | |
85 | for (;;) { | |
86 | if ((ch = getc(fp)) == EOF) { | |
31919d23 | 87 | if (ferror(fp)) { |
48f06ece | 88 | ierr(); |
31919d23 KB |
89 | return; |
90 | } | |
48f06ece KB |
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 && | |
31919d23 | 100 | fseek(fp, -off, SEEK_END) == -1) { |
48f06ece | 101 | ierr(); |
31919d23 KB |
102 | return; |
103 | } | |
48f06ece KB |
104 | } else if (off == 0) { |
105 | while (getc(fp) != EOF); | |
31919d23 | 106 | if (ferror(fp)) { |
48f06ece | 107 | ierr(); |
31919d23 KB |
108 | return; |
109 | } | |
48f06ece KB |
110 | } else |
111 | bytes(fp, off); | |
112 | break; | |
113 | case RLINES: | |
114 | if (S_ISREG(sbp->st_mode)) | |
115 | if (!off) { | |
31919d23 | 116 | if (fseek(fp, 0L, SEEK_END) == -1) { |
48f06ece | 117 | ierr(); |
31919d23 KB |
118 | return; |
119 | } | |
48f06ece KB |
120 | } else |
121 | rlines(fp, off, sbp); | |
122 | else if (off == 0) { | |
123 | while (getc(fp) != EOF); | |
31919d23 | 124 | if (ferror(fp)) { |
48f06ece | 125 | ierr(); |
31919d23 KB |
126 | return; |
127 | } | |
48f06ece KB |
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(); | |
31919d23 | 147 | if (ferror(fp)) { |
48f06ece | 148 | ierr(); |
31919d23 KB |
149 | return; |
150 | } | |
96cc7d48 | 151 | (void)fflush(stdout); |
48f06ece KB |
152 | if (!fflag) |
153 | break; | |
154 | /* Sleep(3) is eight system calls. Do it fast. */ | |
155 | if (select(0, &zero, &zero, &zero, &second) == -1) | |
31919d23 | 156 | err(1, "select: %s", strerror(errno)); |
48f06ece KB |
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; | |
aae81fb4 | 172 | char *start; |
48f06ece KB |
173 | |
174 | if (!(size = sbp->st_size)) | |
175 | return; | |
176 | ||
aae81fb4 | 177 | if ((start = mmap(NULL, |
31919d23 KB |
178 | size, PROT_READ, MAP_FILE, fileno(fp), (off_t)0)) == (caddr_t)-1) { |
179 | err(0, "%s", strerror(errno)); | |
180 | return; | |
181 | } | |
48f06ece KB |
182 | |
183 | /* Last char is special, ignore whether newline or not. */ | |
aae81fb4 | 184 | for (p = start + size - 1; --size;) |
34554469 KB |
185 | if (*--p == '\n' && !--off) { |
186 | ++p; | |
48f06ece | 187 | break; |
34554469 | 188 | } |
48f06ece KB |
189 | |
190 | /* Set the file pointer to reflect the length displayed. */ | |
191 | size = sbp->st_size - size; | |
34554469 | 192 | WR(p, size); |
31919d23 | 193 | if (fseek(fp, sbp->st_size, SEEK_SET) == -1) { |
48f06ece | 194 | ierr(); |
31919d23 KB |
195 | return; |
196 | } | |
aae81fb4 KB |
197 | if (munmap(start, size)) { |
198 | err(0, "%s", strerror(errno)); | |
199 | return; | |
200 | } | |
48f06ece | 201 | } |