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