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