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