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