GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: tail.c,v 1.21 2016/02/03 12:23:57 halex Exp $ */ |
||
2 |
|||
3 |
/*- |
||
4 |
* Copyright (c) 1991, 1993 |
||
5 |
* The Regents of the University of California. All rights reserved. |
||
6 |
* |
||
7 |
* This code is derived from software contributed to Berkeley by |
||
8 |
* Edward Sze-Tyan Wang. |
||
9 |
* |
||
10 |
* Redistribution and use in source and binary forms, with or without |
||
11 |
* modification, are permitted provided that the following conditions |
||
12 |
* are met: |
||
13 |
* 1. Redistributions of source code must retain the above copyright |
||
14 |
* notice, this list of conditions and the following disclaimer. |
||
15 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
16 |
* notice, this list of conditions and the following disclaimer in the |
||
17 |
* documentation and/or other materials provided with the distribution. |
||
18 |
* 3. Neither the name of the University nor the names of its contributors |
||
19 |
* may be used to endorse or promote products derived from this software |
||
20 |
* without specific prior written permission. |
||
21 |
* |
||
22 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
23 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
24 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
25 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
26 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
27 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
28 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
29 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
30 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
31 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
32 |
* SUCH DAMAGE. |
||
33 |
*/ |
||
34 |
|||
35 |
#include <sys/types.h> |
||
36 |
#include <sys/stat.h> |
||
37 |
|||
38 |
#include <err.h> |
||
39 |
#include <errno.h> |
||
40 |
#include <stdio.h> |
||
41 |
#include <stdlib.h> |
||
42 |
#include <string.h> |
||
43 |
#include <unistd.h> |
||
44 |
|||
45 |
#include "extern.h" |
||
46 |
|||
47 |
int fflag, rflag, rval; |
||
48 |
int is_stdin; |
||
49 |
|||
50 |
static void obsolete(char **); |
||
51 |
static void usage(void); |
||
52 |
|||
53 |
int |
||
54 |
main(int argc, char *argv[]) |
||
55 |
{ |
||
56 |
struct tailfile *tf; |
||
57 |
off_t off = 0; |
||
58 |
enum STYLE style; |
||
59 |
int ch; |
||
60 |
int i; |
||
61 |
4126 |
char *p; |
|
62 |
|||
63 |
✗✓ | 2063 |
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
64 |
err(1, "pledge"); |
||
65 |
|||
66 |
/* |
||
67 |
* Tail's options are weird. First, -n10 is the same as -n-10, not |
||
68 |
* -n+10. Second, the number options are 1 based and not offsets, |
||
69 |
* so -n+1 is the first line, and -c-1 is the last byte. Third, the |
||
70 |
* number options for the -r option specify the number of things that |
||
71 |
* get displayed, not the starting point in the file. The one major |
||
72 |
* incompatibility in this version as compared to historical versions |
||
73 |
* is that the 'r' option couldn't be modified by the -lbc options, |
||
74 |
* i.e. it was always done in lines. This version treats -rc as a |
||
75 |
* number of characters in reverse order. Finally, the default for |
||
76 |
* -r is the entire file, not 10 lines. |
||
77 |
*/ |
||
78 |
#define ARG(units, forward, backward) { \ |
||
79 |
if (style) \ |
||
80 |
usage(); \ |
||
81 |
off = strtoll(optarg, &p, 10) * (units); \ |
||
82 |
if (*p) \ |
||
83 |
errx(1, "illegal offset -- %s", optarg); \ |
||
84 |
switch(optarg[0]) { \ |
||
85 |
case '+': \ |
||
86 |
if (off) \ |
||
87 |
off -= (units); \ |
||
88 |
style = (forward); \ |
||
89 |
break; \ |
||
90 |
case '-': \ |
||
91 |
off = -off; \ |
||
92 |
/* FALLTHROUGH */ \ |
||
93 |
default: \ |
||
94 |
style = (backward); \ |
||
95 |
break; \ |
||
96 |
} \ |
||
97 |
} |
||
98 |
|||
99 |
2063 |
obsolete(argv); |
|
100 |
style = NOTSET; |
||
101 |
✓✓ | 6189 |
while ((ch = getopt(argc, argv, "b:c:fn:r")) != -1) |
102 |
✗✗✗✓ ✗✗ |
2063 |
switch(ch) { |
103 |
case 'b': |
||
104 |
ARG(512, FBYTES, RBYTES); |
||
105 |
break; |
||
106 |
case 'c': |
||
107 |
ARG(1, FBYTES, RBYTES); |
||
108 |
break; |
||
109 |
case 'f': |
||
110 |
fflag = 1; |
||
111 |
break; |
||
112 |
case 'n': |
||
113 |
✗✓✗✓ ✓✓✓✓ ✗ |
12376 |
ARG(1, FLINES, RLINES); |
114 |
break; |
||
115 |
case 'r': |
||
116 |
rflag = 1; |
||
117 |
break; |
||
118 |
case '?': |
||
119 |
default: |
||
120 |
usage(); |
||
121 |
} |
||
122 |
2063 |
argc -= optind; |
|
123 |
2063 |
argv += optind; |
|
124 |
|||
125 |
/* |
||
126 |
* If displaying in reverse, don't permit follow option, and convert |
||
127 |
* style values. |
||
128 |
*/ |
||
129 |
✗✓ | 2063 |
if (rflag) { |
130 |
if (fflag) |
||
131 |
usage(); |
||
132 |
if (style == FBYTES) |
||
133 |
style = RBYTES; |
||
134 |
else if (style == FLINES) |
||
135 |
style = RLINES; |
||
136 |
} |
||
137 |
|||
138 |
/* |
||
139 |
* If style not specified, the default is the whole file for -r, and |
||
140 |
* the last 10 lines if not -r. |
||
141 |
*/ |
||
142 |
✗✓ | 2063 |
if (style == NOTSET) { |
143 |
if (rflag) { |
||
144 |
off = 0; |
||
145 |
style = REVERSE; |
||
146 |
} else { |
||
147 |
off = 10; |
||
148 |
style = RLINES; |
||
149 |
} |
||
150 |
} |
||
151 |
|||
152 |
✗✓ | 2063 |
if ((tf = reallocarray(NULL, argc ? argc : 1, sizeof(*tf))) == NULL) |
153 |
err(1, "reallocarray"); |
||
154 |
|||
155 |
✗✓ | 2063 |
if (argc) { |
156 |
for (i = 0; *argv; i++) { |
||
157 |
tf[i].fname = *argv++; |
||
158 |
if ((tf[i].fp = fopen(tf[i].fname, "r")) == NULL || |
||
159 |
fstat(fileno(tf[i].fp), &(tf[i].sb))) { |
||
160 |
ierr(tf[i].fname); |
||
161 |
i--; |
||
162 |
continue; |
||
163 |
} |
||
164 |
} |
||
165 |
if (rflag) |
||
166 |
reverse(tf, i, style, off); |
||
167 |
else |
||
168 |
forward(tf, i, style, off); |
||
169 |
} |
||
170 |
else { |
||
171 |
✗✓ | 2063 |
if (pledge("stdio flock rpath cpath wpath", NULL) == -1) |
172 |
err(1, "pledge"); |
||
173 |
|||
174 |
2063 |
tf[0].fname = "stdin"; |
|
175 |
2063 |
tf[0].fp = stdin; |
|
176 |
2063 |
is_stdin = 1; |
|
177 |
|||
178 |
✓✗✗✓ |
6189 |
if (fstat(fileno(stdin), &(tf[0].sb))) { |
179 |
ierr(tf[0].fname); |
||
180 |
exit(1); |
||
181 |
} |
||
182 |
|||
183 |
/* |
||
184 |
* Determine if input is a pipe. 4.4BSD will set the SOCKET |
||
185 |
* bit in the st_mode field for pipes. Fix this then. |
||
186 |
*/ |
||
187 |
✓✗✓✗ ✓✗ |
8252 |
if (lseek(fileno(tf[0].fp), (off_t)0, SEEK_CUR) == -1 && |
188 |
2063 |
errno == ESPIPE) { |
|
189 |
2063 |
errno = 0; |
|
190 |
2063 |
fflag = 0; /* POSIX.2 requires this. */ |
|
191 |
2063 |
} |
|
192 |
|||
193 |
✗✓ | 2063 |
if (rflag) |
194 |
reverse(tf, 1, style, off); |
||
195 |
else |
||
196 |
2063 |
forward(tf, 1, style, off); |
|
197 |
} |
||
198 |
exit(rval); |
||
199 |
} |
||
200 |
|||
201 |
/* |
||
202 |
* Convert the obsolete argument form into something that getopt can handle. |
||
203 |
* This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't |
||
204 |
* the option argument for a -b, -c or -n option gets converted. |
||
205 |
*/ |
||
206 |
static void |
||
207 |
obsolete(char *argv[]) |
||
208 |
{ |
||
209 |
char *ap, *p, *t; |
||
210 |
size_t len; |
||
211 |
char *start; |
||
212 |
|||
213 |
✓✓ | 8252 |
while ((ap = *++argv)) { |
214 |
/* Return if "--" or not an option of any form. */ |
||
215 |
✗✓ | 2063 |
if (ap[0] != '-') { |
216 |
if (ap[0] != '+') |
||
217 |
return; |
||
218 |
✗✓ | 2063 |
} else if (ap[1] == '-') |
219 |
return; |
||
220 |
|||
221 |
✗✓✗✗ ✗✗✗✗ ✗✗✗✓ ✗✗✓✗ |
4013 |
switch(*++ap) { |
222 |
/* Old-style option. */ |
||
223 |
case '0': case '1': case '2': case '3': case '4': |
||
224 |
case '5': case '6': case '7': case '8': case '9': |
||
225 |
|||
226 |
/* Malloc space for dash, new option and argument. */ |
||
227 |
113 |
len = strlen(*argv); |
|
228 |
✗✓ | 113 |
if ((start = p = malloc(len + 4)) == NULL) |
229 |
err(1, NULL); |
||
230 |
113 |
*p++ = '-'; |
|
231 |
|||
232 |
/* |
||
233 |
* Go to the end of the option argument. Save off any |
||
234 |
* trailing options (-3lf) and translate any trailing |
||
235 |
* output style characters. |
||
236 |
*/ |
||
237 |
113 |
t = *argv + len - 1; |
|
238 |
✓✗✗✓ |
226 |
if (*t == 'f' || *t == 'r') { |
239 |
*p++ = *t; |
||
240 |
*t-- = '\0'; |
||
241 |
} |
||
242 |
✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✗ |
113 |
switch(*t) { |
243 |
case 'b': |
||
244 |
*p++ = 'b'; |
||
245 |
*t = '\0'; |
||
246 |
break; |
||
247 |
case 'c': |
||
248 |
*p++ = 'c'; |
||
249 |
*t = '\0'; |
||
250 |
break; |
||
251 |
case 'l': |
||
252 |
*t = '\0'; |
||
253 |
/* FALLTHROUGH */ |
||
254 |
case '0': case '1': case '2': case '3': case '4': |
||
255 |
case '5': case '6': case '7': case '8': case '9': |
||
256 |
113 |
*p++ = 'n'; |
|
257 |
113 |
break; |
|
258 |
default: |
||
259 |
errx(1, "illegal option -- %s", *argv); |
||
260 |
} |
||
261 |
113 |
*p++ = *argv[0]; |
|
262 |
113 |
(void)strlcpy(p, ap, start + len + 4 - p); |
|
263 |
113 |
*argv = start; |
|
264 |
113 |
continue; |
|
265 |
|||
266 |
/* |
||
267 |
* Options w/ arguments, skip the argument and continue |
||
268 |
* with the next option. |
||
269 |
*/ |
||
270 |
case 'b': |
||
271 |
case 'c': |
||
272 |
case 'n': |
||
273 |
✗✓ | 1950 |
if (!ap[1]) |
274 |
1950 |
++argv; |
|
275 |
/* FALLTHROUGH */ |
||
276 |
/* Options w/o arguments, continue with the next option. */ |
||
277 |
case 'f': |
||
278 |
case 'r': |
||
279 |
continue; |
||
280 |
|||
281 |
/* Illegal option, return and let getopt handle it. */ |
||
282 |
default: |
||
283 |
return; |
||
284 |
} |
||
285 |
} |
||
286 |
4126 |
} |
|
287 |
|||
288 |
static void |
||
289 |
usage(void) |
||
290 |
{ |
||
291 |
(void)fprintf(stderr, |
||
292 |
"usage: tail [-f | -r] " |
||
293 |
"[-b number | -c number | -n number | -number] [file ...]\n"); |
||
294 |
exit(1); |
||
295 |
} |
Generated by: GCOVR (Version 3.3) |