1 /* $OpenBSD: tail.c,v 1.12 2004/02/16 19:48:21 otto 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 __COPYRIGHT("@(#) Copyright (c) 1991, 1993\n\
48 The Regents of the University of California. All rights reserved.\n");
49 __SCCSID("@(#)tail.c 8.1 (Berkeley) 6/6/93");
50 __RCSID("$MirOS: src/usr.bin/tail/tail.c,v 1.3 2012/05/20 15:13:18 tg Exp $");
51
52 int fflag, rflag, rval;
53 const char *fname;
54 int is_stdin;
55
56 static void obsolete(char **);
57 static void usage(void) __dead;
58
59 int
main(int argc,char * argv[])60 main(int argc, char *argv[])
61 {
62 struct stat sb;
63 FILE *fp;
64 off_t off = 0;
65 enum STYLE style;
66 int ch, first;
67 char *p;
68
69 /*
70 * Tail's options are weird. First, -n10 is the same as -n-10, not
71 * -n+10. Second, the number options are 1 based and not offsets,
72 * so -n+1 is the first line, and -c-1 is the last byte. Third, the
73 * number options for the -r option specify the number of things that
74 * get displayed, not the starting point in the file. The one major
75 * incompatibility in this version as compared to historical versions
76 * is that the 'r' option couldn't be modified by the -lbc options,
77 * i.e. it was always done in lines. This version treats -rc as a
78 * number of characters in reverse order. Finally, the default for
79 * -r is the entire file, not 10 lines.
80 */
81 #define ARG(units, forward, backward) { \
82 if (style) \
83 usage(); \
84 off = strtoll(optarg, &p, 10) * (units); \
85 if (*p) \
86 errx(1, "illegal offset -- %s", optarg); \
87 switch(optarg[0]) { \
88 case '+': \
89 if (off) \
90 off -= (units); \
91 style = (forward); \
92 break; \
93 case '-': \
94 off = -off; \
95 /* FALLTHROUGH */ \
96 default: \
97 style = (backward); \
98 break; \
99 } \
100 }
101
102 obsolete(argv);
103 style = NOTSET;
104 while ((ch = getopt(argc, argv, "b:c:Ffn:r")) != -1)
105 switch(ch) {
106 case 'b':
107 ARG(512, FBYTES, RBYTES);
108 break;
109 case 'c':
110 ARG(1, FBYTES, RBYTES);
111 break;
112 case 'F':
113 setlinebuf(stdout);
114 case 'f':
115 fflag = 1;
116 break;
117 case 'n':
118 ARG(1, FLINES, RLINES);
119 break;
120 case 'r':
121 rflag = 1;
122 break;
123 case '?':
124 default:
125 usage();
126 }
127 argc -= optind;
128 argv += optind;
129
130 if (fflag && argc > 1)
131 errx(1, "-f option only appropriate for a single file");
132
133 /*
134 * If displaying in reverse, don't permit follow option, and convert
135 * style values.
136 */
137 if (rflag) {
138 if (fflag)
139 usage();
140 if (style == FBYTES)
141 style = RBYTES;
142 else if (style == FLINES)
143 style = RLINES;
144 }
145
146 /*
147 * If style not specified, the default is the whole file for -r, and
148 * the last 10 lines if not -r.
149 */
150 if (style == NOTSET) {
151 if (rflag) {
152 off = 0;
153 style = REVERSE;
154 } else {
155 off = 10;
156 style = RLINES;
157 }
158 }
159
160 if (*argv)
161 for (first = 1; (fname = *argv++);) {
162 if ((fp = fopen(fname, "r")) == NULL ||
163 fstat(fileno(fp), &sb)) {
164 ierr();
165 continue;
166 }
167 if (argc > 1) {
168 (void)printf("%s==> %s <==\n",
169 first ? "" : "\n", fname);
170 first = 0;
171 (void)fflush(stdout);
172 }
173
174 if (rflag)
175 reverse(fp, style, off, &sb);
176 else
177 forward(fp, style, off, &sb);
178 (void)fclose(fp);
179 }
180 else {
181 fname = "stdin";
182 is_stdin = 1;
183
184 if (fstat(fileno(stdin), &sb)) {
185 ierr();
186 exit(1);
187 }
188
189 /*
190 * Determine if input is a pipe. 4.4BSD will set the SOCKET
191 * bit in the st_mode field for pipes. Fix this then.
192 */
193 if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
194 errno == ESPIPE) {
195 errno = 0;
196 fflag = 0; /* POSIX.2 requires this. */
197 }
198
199 if (rflag)
200 reverse(stdin, style, off, &sb);
201 else
202 forward(stdin, style, off, &sb);
203 }
204 exit(rval);
205 }
206
207 /*
208 * Convert the obsolete argument form into something that getopt can handle.
209 * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
210 * the option argument for a -b, -c or -n option gets converted.
211 */
212 static void
obsolete(char * argv[])213 obsolete(char *argv[])
214 {
215 char *ap, *p, *t;
216 int len;
217 char *start;
218
219 while ((ap = *++argv)) {
220 /* Return if "--" or not an option of any form. */
221 if (ap[0] != '-') {
222 if (ap[0] != '+')
223 return;
224 } else if (ap[1] == '-')
225 return;
226
227 switch(*++ap) {
228 /* Old-style option. */
229 case '0': case '1': case '2': case '3': case '4':
230 case '5': case '6': case '7': case '8': case '9':
231
232 /* Malloc space for dash, new option and argument. */
233 len = strlen(*argv);
234 if ((start = p = malloc(len + 4)) == NULL)
235 err(1, NULL);
236 *p++ = '-';
237
238 /*
239 * Go to the end of the option argument. Save off any
240 * trailing options (-3lf) and translate any trailing
241 * output style characters.
242 */
243 t = *argv + len - 1;
244 if (*t == 'f' || *t == 'r') {
245 *p++ = *t;
246 *t-- = '\0';
247 }
248 switch(*t) {
249 case 'b':
250 *p++ = 'b';
251 *t = '\0';
252 break;
253 case 'c':
254 *p++ = 'c';
255 *t = '\0';
256 break;
257 case 'l':
258 *t = '\0';
259 /* FALLTHROUGH */
260 case '0': case '1': case '2': case '3': case '4':
261 case '5': case '6': case '7': case '8': case '9':
262 *p++ = 'n';
263 break;
264 default:
265 errx(1, "illegal option -- %s", *argv);
266 }
267 *p++ = *argv[0];
268 (void)strlcpy(p, ap, start + len + 4 - p);
269 *argv = start;
270 continue;
271
272 /*
273 * Options w/ arguments, skip the argument and continue
274 * with the next option.
275 */
276 case 'b':
277 case 'c':
278 case 'n':
279 if (!ap[1])
280 ++argv;
281 /* FALLTHROUGH */
282 /* Options w/o arguments, continue with the next option. */
283 case 'f':
284 case 'r':
285 continue;
286
287 /* Illegal option, return and let getopt handle it. */
288 default:
289 return;
290 }
291 }
292 }
293
294 static void
usage(void)295 usage(void)
296 {
297 (void)fprintf(stderr,
298 "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n");
299 exit(1);
300 }
301