GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: forward.c,v 1.31 2016/07/05 05:06:27 jsg Exp $ */ |
||
2 |
/* $NetBSD: forward.c,v 1.7 1996/02/13 16:49:10 ghudson Exp $ */ |
||
3 |
|||
4 |
/*- |
||
5 |
* Copyright (c) 1991, 1993 |
||
6 |
* The Regents of the University of California. All rights reserved. |
||
7 |
* |
||
8 |
* This code is derived from software contributed to Berkeley by |
||
9 |
* Edward Sze-Tyan Wang. |
||
10 |
* |
||
11 |
* Redistribution and use in source and binary forms, with or without |
||
12 |
* modification, are permitted provided that the following conditions |
||
13 |
* are met: |
||
14 |
* 1. Redistributions of source code must retain the above copyright |
||
15 |
* notice, this list of conditions and the following disclaimer. |
||
16 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
17 |
* notice, this list of conditions and the following disclaimer in the |
||
18 |
* documentation and/or other materials provided with the distribution. |
||
19 |
* 3. Neither the name of the University nor the names of its contributors |
||
20 |
* may be used to endorse or promote products derived from this software |
||
21 |
* without specific prior written permission. |
||
22 |
* |
||
23 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
24 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
25 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
26 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
27 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
28 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
29 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
30 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
31 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
32 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
33 |
* SUCH DAMAGE. |
||
34 |
*/ |
||
35 |
|||
36 |
#include <sys/types.h> |
||
37 |
#include <sys/stat.h> |
||
38 |
#include <sys/event.h> |
||
39 |
|||
40 |
#include <err.h> |
||
41 |
#include <errno.h> |
||
42 |
#include <stdio.h> |
||
43 |
#include <stdlib.h> |
||
44 |
#include <string.h> |
||
45 |
#include <unistd.h> |
||
46 |
|||
47 |
#include "extern.h" |
||
48 |
|||
49 |
static int rlines(struct tailfile *, off_t); |
||
50 |
static inline void tfprint(FILE *fp); |
||
51 |
static int tfqueue(struct tailfile *tf); |
||
52 |
static const struct timespec *tfreopen(struct tailfile *tf); |
||
53 |
|||
54 |
static int kq = -1; |
||
55 |
|||
56 |
/* |
||
57 |
* forward -- display the file, from an offset, forward. |
||
58 |
* |
||
59 |
* There are eight separate cases for this -- regular and non-regular |
||
60 |
* files, by bytes or lines and from the beginning or end of the file. |
||
61 |
* |
||
62 |
* FBYTES byte offset from the beginning of the file |
||
63 |
* REG seek |
||
64 |
* NOREG read, counting bytes |
||
65 |
* |
||
66 |
* FLINES line offset from the beginning of the file |
||
67 |
* REG read, counting lines |
||
68 |
* NOREG read, counting lines |
||
69 |
* |
||
70 |
* RBYTES byte offset from the end of the file |
||
71 |
* REG seek |
||
72 |
* NOREG cyclically read characters into a wrap-around buffer |
||
73 |
* |
||
74 |
* RLINES |
||
75 |
* REG step back until the correct offset is reached. |
||
76 |
* NOREG cyclically read lines into a wrap-around array of buffers |
||
77 |
*/ |
||
78 |
void |
||
79 |
forward(struct tailfile *tf, int nfiles, enum STYLE style, off_t origoff) |
||
80 |
54 |
{ |
|
81 |
int ch; |
||
82 |
struct tailfile *ctf, *ltf; |
||
83 |
struct kevent ke; |
||
84 |
54 |
const struct timespec *ts = NULL; |
|
85 |
int i; |
||
86 |
int nevents; |
||
87 |
|||
88 |
✓✗ | 54 |
if (nfiles < 1) |
89 |
return; |
||
90 |
|||
91 |
✗✓✗✗ |
54 |
if (fflag && (kq = kqueue()) < 0) |
92 |
warn("kqueue"); |
||
93 |
|||
94 |
✓✓ | 108 |
for (i = 0; i < nfiles; i++) { |
95 |
54 |
off_t off = origoff; |
|
96 |
✗✓ | 54 |
if (nfiles > 1) |
97 |
printfname(tf[i].fname); |
||
98 |
|||
99 |
✗✓✗✗ ✗ |
54 |
switch(style) { |
100 |
case FBYTES: |
||
101 |
if (off == 0) |
||
102 |
break; |
||
103 |
if (S_ISREG(tf[i].sb.st_mode)) { |
||
104 |
if (tf[i].sb.st_size < off) |
||
105 |
off = tf[i].sb.st_size; |
||
106 |
if (fseeko(tf[i].fp, off, SEEK_SET) == -1) { |
||
107 |
ierr(tf[i].fname); |
||
108 |
return; |
||
109 |
} |
||
110 |
} else while (off--) |
||
111 |
if ((ch = getc(tf[i].fp)) == EOF) { |
||
112 |
if (ferror(tf[i].fp)) { |
||
113 |
ierr(tf[i].fname); |
||
114 |
return; |
||
115 |
} |
||
116 |
break; |
||
117 |
} |
||
118 |
break; |
||
119 |
case FLINES: |
||
120 |
✗✓ | 54 |
if (off == 0) |
121 |
break; |
||
122 |
for (;;) { |
||
123 |
✓✗✓✓ ✗✓ |
1267 |
if ((ch = getc(tf[i].fp)) == EOF) { |
124 |
if (ferror(tf[i].fp)) { |
||
125 |
ierr(tf[i].fname); |
||
126 |
return; |
||
127 |
} |
||
128 |
break; |
||
129 |
} |
||
130 |
✓✓✗✓ |
1267 |
if (ch == '\n' && !--off) |
131 |
54 |
break; |
|
132 |
} |
||
133 |
break; |
||
134 |
case RBYTES: |
||
135 |
if (S_ISREG(tf[i].sb.st_mode)) { |
||
136 |
if (tf[i].sb.st_size >= off && |
||
137 |
fseeko(tf[i].fp, -off, SEEK_END) == -1) { |
||
138 |
ierr(tf[i].fname); |
||
139 |
return; |
||
140 |
} |
||
141 |
} else if (off == 0) { |
||
142 |
while (getc(tf[i].fp) != EOF) |
||
143 |
; |
||
144 |
if (ferror(tf[i].fp)) { |
||
145 |
ierr(tf[i].fname); |
||
146 |
return; |
||
147 |
} |
||
148 |
} else { |
||
149 |
if (bytes(&(tf[i]), off)) |
||
150 |
return; |
||
151 |
} |
||
152 |
break; |
||
153 |
case RLINES: |
||
154 |
if (S_ISREG(tf[i].sb.st_mode)) { |
||
155 |
if (!off) { |
||
156 |
if (fseeko(tf[i].fp, (off_t)0, |
||
157 |
SEEK_END) == -1) { |
||
158 |
ierr(tf[i].fname); |
||
159 |
return; |
||
160 |
} |
||
161 |
} else if (rlines(&(tf[i]), off) != 0) |
||
162 |
lines(&(tf[i]), off); |
||
163 |
} else if (off == 0) { |
||
164 |
while (getc(tf[i].fp) != EOF) |
||
165 |
; |
||
166 |
if (ferror(tf[i].fp)) { |
||
167 |
ierr(tf[i].fname); |
||
168 |
return; |
||
169 |
} |
||
170 |
} else { |
||
171 |
if (lines(&(tf[i]), off)) |
||
172 |
return; |
||
173 |
} |
||
174 |
break; |
||
175 |
default: |
||
176 |
err(1, "Unsupported style"); |
||
177 |
} |
||
178 |
|||
179 |
54 |
tfprint(tf[i].fp); |
|
180 |
✗✓✗✗ |
54 |
if (fflag && tfqueue(&(tf[i])) == -1) |
181 |
warn("Unable to follow %s", tf[i].fname); |
||
182 |
|||
183 |
} |
||
184 |
54 |
ltf = &(tf[i-1]); |
|
185 |
|||
186 |
54 |
(void)fflush(stdout); |
|
187 |
✗✓✗✗ |
54 |
if (!fflag || kq < 0) |
188 |
return; |
||
189 |
|||
190 |
while (1) { |
||
191 |
if ((nevents = kevent(kq, NULL, 0, &ke, 1, ts)) <= 0) { |
||
192 |
if (errno == EINTR) { |
||
193 |
close(kq); |
||
194 |
return; |
||
195 |
} |
||
196 |
} |
||
197 |
|||
198 |
ctf = ke.udata; |
||
199 |
if (nevents > 0) { |
||
200 |
if (ke.filter == EVFILT_READ) { |
||
201 |
if (ctf != ltf) { |
||
202 |
printfname(ctf->fname); |
||
203 |
ltf = ctf; |
||
204 |
} |
||
205 |
clearerr(ctf->fp); |
||
206 |
tfprint(ctf->fp); |
||
207 |
if (ferror(ctf->fp)) { |
||
208 |
ierr(ctf->fname); |
||
209 |
fclose(ctf->fp); |
||
210 |
warn("Lost file %s", ctf->fname); |
||
211 |
continue; |
||
212 |
} |
||
213 |
(void)fflush(stdout); |
||
214 |
clearerr(ctf->fp); |
||
215 |
} else if (ke.filter == EVFILT_VNODE) { |
||
216 |
if (ke.fflags & (NOTE_DELETE | NOTE_RENAME)) { |
||
217 |
/* |
||
218 |
* File was deleted or renamed. |
||
219 |
* |
||
220 |
* Continue to look at it until |
||
221 |
* a new file reappears with |
||
222 |
* the same name. |
||
223 |
*/ |
||
224 |
(void) tfreopen(ctf); |
||
225 |
} else if (ke.fflags & NOTE_TRUNCATE) { |
||
226 |
warnx("%s has been truncated, " |
||
227 |
"resetting.", ctf->fname); |
||
228 |
fpurge(ctf->fp); |
||
229 |
rewind(ctf->fp); |
||
230 |
} |
||
231 |
} |
||
232 |
} |
||
233 |
ts = tfreopen(NULL); |
||
234 |
} |
||
235 |
} |
||
236 |
|||
237 |
/* |
||
238 |
* rlines -- display the last offset lines of the file. |
||
239 |
*/ |
||
240 |
static int |
||
241 |
rlines(struct tailfile *tf, off_t off) |
||
242 |
{ |
||
243 |
off_t pos; |
||
244 |
int ch; |
||
245 |
|||
246 |
pos = tf->sb.st_size; |
||
247 |
if (pos == 0) |
||
248 |
return (0); |
||
249 |
|||
250 |
/* |
||
251 |
* Position before char. |
||
252 |
* Last char is special, ignore it whether newline or not. |
||
253 |
*/ |
||
254 |
pos -= 2; |
||
255 |
ch = EOF; |
||
256 |
for (; off > 0 && pos >= 0; pos--) { |
||
257 |
/* A seek per char isn't a problem with a smart stdio */ |
||
258 |
if (fseeko(tf[0].fp, pos, SEEK_SET) == -1) { |
||
259 |
ierr(tf->fname); |
||
260 |
return (1); |
||
261 |
} |
||
262 |
if ((ch = getc(tf[0].fp)) == '\n') |
||
263 |
off--; |
||
264 |
else if (ch == EOF) { |
||
265 |
if (ferror(tf[0].fp)) { |
||
266 |
ierr(tf->fname); |
||
267 |
return (1); |
||
268 |
} |
||
269 |
break; |
||
270 |
} |
||
271 |
} |
||
272 |
/* If we read until start of file, put back last read char */ |
||
273 |
if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, tf[0].fp) == EOF) { |
||
274 |
ierr(tf->fname); |
||
275 |
return (1); |
||
276 |
} |
||
277 |
|||
278 |
while (!feof(tf[0].fp) && (ch = getc(tf[0].fp)) != EOF) |
||
279 |
if (putchar(ch) == EOF) |
||
280 |
oerr(); |
||
281 |
if (ferror(tf[0].fp)) { |
||
282 |
ierr(tf->fname); |
||
283 |
return (1); |
||
284 |
} |
||
285 |
|||
286 |
return (0); |
||
287 |
} |
||
288 |
|||
289 |
static inline void |
||
290 |
tfprint(FILE *fp) |
||
291 |
54 |
{ |
|
292 |
int ch; |
||
293 |
|||
294 |
✓✗✓✗ ✗✗✓✗ ✓✓✓✓ |
1589 |
while (!feof(fp) && (ch = getc(fp)) != EOF) |
295 |
✓✗✗✓ |
1481 |
if (putchar(ch) == EOF) |
296 |
oerr(); |
||
297 |
54 |
} |
|
298 |
|||
299 |
static int |
||
300 |
tfqueue(struct tailfile *tf) |
||
301 |
{ |
||
302 |
struct kevent ke[2]; |
||
303 |
int i = 1; |
||
304 |
|||
305 |
if (kq < 0) { |
||
306 |
errno = EBADF; |
||
307 |
return -1; |
||
308 |
} |
||
309 |
|||
310 |
EV_SET(&(ke[0]), fileno(tf->fp), EVFILT_READ, |
||
311 |
EV_ENABLE | EV_ADD | EV_CLEAR, 0, 0, tf); |
||
312 |
|||
313 |
if (S_ISREG(tf->sb.st_mode)) { |
||
314 |
i = 2; |
||
315 |
EV_SET(&(ke[1]), fileno(tf->fp), EVFILT_VNODE, |
||
316 |
EV_ENABLE | EV_ADD | EV_CLEAR, |
||
317 |
NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE, |
||
318 |
0, tf); |
||
319 |
} |
||
320 |
if (kevent(kq, ke, i, NULL, 0, NULL) < 0) { |
||
321 |
ierr(tf->fname); |
||
322 |
return -1; |
||
323 |
} |
||
324 |
return 0; |
||
325 |
} |
||
326 |
|||
327 |
#define AFILESINCR 8 |
||
328 |
static const struct timespec * |
||
329 |
tfreopen(struct tailfile *tf) { |
||
330 |
static struct tailfile **reopen = NULL; |
||
331 |
static int nfiles = 0, afiles = 0; |
||
332 |
static const struct timespec ts = {1, 0}; |
||
333 |
|||
334 |
struct stat sb; |
||
335 |
struct tailfile **treopen, *ttf; |
||
336 |
int i; |
||
337 |
|||
338 |
if (tf && ((stat(tf->fname, &sb) != 0) || sb.st_ino != tf->sb.st_ino)) { |
||
339 |
if (afiles < ++nfiles) { |
||
340 |
afiles += AFILESINCR; |
||
341 |
treopen = reallocarray(reopen, afiles, sizeof(*reopen)); |
||
342 |
if (treopen) |
||
343 |
reopen = treopen; |
||
344 |
else |
||
345 |
afiles -= AFILESINCR; |
||
346 |
} |
||
347 |
if (nfiles <= afiles) { |
||
348 |
for (i = 0; i < nfiles - 1; i++) |
||
349 |
if (strcmp(reopen[i]->fname, tf->fname) == 0) |
||
350 |
break; |
||
351 |
if (i < nfiles - 1) |
||
352 |
nfiles--; |
||
353 |
else |
||
354 |
reopen[nfiles-1] = tf; |
||
355 |
} else { |
||
356 |
warnx("Lost track of %s", tf->fname); |
||
357 |
nfiles--; |
||
358 |
} |
||
359 |
} |
||
360 |
|||
361 |
for (i = 0; i < nfiles; i++) { |
||
362 |
ttf = reopen[i]; |
||
363 |
if (stat(ttf->fname, &sb) == -1) |
||
364 |
continue; |
||
365 |
if (sb.st_ino != ttf->sb.st_ino) { |
||
366 |
(void) memcpy(&(ttf->sb), &sb, sizeof(ttf->sb)); |
||
367 |
ttf->fp = freopen(ttf->fname, "r", ttf->fp); |
||
368 |
if (ttf->fp == NULL) |
||
369 |
ierr(ttf->fname); |
||
370 |
else { |
||
371 |
warnx("%s has been replaced, reopening.", |
||
372 |
ttf->fname); |
||
373 |
tfqueue(ttf); |
||
374 |
} |
||
375 |
} |
||
376 |
reopen[i] = reopen[--nfiles]; |
||
377 |
} |
||
378 |
|||
379 |
return nfiles ? &ts : NULL; |
||
380 |
} |
Generated by: GCOVR (Version 3.3) |