tail.c revision 48566
1275970Scy/*-
2275970Scy * Copyright (c) 1991, 1993
3275970Scy *	The Regents of the University of California.  All rights reserved.
4275970Scy *
5275970Scy * This code is derived from software contributed to Berkeley by
6275970Scy * Edward Sze-Tyan Wang.
7275970Scy *
8275970Scy * Redistribution and use in source and binary forms, with or without
9275970Scy * modification, are permitted provided that the following conditions
10275970Scy * are met:
11275970Scy * 1. Redistributions of source code must retain the above copyright
12275970Scy *    notice, this list of conditions and the following disclaimer.
13275970Scy * 2. Redistributions in binary form must reproduce the above copyright
14275970Scy *    notice, this list of conditions and the following disclaimer in the
15275970Scy *    documentation and/or other materials provided with the distribution.
16275970Scy * 3. All advertising materials mentioning features or use of this software
17275970Scy *    must display the following acknowledgement:
18275970Scy *	This product includes software developed by the University of
19275970Scy *	California, Berkeley and its contributors.
20275970Scy * 4. Neither the name of the University nor the names of its contributors
21275970Scy *    may be used to endorse or promote products derived from this software
22275970Scy *    without specific prior written permission.
23275970Scy *
24275970Scy * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25275970Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26275970Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27275970Scy * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28275970Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29275970Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30275970Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31275970Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32275970Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33275970Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34275970Scy * SUCH DAMAGE.
35275970Scy */
36275970Scy
37275970Scy#ifndef lint
38275970Scystatic char copyright[] =
39275970Scy"@(#) Copyright (c) 1991, 1993\n\
40275970Scy	The Regents of the University of California.  All rights reserved.\n";
41275970Scy#endif /* not lint */
42275970Scy
43275970Scy#ifndef lint
44275970Scystatic char sccsid[] = "@(#)tail.c	8.1 (Berkeley) 6/6/93";
45275970Scy#endif /* not lint */
46275970Scy
47275970Scy#include <sys/types.h>
48275970Scy#include <sys/stat.h>
49275970Scy#include <errno.h>
50275970Scy#include <unistd.h>
51275970Scy#include <stdio.h>
52275970Scy#include <stdlib.h>
53275970Scy#include <string.h>
54275970Scy#include <err.h>
55275970Scy#include "extern.h"
56275970Scy
57275970Scyint Fflag, fflag, rflag, rval;
58275970Scychar *fname;
59275970Scy
60275970Scystatic void obsolete __P((char **));
61275970Scystatic void usage __P((void));
62275970Scy
63275970Scyint
64275970Scymain(argc, argv)
65275970Scy	int argc;
66275970Scy	char *argv[];
67275970Scy{
68275970Scy	struct stat sb;
69275970Scy	FILE *fp;
70275970Scy	long off;
71275970Scy	enum STYLE style;
72275970Scy	int ch, first;
73275970Scy	char *p;
74275970Scy
75275970Scy	/*
76275970Scy	 * Tail's options are weird.  First, -n10 is the same as -n-10, not
77275970Scy	 * -n+10.  Second, the number options are 1 based and not offsets,
78275970Scy	 * so -n+1 is the first line, and -c-1 is the last byte.  Third, the
79275970Scy	 * number options for the -r option specify the number of things that
80275970Scy	 * get displayed, not the starting point in the file.  The one major
81275970Scy	 * incompatibility in this version as compared to historical versions
82275970Scy	 * is that the 'r' option couldn't be modified by the -lbc options,
83275970Scy	 * i.e. it was always done in lines.  This version treats -rc as a
84275970Scy	 * number of characters in reverse order.  Finally, the default for
85275970Scy	 * -r is the entire file, not 10 lines.
86275970Scy	 */
87275970Scy#define	ARG(units, forward, backward) {					\
88275970Scy	if (style)							\
89275970Scy		usage();						\
90275970Scy	off = strtol(optarg, &p, 10) * (units);				\
91275970Scy	if (*p)								\
92275970Scy		errx(1, "illegal offset -- %s", optarg);		\
93275970Scy	switch(optarg[0]) {						\
94275970Scy	case '+':							\
95275970Scy		if (off)						\
96275970Scy			off -= (units);					\
97275970Scy			style = (forward);				\
98275970Scy		break;							\
99275970Scy	case '-':							\
100275970Scy		off = -off;						\
101275970Scy		/* FALLTHROUGH */					\
102275970Scy	default:							\
103275970Scy		style = (backward);					\
104275970Scy		break;							\
105275970Scy	}								\
106275970Scy}
107275970Scy
108275970Scy	obsolete(argv);
109275970Scy	style = NOTSET;
110275970Scy	while ((ch = getopt(argc, argv, "Fb:c:fn:r")) != -1)
111275970Scy		switch(ch) {
112275970Scy		case 'F':	/* -F is superset of (and implies) -f */
113275970Scy			Fflag = fflag = 1;
114275970Scy			break;
115275970Scy		case 'b':
116275970Scy			ARG(512, FBYTES, RBYTES);
117275970Scy			break;
118275970Scy		case 'c':
119275970Scy			ARG(1, FBYTES, RBYTES);
120275970Scy			break;
121275970Scy		case 'f':
122275970Scy			fflag = 1;
123275970Scy			break;
124275970Scy		case 'n':
125275970Scy			ARG(1, FLINES, RLINES);
126275970Scy			break;
127275970Scy		case 'r':
128275970Scy			rflag = 1;
129275970Scy			break;
130275970Scy		case '?':
131275970Scy		default:
132275970Scy			usage();
133275970Scy		}
134275970Scy	argc -= optind;
135275970Scy	argv += optind;
136275970Scy
137275970Scy	if (fflag && argc > 1)
138275970Scy		errx(1, "-f option only appropriate for a single file");
139275970Scy
140275970Scy	/*
141275970Scy	 * If displaying in reverse, don't permit follow option, and convert
142275970Scy	 * style values.
143275970Scy	 */
144275970Scy	if (rflag) {
145275970Scy		if (fflag)
146275970Scy			usage();
147275970Scy		if (style == FBYTES)
148275970Scy			style = RBYTES;
149275970Scy		else if (style == FLINES)
150275970Scy			style = RLINES;
151275970Scy	}
152275970Scy
153275970Scy	/*
154275970Scy	 * If style not specified, the default is the whole file for -r, and
155275970Scy	 * the last 10 lines if not -r.
156275970Scy	 */
157275970Scy	if (style == NOTSET) {
158275970Scy		if (rflag) {
159275970Scy			off = 0;
160275970Scy			style = REVERSE;
161275970Scy		} else {
162275970Scy			off = 10;
163275970Scy			style = RLINES;
164275970Scy		}
165275970Scy	}
166275970Scy
167275970Scy	if (*argv)
168275970Scy		for (first = 1; fname = *argv++;) {
169275970Scy			if ((fp = fopen(fname, "r")) == NULL ||
170275970Scy			    fstat(fileno(fp), &sb)) {
171275970Scy				ierr();
172275970Scy				continue;
173275970Scy			}
174275970Scy			if (argc > 1) {
175275970Scy				(void)printf("%s==> %s <==\n",
176275970Scy				    first ? "" : "\n", fname);
177275970Scy				first = 0;
178275970Scy				(void)fflush(stdout);
179275970Scy			}
180275970Scy
181275970Scy			if (rflag)
182275970Scy				reverse(fp, style, off, &sb);
183275970Scy			else
184275970Scy				forward(fp, style, off, &sb);
185275970Scy			(void)fclose(fp);
186275970Scy		}
187275970Scy	else {
188275970Scy		fname = "stdin";
189275970Scy
190275970Scy		if (fstat(fileno(stdin), &sb)) {
191275970Scy			ierr();
192275970Scy			exit(1);
193275970Scy		}
194275970Scy
195275970Scy		/*
196275970Scy		 * Determine if input is a pipe.  4.4BSD will set the SOCKET
197275970Scy		 * bit in the st_mode field for pipes.  Fix this then.
198275970Scy		 */
199275970Scy		if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
200275970Scy		    errno == ESPIPE) {
201275970Scy			errno = 0;
202275970Scy			fflag = 0;		/* POSIX.2 requires this. */
203275970Scy		}
204275970Scy
205275970Scy		if (rflag)
206275970Scy			reverse(stdin, style, off, &sb);
207275970Scy		else
208275970Scy			forward(stdin, style, off, &sb);
209275970Scy	}
210275970Scy	exit(rval);
211275970Scy}
212275970Scy
213275970Scy/*
214275970Scy * Convert the obsolete argument form into something that getopt can handle.
215275970Scy * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
216275970Scy * the option argument for a -b, -c or -n option gets converted.
217275970Scy */
218275970Scystatic void
219275970Scyobsolete(argv)
220275970Scy	char *argv[];
221275970Scy{
222275970Scy	register char *ap, *p, *t;
223275970Scy	int len;
224275970Scy	char *start;
225275970Scy
226275970Scy	while (ap = *++argv) {
227275970Scy		/* Return if "--" or not an option of any form. */
228275970Scy		if (ap[0] != '-') {
229275970Scy			if (ap[0] != '+')
230275970Scy				return;
231275970Scy		} else if (ap[1] == '-')
232275970Scy			return;
233275970Scy
234275970Scy		switch(*++ap) {
235275970Scy		/* Old-style option. */
236275970Scy		case '0': case '1': case '2': case '3': case '4':
237275970Scy		case '5': case '6': case '7': case '8': case '9':
238275970Scy
239275970Scy			/* Malloc space for dash, new option and argument. */
240275970Scy			len = strlen(*argv);
241275970Scy			if ((start = p = malloc(len + 3)) == NULL)
242275970Scy				err(1, "malloc");
243275970Scy			*p++ = '-';
244275970Scy
245275970Scy			/*
246275970Scy			 * Go to the end of the option argument.  Save off any
247275970Scy			 * trailing options (-3lf) and translate any trailing
248275970Scy			 * output style characters.
249275970Scy			 */
250275970Scy			t = *argv + len - 1;
251275970Scy			if (*t == 'f' || *t == 'r') {
252275970Scy				*p++ = *t;
253275970Scy				*t-- = '\0';
254275970Scy			}
255275970Scy			switch(*t) {
256275970Scy			case 'b':
257275970Scy				*p++ = 'b';
258275970Scy				*t = '\0';
259275970Scy				break;
260275970Scy			case 'c':
261275970Scy				*p++ = 'c';
262275970Scy				*t = '\0';
263275970Scy				break;
264275970Scy			case 'l':
265275970Scy				*t = '\0';
266275970Scy				/* FALLTHROUGH */
267275970Scy			case '0': case '1': case '2': case '3': case '4':
268275970Scy			case '5': case '6': case '7': case '8': case '9':
269275970Scy				*p++ = 'n';
270275970Scy				break;
271275970Scy			default:
272275970Scy				errx(1, "illegal option -- %s", *argv);
273275970Scy			}
274275970Scy			*p++ = *argv[0];
275275970Scy			(void)strcpy(p, ap);
276275970Scy			*argv = start;
277275970Scy			continue;
278275970Scy
279275970Scy		/*
280275970Scy		 * Options w/ arguments, skip the argument and continue
281275970Scy		 * with the next option.
282275970Scy		 */
283275970Scy		case 'b':
284275970Scy		case 'c':
285275970Scy		case 'n':
286275970Scy			if (!ap[1])
287275970Scy				++argv;
288275970Scy			/* FALLTHROUGH */
289275970Scy		/* Options w/o arguments, continue with the next option. */
290275970Scy		case 'f':
291275970Scy		case 'r':
292275970Scy			continue;
293275970Scy
294275970Scy		/* Illegal option, return and let getopt handle it. */
295275970Scy		default:
296275970Scy			return;
297275970Scy		}
298275970Scy	}
299275970Scy}
300275970Scy
301275970Scystatic void
302275970Scyusage()
303275970Scy{
304275970Scy	(void)fprintf(stderr,
305275970Scy	    "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n");
306275970Scy	exit(1);
307275970Scy}
308275970Scy