Annotation of src/usr.bin/tail/tail.c, Revision 1.8
1.8 ! millert 1: /* $OpenBSD: tail.c,v 1.7 2001/11/19 19:02:16 mpech 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.8 ! millert 49: static char rcsid[] = "$OpenBSD: tail.c,v 1.7 2001/11/19 19:02:16 mpech 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;
1.6 art 66: int is_stdin;
1.1 deraadt 67:
1.8 ! millert 68: static void obsolete(char **);
! 69: static void usage(void);
1.1 deraadt 70:
71: int
72: main(argc, argv)
73: int argc;
74: char *argv[];
75: {
76: struct stat sb;
77: FILE *fp;
1.3 millert 78: long off = 0;
1.1 deraadt 79: enum STYLE style;
80: int ch, first;
81: char *p;
82:
83: /*
84: * Tail's options are weird. First, -n10 is the same as -n-10, not
85: * -n+10. Second, the number options are 1 based and not offsets,
86: * so -n+1 is the first line, and -c-1 is the last byte. Third, the
87: * number options for the -r option specify the number of things that
88: * get displayed, not the starting point in the file. The one major
89: * incompatibility in this version as compared to historical versions
90: * is that the 'r' option couldn't be modified by the -lbc options,
91: * i.e. it was always done in lines. This version treats -rc as a
92: * number of characters in reverse order. Finally, the default for
93: * -r is the entire file, not 10 lines.
94: */
95: #define ARG(units, forward, backward) { \
96: if (style) \
97: usage(); \
98: off = strtol(optarg, &p, 10) * (units); \
99: if (*p) \
1.3 millert 100: errx(1, "illegal offset -- %s", optarg); \
1.1 deraadt 101: switch(optarg[0]) { \
102: case '+': \
103: if (off) \
104: off -= (units); \
105: style = (forward); \
106: break; \
107: case '-': \
108: off = -off; \
109: /* FALLTHROUGH */ \
110: default: \
111: style = (backward); \
112: break; \
113: } \
114: }
115:
116: obsolete(argv);
117: style = NOTSET;
1.4 millert 118: while ((ch = getopt(argc, argv, "b:c:fn:r")) != -1)
1.1 deraadt 119: switch(ch) {
120: case 'b':
121: ARG(512, FBYTES, RBYTES);
122: break;
123: case 'c':
124: ARG(1, FBYTES, RBYTES);
125: break;
126: case 'f':
127: fflag = 1;
128: break;
129: case 'n':
130: ARG(1, FLINES, RLINES);
131: break;
132: case 'r':
133: rflag = 1;
134: break;
135: case '?':
136: default:
137: usage();
138: }
139: argc -= optind;
140: argv += optind;
141:
142: if (fflag && argc > 1)
1.3 millert 143: errx(1, "-f option only appropriate for a single file");
1.1 deraadt 144:
145: /*
146: * If displaying in reverse, don't permit follow option, and convert
147: * style values.
148: */
149: if (rflag) {
150: if (fflag)
151: usage();
152: if (style == FBYTES)
153: style = RBYTES;
154: else if (style == FLINES)
155: style = RLINES;
156: }
157:
158: /*
159: * If style not specified, the default is the whole file for -r, and
160: * the last 10 lines if not -r.
161: */
1.5 millert 162: if (style == NOTSET) {
1.1 deraadt 163: if (rflag) {
164: off = 0;
165: style = REVERSE;
166: } else {
167: off = 10;
168: style = RLINES;
169: }
1.5 millert 170: }
1.1 deraadt 171:
172: if (*argv)
1.3 millert 173: for (first = 1; (fname = *argv++);) {
1.1 deraadt 174: if ((fp = fopen(fname, "r")) == NULL ||
175: fstat(fileno(fp), &sb)) {
176: ierr();
177: continue;
178: }
179: if (argc > 1) {
180: (void)printf("%s==> %s <==\n",
181: first ? "" : "\n", fname);
182: first = 0;
183: (void)fflush(stdout);
184: }
185:
186: if (rflag)
187: reverse(fp, style, off, &sb);
188: else
189: forward(fp, style, off, &sb);
190: (void)fclose(fp);
191: }
192: else {
193: fname = "stdin";
1.6 art 194: is_stdin = 1;
1.1 deraadt 195:
196: if (fstat(fileno(stdin), &sb)) {
197: ierr();
198: exit(1);
199: }
200:
201: /*
202: * Determine if input is a pipe. 4.4BSD will set the SOCKET
203: * bit in the st_mode field for pipes. Fix this then.
204: */
205: if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
206: errno == ESPIPE) {
207: errno = 0;
208: fflag = 0; /* POSIX.2 requires this. */
209: }
210:
211: if (rflag)
212: reverse(stdin, style, off, &sb);
213: else
214: forward(stdin, style, off, &sb);
215: }
216: exit(rval);
217: }
218:
219: /*
220: * Convert the obsolete argument form into something that getopt can handle.
221: * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
222: * the option argument for a -b, -c or -n option gets converted.
223: */
224: static void
225: obsolete(argv)
226: char *argv[];
227: {
1.7 mpech 228: char *ap, *p, *t;
1.1 deraadt 229: int len;
230: char *start;
231:
1.3 millert 232: while ((ap = *++argv)) {
1.1 deraadt 233: /* Return if "--" or not an option of any form. */
234: if (ap[0] != '-') {
235: if (ap[0] != '+')
236: return;
237: } else if (ap[1] == '-')
238: return;
239:
240: switch(*++ap) {
241: /* Old-style option. */
242: case '0': case '1': case '2': case '3': case '4':
243: case '5': case '6': case '7': case '8': case '9':
244:
245: /* Malloc space for dash, new option and argument. */
246: len = strlen(*argv);
247: if ((start = p = malloc(len + 3)) == NULL)
1.3 millert 248: err(1, NULL);
1.1 deraadt 249: *p++ = '-';
250:
251: /*
252: * Go to the end of the option argument. Save off any
253: * trailing options (-3lf) and translate any trailing
254: * output style characters.
255: */
256: t = *argv + len - 1;
257: if (*t == 'f' || *t == 'r') {
258: *p++ = *t;
259: *t-- = '\0';
260: }
261: switch(*t) {
262: case 'b':
263: *p++ = 'b';
264: *t = '\0';
265: break;
266: case 'c':
267: *p++ = 'c';
268: *t = '\0';
269: break;
270: case 'l':
271: *t = '\0';
272: /* FALLTHROUGH */
273: case '0': case '1': case '2': case '3': case '4':
274: case '5': case '6': case '7': case '8': case '9':
275: *p++ = 'n';
276: break;
277: default:
1.3 millert 278: errx(1, "illegal option -- %s", *argv);
1.1 deraadt 279: }
280: *p++ = *argv[0];
281: (void)strcpy(p, ap);
282: *argv = start;
283: continue;
284:
285: /*
286: * Options w/ arguments, skip the argument and continue
287: * with the next option.
288: */
289: case 'b':
290: case 'c':
291: case 'n':
292: if (!ap[1])
293: ++argv;
294: /* FALLTHROUGH */
295: /* Options w/o arguments, continue with the next option. */
296: case 'f':
297: case 'r':
298: continue;
299:
300: /* Illegal option, return and let getopt handle it. */
301: default:
302: return;
303: }
304: }
305: }
306:
307: static void
308: usage()
309: {
310: (void)fprintf(stderr,
311: "usage: tail [-f | -r] [-b # | -c # | -n #] [file ...]\n");
312: exit(1);
313: }