Annotation of src/usr.bin/tail/tail.c, Revision 1.3
1.3 ! millert 1: /* $OpenBSD: tail.c,v 1.2 1996/06/26 05:40:18 deraadt Exp $ */
1.2 deraadt 2:
1.1 deraadt 3: /*-
4: * Copyright (c) 1991, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Edward Sze-Tyan Wang.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. All advertising materials mentioning features or use of this software
19: * must display the following acknowledgement:
20: * This product includes software developed by the University of
21: * California, Berkeley and its contributors.
22: * 4. Neither the name of the University nor the names of its contributors
23: * may be used to endorse or promote products derived from this software
24: * without specific prior written permission.
25: *
26: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36: * SUCH DAMAGE.
37: */
38:
39: #ifndef lint
40: static char copyright[] =
41: "@(#) Copyright (c) 1991, 1993\n\
42: The Regents of the University of California. All rights reserved.\n";
43: #endif /* not lint */
44:
45: #ifndef lint
46: #if 0
47: static char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
48: #endif
1.3 ! millert 49: static char rcsid[] = "$OpenBSD: tail.c,v 1.2 1996/06/26 05:40:18 deraadt Exp $";
1.1 deraadt 50: #endif /* not lint */
51:
52: #include <sys/types.h>
53: #include <sys/stat.h>
1.3 ! millert 54:
! 55: #include <err.h>
1.1 deraadt 56: #include <errno.h>
57: #include <stdio.h>
58: #include <stdlib.h>
59: #include <string.h>
1.3 ! millert 60: #include <unistd.h>
! 61:
1.1 deraadt 62: #include "extern.h"
63:
64: int fflag, rflag, rval;
65: char *fname;
66:
67: static void obsolete __P((char **));
68: static void usage __P((void));
69:
70: int
71: main(argc, argv)
72: int argc;
73: char *argv[];
74: {
75: struct stat sb;
76: FILE *fp;
1.3 ! millert 77: long off = 0;
1.1 deraadt 78: enum STYLE style;
79: int ch, first;
80: char *p;
81:
82: /*
83: * Tail's options are weird. First, -n10 is the same as -n-10, not
84: * -n+10. Second, the number options are 1 based and not offsets,
85: * so -n+1 is the first line, and -c-1 is the last byte. Third, the
86: * number options for the -r option specify the number of things that
87: * get displayed, not the starting point in the file. The one major
88: * incompatibility in this version as compared to historical versions
89: * is that the 'r' option couldn't be modified by the -lbc options,
90: * i.e. it was always done in lines. This version treats -rc as a
91: * number of characters in reverse order. Finally, the default for
92: * -r is the entire file, not 10 lines.
93: */
94: #define ARG(units, forward, backward) { \
95: if (style) \
96: usage(); \
97: off = strtol(optarg, &p, 10) * (units); \
98: if (*p) \
1.3 ! millert 99: errx(1, "illegal offset -- %s", optarg); \
1.1 deraadt 100: switch(optarg[0]) { \
101: case '+': \
102: if (off) \
103: off -= (units); \
104: style = (forward); \
105: break; \
106: case '-': \
107: off = -off; \
108: /* FALLTHROUGH */ \
109: default: \
110: style = (backward); \
111: break; \
112: } \
113: }
114:
115: obsolete(argv);
116: style = NOTSET;
117: while ((ch = getopt(argc, argv, "b:c:fn:r")) != EOF)
118: switch(ch) {
119: case 'b':
120: ARG(512, FBYTES, RBYTES);
121: break;
122: case 'c':
123: ARG(1, FBYTES, RBYTES);
124: break;
125: case 'f':
126: fflag = 1;
127: break;
128: case 'n':
129: ARG(1, FLINES, RLINES);
130: break;
131: case 'r':
132: rflag = 1;
133: break;
134: case '?':
135: default:
136: usage();
137: }
138: argc -= optind;
139: argv += optind;
140:
141: if (fflag && argc > 1)
1.3 ! millert 142: errx(1, "-f option only appropriate for a single file");
1.1 deraadt 143:
144: /*
145: * If displaying in reverse, don't permit follow option, and convert
146: * style values.
147: */
148: if (rflag) {
149: if (fflag)
150: usage();
151: if (style == FBYTES)
152: style = RBYTES;
153: else if (style == FLINES)
154: style = RLINES;
155: }
156:
157: /*
158: * If style not specified, the default is the whole file for -r, and
159: * the last 10 lines if not -r.
160: */
161: if (style == NOTSET)
162: if (rflag) {
163: off = 0;
164: style = REVERSE;
165: } else {
166: off = 10;
167: style = RLINES;
168: }
169:
170: if (*argv)
1.3 ! millert 171: for (first = 1; (fname = *argv++);) {
1.1 deraadt 172: if ((fp = fopen(fname, "r")) == NULL ||
173: fstat(fileno(fp), &sb)) {
174: ierr();
175: continue;
176: }
177: if (argc > 1) {
178: (void)printf("%s==> %s <==\n",
179: first ? "" : "\n", fname);
180: first = 0;
181: (void)fflush(stdout);
182: }
183:
184: if (rflag)
185: reverse(fp, style, off, &sb);
186: else
187: forward(fp, style, off, &sb);
188: (void)fclose(fp);
189: }
190: else {
191: fname = "stdin";
192:
193: if (fstat(fileno(stdin), &sb)) {
194: ierr();
195: exit(1);
196: }
197:
198: /*
199: * Determine if input is a pipe. 4.4BSD will set the SOCKET
200: * bit in the st_mode field for pipes. Fix this then.
201: */
202: if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
203: errno == ESPIPE) {
204: errno = 0;
205: fflag = 0; /* POSIX.2 requires this. */
206: }
207:
208: if (rflag)
209: reverse(stdin, style, off, &sb);
210: else
211: forward(stdin, style, off, &sb);
212: }
213: exit(rval);
214: }
215:
216: /*
217: * Convert the obsolete argument form into something that getopt can handle.
218: * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
219: * the option argument for a -b, -c or -n option gets converted.
220: */
221: static void
222: obsolete(argv)
223: char *argv[];
224: {
225: register char *ap, *p, *t;
226: int len;
227: char *start;
228:
1.3 ! millert 229: while ((ap = *++argv)) {
1.1 deraadt 230: /* Return if "--" or not an option of any form. */
231: if (ap[0] != '-') {
232: if (ap[0] != '+')
233: return;
234: } else if (ap[1] == '-')
235: return;
236:
237: switch(*++ap) {
238: /* Old-style option. */
239: case '0': case '1': case '2': case '3': case '4':
240: case '5': case '6': case '7': case '8': case '9':
241:
242: /* Malloc space for dash, new option and argument. */
243: len = strlen(*argv);
244: if ((start = p = malloc(len + 3)) == NULL)
1.3 ! millert 245: err(1, NULL);
1.1 deraadt 246: *p++ = '-';
247:
248: /*
249: * Go to the end of the option argument. Save off any
250: * trailing options (-3lf) and translate any trailing
251: * output style characters.
252: */
253: t = *argv + len - 1;
254: if (*t == 'f' || *t == 'r') {
255: *p++ = *t;
256: *t-- = '\0';
257: }
258: switch(*t) {
259: case 'b':
260: *p++ = 'b';
261: *t = '\0';
262: break;
263: case 'c':
264: *p++ = 'c';
265: *t = '\0';
266: break;
267: case 'l':
268: *t = '\0';
269: /* FALLTHROUGH */
270: case '0': case '1': case '2': case '3': case '4':
271: case '5': case '6': case '7': case '8': case '9':
272: *p++ = 'n';
273: break;
274: default:
1.3 ! millert 275: errx(1, "illegal option -- %s", *argv);
1.1 deraadt 276: }
277: *p++ = *argv[0];
278: (void)strcpy(p, ap);
279: *argv = start;
280: continue;
281:
282: /*
283: * Options w/ arguments, skip the argument and continue
284: * with the next option.
285: */
286: case 'b':
287: case 'c':
288: case 'n':
289: if (!ap[1])
290: ++argv;
291: /* FALLTHROUGH */
292: /* Options w/o arguments, continue with the next option. */
293: case 'f':
294: case 'r':
295: continue;
296:
297: /* Illegal option, return and let getopt handle it. */
298: default:
299: return;
300: }
301: }
302: }
303:
304: static void
305: usage()
306: {
307: (void)fprintf(stderr,
308: "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n");
309: exit(1);
310: }