xref: /csrg-svn/usr.bin/tail/tail.c (revision 50453)
148313Sbostic /*-
2*50453Sbostic  * Copyright (c) 1991 The Regents of the University of California.
348313Sbostic  * All rights reserved.
448313Sbostic  *
5*50453Sbostic  * This code is derived from software contributed to Berkeley by
6*50453Sbostic  * Edward Sze-Tyan Wang.
7*50453Sbostic  *
8*50453Sbostic  * %sccs.include.redist.c%
921579Sdist  */
1021579Sdist 
1117708Sbloom #ifndef lint
1221579Sdist char copyright[] =
13*50453Sbostic "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
1421579Sdist  All rights reserved.\n";
1548313Sbostic #endif /* not lint */
1621579Sdist 
1721579Sdist #ifndef lint
18*50453Sbostic static char sccsid[] = "@(#)tail.c	5.5 (Berkeley) 07/21/91";
1948313Sbostic #endif /* not lint */
2021579Sdist 
21*50453Sbostic #include <sys/types.h>
22*50453Sbostic #include <sys/stat.h>
23*50453Sbostic #include <errno.h>
24*50453Sbostic #include <unistd.h>
25*50453Sbostic #include <stdio.h>
26*50453Sbostic #include <stdlib.h>
27*50453Sbostic #include <string.h>
28*50453Sbostic #include "extern.h"
291214Sroot 
30*50453Sbostic int fflag, rflag, rval;
31*50453Sbostic char *fname;
321214Sroot 
33*50453Sbostic static void obsolete __P((char **));
34*50453Sbostic static void usage __P((void));
351214Sroot 
36*50453Sbostic main(argc, argv)
37*50453Sbostic 	int argc;
38*50453Sbostic 	char **argv;
391214Sroot {
40*50453Sbostic 	struct stat sb;
41*50453Sbostic 	FILE *fp;
42*50453Sbostic 	long off;
43*50453Sbostic 	enum STYLE style;
44*50453Sbostic 	int ch;
45*50453Sbostic 	char *p, *num;
461214Sroot 
47*50453Sbostic 	obsolete(argv);
48*50453Sbostic 
49*50453Sbostic 	style = NOTSET;
50*50453Sbostic 	while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF)
51*50453Sbostic 		switch(ch) {
52*50453Sbostic 		case 'b':
53*50453Sbostic 			if (style)
54*50453Sbostic 				usage();
55*50453Sbostic 			off = strtol(num = optarg, &p, 10) * 512;
56*50453Sbostic 			if (*p)
57*50453Sbostic 				err("illegal offset -- %s", optarg);
58*50453Sbostic 			style = *num == '+' ? FBYTES : RBYTES;
59*50453Sbostic 			break;
60*50453Sbostic 		case 'c':
61*50453Sbostic 			if (style)
62*50453Sbostic 				usage();
63*50453Sbostic 			off = strtol(num = optarg, &p, 10);
64*50453Sbostic 			if (*p)
65*50453Sbostic 				err("illegal offset -- %s", optarg);
66*50453Sbostic 			style = *num == '+' ? FBYTES : RBYTES;
67*50453Sbostic 			break;
68*50453Sbostic 		case 'f':
69*50453Sbostic 			fflag = 1;
70*50453Sbostic 			break;
71*50453Sbostic 		case 'n':
72*50453Sbostic 			if (style)
73*50453Sbostic 				usage();
74*50453Sbostic 			off = strtol(num = optarg, &p, 10);
75*50453Sbostic 			if (*p)
76*50453Sbostic 				err("illegal offset -- %s", optarg);
77*50453Sbostic 			style = *num == '+' ? FLINES : RLINES;
78*50453Sbostic 			break;
79*50453Sbostic 		case 'r':
80*50453Sbostic 			rflag = 1;
81*50453Sbostic 			break;
82*50453Sbostic 		case '?':
83*50453Sbostic 		default:
84*50453Sbostic 			usage();
851214Sroot 		}
86*50453Sbostic 	argc -= optind;
87*50453Sbostic 	argv += optind;
88*50453Sbostic 
89*50453Sbostic 	/*
90*50453Sbostic 	 * Don't permit follow option if displaying in reverse.  An offset
91*50453Sbostic 	 * with an explicit leading minus is meaningless.
92*50453Sbostic 	 */
93*50453Sbostic 	if (rflag) {
94*50453Sbostic 		if (fflag)
95*50453Sbostic 			usage();
96*50453Sbostic 		if (style && *num == '-')
97*50453Sbostic 			err("illegal offset for -r option -- %s", num);
98*50453Sbostic 		if (style == FBYTES)
99*50453Sbostic 			style = RBYTES;
100*50453Sbostic 		if (style == FLINES)
101*50453Sbostic 			style = RLINES;
1021214Sroot 	}
1031214Sroot 
104*50453Sbostic 	if (fname = *argv) {
105*50453Sbostic 		if ((fp = fopen(fname, "r")) == NULL)
106*50453Sbostic 			ierr();
107*50453Sbostic 	} else {
108*50453Sbostic 		fp = stdin;
109*50453Sbostic 		fname = "stdin";
1101214Sroot 	}
1111214Sroot 
112*50453Sbostic 	if (fstat(fileno(fp), &sb))
113*50453Sbostic 		ierr();
1141214Sroot 
115*50453Sbostic 	/*
116*50453Sbostic 	 * Determine if input is a pipe.  4.4BSD will set the SOCKET
117*50453Sbostic 	 * bit in the st_mode field for pipes.  Fix this then.
118*50453Sbostic 	 */
119*50453Sbostic 	if (lseek(fileno(fp), 0L, SEEK_CUR) == -1 && errno == ESPIPE) {
120*50453Sbostic 		errno = 0;
121*50453Sbostic 		fflag = 0;		/* POSIX.2 requires this. */
122*50453Sbostic 	}
123*50453Sbostic 
124*50453Sbostic 	/*
125*50453Sbostic 	 * Tail's options are weird.  First, -n10 is the same as -n-10, not
126*50453Sbostic 	 * -n+10.  Second, the number options for the -r option specify the
127*50453Sbostic 	 * number of bytes/chars/lines that get displayed, not the offset from
128*50453Sbostic 	 * the beginning/end of the file.  Finally, the default for -r is the
129*50453Sbostic 	 * entire file, not 10 lines.
130*50453Sbostic 	 */
131*50453Sbostic 	if (!style)
132*50453Sbostic 		if (rflag) {
133*50453Sbostic 			off = 0;
134*50453Sbostic 			style = REVERSE;
135*50453Sbostic 		} else {
136*50453Sbostic 			off = 10;
137*50453Sbostic 			style = RLINES;
1381214Sroot 		}
139*50453Sbostic 	else if (off < 0)
140*50453Sbostic 		off = -off;
141*50453Sbostic 
142*50453Sbostic 	if (rflag)
143*50453Sbostic 		reverse(fp, style, off, &sb);
144*50453Sbostic 	else
145*50453Sbostic 		forward(fp, style, off, &sb);
146*50453Sbostic 	exit(rval);
147*50453Sbostic }
148*50453Sbostic 
149*50453Sbostic /*
150*50453Sbostic  * Convert the obsolete argument form into something that getopt can handle.
151*50453Sbostic  * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
152*50453Sbostic  * the option argument for a -b, -c or -n option gets converted.
153*50453Sbostic  */
154*50453Sbostic static void
155*50453Sbostic obsolete(argv)
156*50453Sbostic 	char **argv;
157*50453Sbostic {
158*50453Sbostic 	register char *ap, *p, *t;
159*50453Sbostic 	int len;
160*50453Sbostic 	char *start;
161*50453Sbostic 
162*50453Sbostic 	while (ap = *++argv) {
163*50453Sbostic 		/* Return if "--" or not an option of any form. */
164*50453Sbostic 		if (ap[0] != '-') {
165*50453Sbostic 			if (ap[0] != '+')
166*50453Sbostic 				return;
167*50453Sbostic 		} else if (ap[1] == '-')
168*50453Sbostic 			return;
169*50453Sbostic 
170*50453Sbostic 		switch(*++ap) {
171*50453Sbostic 		/* Old-style option. */
172*50453Sbostic 		case '0': case '1': case '2': case '3': case '4':
173*50453Sbostic 		case '5': case '6': case '7': case '8': case '9':
174*50453Sbostic 
175*50453Sbostic 			/* Malloc space for dash, new option and argument. */
176*50453Sbostic 			len = strlen(*argv);
177*50453Sbostic 			if ((start = p = malloc(len + 3)) == NULL)
178*50453Sbostic 				err("%s", strerror(errno));
179*50453Sbostic 			*p++ = '-';
180*50453Sbostic 
181*50453Sbostic 			/*
182*50453Sbostic 			 * Go to the end of the option argument.  Save off any
183*50453Sbostic 			 * trailing options (-3lf) and translate any trailing
184*50453Sbostic 			 * output style characters.
185*50453Sbostic 			 */
186*50453Sbostic 			t = *argv + len - 1;
187*50453Sbostic 			if (*t == 'f' || *t == 'r')
188*50453Sbostic 				*p++ = *t--;
189*50453Sbostic 			switch(*t) {
190*50453Sbostic 			case 'b':
191*50453Sbostic 				*p++ = 'b';
192*50453Sbostic 				*t = '\0';
193*50453Sbostic 				break;
194*50453Sbostic 			case 'c':
195*50453Sbostic 				*p++ = 'c';
196*50453Sbostic 				*t = '\0';
197*50453Sbostic 				break;
198*50453Sbostic 			case 'l':
199*50453Sbostic 				*t = '\0';
200*50453Sbostic 				/* FALLTHROUGH */
201*50453Sbostic 			case '0': case '1': case '2': case '3': case '4':
202*50453Sbostic 			case '5': case '6': case '7': case '8': case '9':
203*50453Sbostic 				*p++ = 'n';
204*50453Sbostic 				break;
205*50453Sbostic 			default:
206*50453Sbostic 				err("illegal option -- %s", *argv);
2071214Sroot 			}
208*50453Sbostic 			*p++ = *argv[0];
209*50453Sbostic 			(void)strcpy(p, ap);
210*50453Sbostic 			*argv = start;
211*50453Sbostic 			continue;
2121214Sroot 
213*50453Sbostic 		/*
214*50453Sbostic 		 * Options w/ arguments, skip the argument and continue
215*50453Sbostic 		 * with the next option.
216*50453Sbostic 		 */
217*50453Sbostic 		case 'b':
218*50453Sbostic 		case 'c':
219*50453Sbostic 		case 'n':
220*50453Sbostic 			if (!ap[1])
221*50453Sbostic 				++argv;
222*50453Sbostic 			/* FALLTHROUGH */
223*50453Sbostic 		/* Options w/o arguments, continue with the next option. */
224*50453Sbostic 		case 'f':
225*50453Sbostic 		case 'r':
226*50453Sbostic 			continue;
2271214Sroot 
228*50453Sbostic 		/* Illegal option, return and let getopt handle it. */
229*50453Sbostic 		default:
230*50453Sbostic 			return;
2311214Sroot 		}
2321214Sroot 	}
2331214Sroot }
2341214Sroot 
235*50453Sbostic static void
236*50453Sbostic usage()
237*50453Sbostic {
238*50453Sbostic 	(void)fprintf(stderr,
239*50453Sbostic 	    "usage: tail [-f | -r] [-b # | -c # | -n #] [file]\n");
240*50453Sbostic 	exit(1);
2411214Sroot }
242