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