Bug Summary

File:src/usr.bin/tail/forward.c
Warning:line 111, column 10
Although the value stored to 'ch' is used in the enclosing expression, the value is never actually read from 'ch'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name forward.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/tail/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/tail/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/tail/forward.c
1/* $OpenBSD: forward.c,v 1.33 2019/06/28 13:35:04 deraadt 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(*__errno()).h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "extern.h"
48
49static int rlines(struct tailfile *, off_t);
50static inline void tfprint(FILE *fp);
51static int tfqueue(struct tailfile *tf);
52static const struct timespec *tfreopen(struct tailfile *tf);
53
54static 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 */
78void
79forward(struct tailfile *tf, int nfiles, enum STYLE style, off_t origoff)
80{
81 int ch;
82 struct tailfile *ctf, *ltf;
83 struct kevent ke;
84 const struct timespec *ts = NULL((void *)0);
85 int i;
86 int nevents;
87
88 if (nfiles < 1)
89 return;
90
91 if (fflag && (kq = kqueue()) == -1)
92 warn("kqueue");
93
94 for (i = 0; i < nfiles; i++) {
95 off_t off = origoff;
96 if (nfiles > 1)
97 printfname(tf[i].fname);
98
99 switch(style) {
100 case FBYTES:
101 if (off == 0)
102 break;
103 if (S_ISREG(tf[i].sb.st_mode)((tf[i].sb.st_mode & 0170000) == 0100000)) {
104 if (tf[i].sb.st_size < off)
105 off = tf[i].sb.st_size;
106 if (fseeko(tf[i].fp, off, SEEK_SET0) == -1) {
107 ierr(tf[i].fname);
108 return;
109 }
110 } else while (off--)
111 if ((ch = getc(tf[i].fp)(!__isthreaded ? (--(tf[i].fp)->_r < 0 ? __srget(tf[i].
fp) : (int)(*(tf[i].fp)->_p++)) : (getc)(tf[i].fp))
) == EOF(-1)) {
Although the value stored to 'ch' is used in the enclosing expression, the value is never actually read from 'ch'
112 if (ferror(tf[i].fp)(!__isthreaded ? (((tf[i].fp)->_flags & 0x0040) != 0) :
(ferror)(tf[i].fp))
) {
113 ierr(tf[i].fname);
114 return;
115 }
116 break;
117 }
118 break;
119 case FLINES:
120 if (off == 0)
121 break;
122 for (;;) {
123 if ((ch = getc(tf[i].fp)(!__isthreaded ? (--(tf[i].fp)->_r < 0 ? __srget(tf[i].
fp) : (int)(*(tf[i].fp)->_p++)) : (getc)(tf[i].fp))
) == EOF(-1)) {
124 if (ferror(tf[i].fp)(!__isthreaded ? (((tf[i].fp)->_flags & 0x0040) != 0) :
(ferror)(tf[i].fp))
) {
125 ierr(tf[i].fname);
126 return;
127 }
128 break;
129 }
130 if (ch == '\n' && !--off)
131 break;
132 }
133 break;
134 case RBYTES:
135 if (S_ISREG(tf[i].sb.st_mode)((tf[i].sb.st_mode & 0170000) == 0100000)) {
136 if (tf[i].sb.st_size >= off &&
137 fseeko(tf[i].fp, -off, SEEK_END2) == -1) {
138 ierr(tf[i].fname);
139 return;
140 }
141 } else if (off == 0) {
142 while (getc(tf[i].fp)(!__isthreaded ? (--(tf[i].fp)->_r < 0 ? __srget(tf[i].
fp) : (int)(*(tf[i].fp)->_p++)) : (getc)(tf[i].fp))
!= EOF(-1))
143 ;
144 if (ferror(tf[i].fp)(!__isthreaded ? (((tf[i].fp)->_flags & 0x0040) != 0) :
(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)((tf[i].sb.st_mode & 0170000) == 0100000)) {
155 if (!off) {
156 if (fseeko(tf[i].fp, (off_t)0,
157 SEEK_END2) == -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)(!__isthreaded ? (--(tf[i].fp)->_r < 0 ? __srget(tf[i].
fp) : (int)(*(tf[i].fp)->_p++)) : (getc)(tf[i].fp))
!= EOF(-1))
165 ;
166 if (ferror(tf[i].fp)(!__isthreaded ? (((tf[i].fp)->_flags & 0x0040) != 0) :
(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 tfprint(tf[i].fp);
180 if (fflag && tfqueue(&(tf[i])) == -1)
181 warn("Unable to follow %s", tf[i].fname);
182
183 }
184 ltf = &(tf[i-1]);
185
186 (void)fflush(stdout(&__sF[1]));
187 if (!fflag || kq == -1)
188 return;
189
190 while (1) {
191 if ((nevents = kevent(kq, NULL((void *)0), 0, &ke, 1, ts)) <= 0) {
192 if (errno(*__errno()) == EINTR4) {
193 close(kq);
194 return;
195 }
196 }
197
198 ctf = ke.udata;
199 if (nevents > 0) {
200 if (ke.filter == EVFILT_READ(-1)) {
201 if (ctf != ltf) {
202 printfname(ctf->fname);
203 ltf = ctf;
204 }
205 clearerr(ctf->fp)(!__isthreaded ? ((void)((ctf->fp)->_flags &= ~(0x0040
|0x0020))) : (clearerr)(ctf->fp))
;
206 tfprint(ctf->fp);
207 if (ferror(ctf->fp)(!__isthreaded ? (((ctf->fp)->_flags & 0x0040) != 0
) : (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(&__sF[1]));
214 clearerr(ctf->fp)(!__isthreaded ? ((void)((ctf->fp)->_flags &= ~(0x0040
|0x0020))) : (clearerr)(ctf->fp))
;
215 } else if (ke.filter == EVFILT_VNODE(-4)) {
216 if (ke.fflags & (NOTE_DELETE0x0001 | NOTE_RENAME0x0020)) {
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_TRUNCATE0x0080) {
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((void *)0));
234 }
235}
236
237/*
238 * rlines -- display the last offset lines of the file.
239 */
240static int
241rlines(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(-1);
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_SET0) == -1) {
259 ierr(tf->fname);
260 return (1);
261 }
262 if ((ch = getc(tf[0].fp)(!__isthreaded ? (--(tf[0].fp)->_r < 0 ? __srget(tf[0].
fp) : (int)(*(tf[0].fp)->_p++)) : (getc)(tf[0].fp))
) == '\n')
263 off--;
264 else if (ch == EOF(-1)) {
265 if (ferror(tf[0].fp)(!__isthreaded ? (((tf[0].fp)->_flags & 0x0040) != 0) :
(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(-1) && ungetc(ch, tf[0].fp) == EOF(-1)) {
274 ierr(tf->fname);
275 return (1);
276 }
277
278 while (!feof(tf[0].fp)(!__isthreaded ? (((tf[0].fp)->_flags & 0x0020) != 0) :
(feof)(tf[0].fp))
&& (ch = getc(tf[0].fp)(!__isthreaded ? (--(tf[0].fp)->_r < 0 ? __srget(tf[0].
fp) : (int)(*(tf[0].fp)->_p++)) : (getc)(tf[0].fp))
) != EOF(-1))
279 if (putchar(ch)(!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch, (&
__sF[1])))
== EOF(-1))
280 oerr();
281 if (ferror(tf[0].fp)(!__isthreaded ? (((tf[0].fp)->_flags & 0x0040) != 0) :
(ferror)(tf[0].fp))
) {
282 ierr(tf->fname);
283 return (1);
284 }
285
286 return (0);
287}
288
289static inline void
290tfprint(FILE *fp)
291{
292 int ch;
293
294 while (!feof(fp)(!__isthreaded ? (((fp)->_flags & 0x0020) != 0) : (feof
)(fp))
&& (ch = getc(fp)(!__isthreaded ? (--(fp)->_r < 0 ? __srget(fp) : (int)(
*(fp)->_p++)) : (getc)(fp))
) != EOF(-1))
295 if (putchar(ch)(!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch, (&
__sF[1])))
== EOF(-1))
296 oerr();
297}
298
299static int
300tfqueue(struct tailfile *tf)
301{
302 struct kevent ke[2];
303 int i = 1;
304
305 if (kq < 0) {
306 errno(*__errno()) = EBADF9;
307 return -1;
308 }
309
310 EV_SET(&(ke[0]), fileno(tf->fp), EVFILT_READ,do { struct kevent *__kevp = (&(ke[0])); (__kevp)->ident
= ((!__isthreaded ? ((tf->fp)->_file) : (fileno)(tf->
fp))); (__kevp)->filter = ((-1)); (__kevp)->flags = (0x0004
| 0x0001 | 0x0020); (__kevp)->fflags = (0); (__kevp)->
data = (0); (__kevp)->udata = (tf); } while(0)
311 EV_ENABLE | EV_ADD | EV_CLEAR, 0, 0, tf)do { struct kevent *__kevp = (&(ke[0])); (__kevp)->ident
= ((!__isthreaded ? ((tf->fp)->_file) : (fileno)(tf->
fp))); (__kevp)->filter = ((-1)); (__kevp)->flags = (0x0004
| 0x0001 | 0x0020); (__kevp)->fflags = (0); (__kevp)->
data = (0); (__kevp)->udata = (tf); } while(0)
;
312
313 if (S_ISREG(tf->sb.st_mode)((tf->sb.st_mode & 0170000) == 0100000)) {
314 i = 2;
315 EV_SET(&(ke[1]), fileno(tf->fp), EVFILT_VNODE,do { struct kevent *__kevp = (&(ke[1])); (__kevp)->ident
= ((!__isthreaded ? ((tf->fp)->_file) : (fileno)(tf->
fp))); (__kevp)->filter = ((-4)); (__kevp)->flags = (0x0004
| 0x0001 | 0x0020); (__kevp)->fflags = (0x0001 | 0x0020 |
0x0080); (__kevp)->data = (0); (__kevp)->udata = (tf);
} while(0)
316 EV_ENABLE | EV_ADD | EV_CLEAR,do { struct kevent *__kevp = (&(ke[1])); (__kevp)->ident
= ((!__isthreaded ? ((tf->fp)->_file) : (fileno)(tf->
fp))); (__kevp)->filter = ((-4)); (__kevp)->flags = (0x0004
| 0x0001 | 0x0020); (__kevp)->fflags = (0x0001 | 0x0020 |
0x0080); (__kevp)->data = (0); (__kevp)->udata = (tf);
} while(0)
317 NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,do { struct kevent *__kevp = (&(ke[1])); (__kevp)->ident
= ((!__isthreaded ? ((tf->fp)->_file) : (fileno)(tf->
fp))); (__kevp)->filter = ((-4)); (__kevp)->flags = (0x0004
| 0x0001 | 0x0020); (__kevp)->fflags = (0x0001 | 0x0020 |
0x0080); (__kevp)->data = (0); (__kevp)->udata = (tf);
} while(0)
318 0, tf)do { struct kevent *__kevp = (&(ke[1])); (__kevp)->ident
= ((!__isthreaded ? ((tf->fp)->_file) : (fileno)(tf->
fp))); (__kevp)->filter = ((-4)); (__kevp)->flags = (0x0004
| 0x0001 | 0x0020); (__kevp)->fflags = (0x0001 | 0x0020 |
0x0080); (__kevp)->data = (0); (__kevp)->udata = (tf);
} while(0)
;
319 }
320 if (kevent(kq, ke, i, NULL((void *)0), 0, NULL((void *)0)) == -1) {
321 ierr(tf->fname);
322 return -1;
323 }
324 return 0;
325}
326
327#define AFILESINCR8 8
328static const struct timespec *
329tfreopen(struct tailfile *tf) {
330 static struct tailfile **reopen = NULL((void *)0);
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 && !(tf->fp == stdin(&__sF[0])) &&
339 ((stat(tf->fname, &sb) != 0) || sb.st_ino != tf->sb.st_ino)) {
340 if (afiles < ++nfiles) {
341 afiles += AFILESINCR8;
342 treopen = reallocarray(reopen, afiles, sizeof(*reopen));
343 if (treopen)
344 reopen = treopen;
345 else
346 afiles -= AFILESINCR8;
347 }
348 if (nfiles <= afiles) {
349 for (i = 0; i < nfiles - 1; i++)
350 if (strcmp(reopen[i]->fname, tf->fname) == 0)
351 break;
352 if (i < nfiles - 1)
353 nfiles--;
354 else
355 reopen[nfiles-1] = tf;
356 } else {
357 warnx("Lost track of %s", tf->fname);
358 nfiles--;
359 }
360 }
361
362 for (i = 0; i < nfiles; i++) {
363 ttf = reopen[i];
364 if (stat(ttf->fname, &sb) == -1)
365 continue;
366 if (sb.st_ino != ttf->sb.st_ino) {
367 (void) memcpy(&(ttf->sb), &sb, sizeof(ttf->sb));
368 ttf->fp = freopen(ttf->fname, "r", ttf->fp);
369 if (ttf->fp == NULL((void *)0))
370 ierr(ttf->fname);
371 else {
372 warnx("%s has been replaced, reopening.",
373 ttf->fname);
374 tfqueue(ttf);
375 }
376 }
377 reopen[i] = reopen[--nfiles];
378 }
379
380 return nfiles ? &ts : NULL((void *)0);
381}