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