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