| 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  | 
    { | 
    ||
81  | 
    int ch;  | 
    ||
82  | 
    struct tailfile *ctf, *ltf;  | 
    ||
83  | 
    988  | 
    struct kevent ke;  | 
    |
84  | 
    const struct timespec *ts = NULL;  | 
    ||
85  | 
    int i;  | 
    ||
86  | 
    int nevents;  | 
    ||
87  | 
    |||
88  | 
    ✗✓ | 494  | 
    if (nfiles < 1)  | 
    
89  | 
    return;  | 
    ||
90  | 
    |||
91  | 
    ✗✓✗✗ | 
    494  | 
    if (fflag && (kq = kqueue()) < 0)  | 
    
92  | 
    		warn("kqueue"); | 
    ||
93  | 
    |||
94  | 
    ✓✓ | 1976  | 
    	for (i = 0; i < nfiles; i++) { | 
    
95  | 
    off_t off = origoff;  | 
    ||
96  | 
    ✗✓ | 494  | 
    if (nfiles > 1)  | 
    
97  | 
    printfname(tf[i].fname);  | 
    ||
98  | 
    |||
99  | 
    ✗✓✗✓ ✗  | 
    494  | 
    		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  | 
    ✓✗ | 425  | 
    if (off == 0)  | 
    
121  | 
    break;  | 
    ||
122  | 
    7605  | 
    			for (;;) { | 
    |
123  | 
    ✓✗✓✓ ✗✓  | 
    30420  | 
    				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  | 
    ✓✓✗✓ | 
    8030  | 
    if (ch == '\n' && !--off)  | 
    
131  | 
    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  | 
    ✗✓ | 69  | 
    			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  | 
    ✗✓ | 69  | 
    			} 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  | 
    ✗✓ | 69  | 
    if (lines(&(tf[i]), off))  | 
    
172  | 
    return;  | 
    ||
173  | 
    }  | 
    ||
174  | 
    break;  | 
    ||
175  | 
    default:  | 
    ||
176  | 
    err(1, "Unsupported style");  | 
    ||
177  | 
    }  | 
    ||
178  | 
    |||
179  | 
    494  | 
    tfprint(tf[i].fp);  | 
    |
180  | 
    ✗✓✗✗ | 
    494  | 
    if (fflag && tfqueue(&(tf[i])) == -1)  | 
    
181  | 
    			warn("Unable to follow %s", tf[i].fname); | 
    ||
182  | 
    |||
183  | 
    ✓✗ | 494  | 
    }  | 
    
184  | 
    494  | 
    ltf = &(tf[i-1]);  | 
    |
185  | 
    |||
186  | 
    494  | 
    (void)fflush(stdout);  | 
    |
187  | 
    ✓✗ | 494  | 
    if (!fflag || kq < 0)  | 
    
188  | 
    494  | 
    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  | 
    494  | 
    }  | 
    |
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  | 
    { | 
    ||
292  | 
    int ch;  | 
    ||
293  | 
    |||
294  | 
    ✓✗✓✓ ✗✗✓✗ ✓✓✓✓  | 
    72976  | 
    while (!feof(fp) && (ch = getc(fp)) != EOF)  | 
    
295  | 
    ✓✗✗✓ | 
    29700  | 
    if (putchar(ch) == EOF)  | 
    
296  | 
    oerr();  | 
    ||
297  | 
    494  | 
    }  | 
    |
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) |