flush stdout, rest of tail uses write(2)
[unix-history] / usr / src / usr.bin / tail / tail.c
CommitLineData
94bed826 1/*-
0da48e03 2 * Copyright (c) 1991 The Regents of the University of California.
94bed826
KB
3 * All rights reserved.
4 *
0da48e03
KB
5 * This code is derived from software contributed to Berkeley by
6 * Edward Sze-Tyan Wang.
7 *
8 * %sccs.include.redist.c%
22e155fc
DF
9 */
10
11#ifndef lint
12char copyright[] =
0da48e03 13"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
22e155fc 14 All rights reserved.\n";
94bed826 15#endif /* not lint */
22e155fc 16
c9e4db79 17#ifndef lint
fb9b2fa0 18static char sccsid[] = "@(#)tail.c 5.10 (Berkeley) %G%";
94bed826 19#endif /* not lint */
22e155fc 20
0da48e03
KB
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
30int fflag, rflag, rval;
31char *fname;
32
33static void obsolete __P((char **));
34static void usage __P((void));
35
8b48703f 36int
0da48e03
KB
37main(argc, argv)
38 int argc;
8b48703f 39 char *argv[];
15dbf57f 40{
0da48e03
KB
41 struct stat sb;
42 FILE *fp;
43 long off;
44 enum STYLE style;
31919d23 45 int ch, first;
ea8ca20d 46 char *p;
0da48e03 47
ea8ca20d
KB
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 */
fb9b2fa0
KB
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 } \
ea8ca20d 79}
0da48e03 80
ea8ca20d 81 obsolete(argv);
0da48e03
KB
82 style = NOTSET;
83 while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF)
84 switch(ch) {
85 case 'b':
ea8ca20d 86 ARG(512, FBYTES, RBYTES);
0da48e03
KB
87 break;
88 case 'c':
ea8ca20d 89 ARG(1, FBYTES, RBYTES);
0da48e03
KB
90 break;
91 case 'f':
92 fflag = 1;
93 break;
94 case 'n':
ea8ca20d 95 ARG(1, FLINES, RLINES);
0da48e03
KB
96 break;
97 case 'r':
98 rflag = 1;
99 break;
100 case '?':
101 default:
102 usage();
15dbf57f 103 }
0da48e03
KB
104 argc -= optind;
105 argv += optind;
106
8b48703f
KB
107 if (fflag && argc > 1)
108 err(1, "-f option only appropriate for a single file");
109
0da48e03 110 /*
ea8ca20d
KB
111 * If displaying in reverse, don't permit follow option, and convert
112 * style values.
0da48e03
KB
113 */
114 if (rflag) {
115 if (fflag)
116 usage();
0da48e03
KB
117 if (style == FBYTES)
118 style = RBYTES;
8b48703f 119 else if (style == FLINES)
0da48e03 120 style = RLINES;
15dbf57f 121 }
0da48e03 122
ea8ca20d
KB
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
31919d23
KB
136 if (*argv)
137 for (first = 1; fname = *argv++;) {
8b48703f
KB
138 if ((fp = fopen(fname, "r")) == NULL ||
139 fstat(fileno(fp), &sb)) {
31919d23
KB
140 ierr();
141 continue;
142 }
143 if (argc > 1) {
144 (void)printf("%s==> %s <==\n",
145 first ? "" : "\n", fname);
146 first = 0;
fb9b2fa0 147 (void)fflush(stdout);
31919d23
KB
148 }
149
150 if (rflag)
151 reverse(fp, style, off, &sb);
152 else
153 forward(fp, style, off, &sb);
8b48703f 154 (void)fclose(fp);
31919d23
KB
155 }
156 else {
0da48e03 157 fname = "stdin";
0da48e03 158
8b48703f 159 if (fstat(fileno(stdin), &sb)) {
31919d23
KB
160 ierr();
161 exit(1);
162 }
0da48e03 163
31919d23
KB
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 */
8b48703f
KB
168 if (lseek(fileno(stdin), 0L, SEEK_CUR) == -1 &&
169 errno == ESPIPE) {
31919d23
KB
170 errno = 0;
171 fflag = 0; /* POSIX.2 requires this. */
172 }
0da48e03 173
31919d23 174 if (rflag)
8b48703f 175 reverse(stdin, style, off, &sb);
31919d23 176 else
8b48703f 177 forward(stdin, style, off, &sb);
31919d23 178 }
0da48e03
KB
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 */
187static void
188obsolete(argv)
8b48703f 189 char *argv[];
0da48e03
KB
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)
31919d23 211 err(1, "%s", strerror(errno));
0da48e03
KB
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;
94dde571
KB
220 if (*t == 'f' || *t == 'r') {
221 *p++ = *t;
222 *t-- = '\0';
223 }
0da48e03
KB
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:
31919d23 241 err(1, "illegal option -- %s", *argv);
15dbf57f 242 }
0da48e03
KB
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 }
15dbf57f 267 }
15dbf57f
BJ
268}
269
0da48e03
KB
270static void
271usage()
272{
273 (void)fprintf(stderr,
8b48703f 274 "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n");
0da48e03 275 exit(1);
15dbf57f 276}