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