Annotation of src/usr.bin/tail/tail.c, Revision 1.24
1.24 ! cheloha 1: /* $OpenBSD: tail.c,v 1.23 2021/12/23 23:23:42 jsg 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); \
1.23 jsg 87: style = (forward); \
1.1 deraadt 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: default:
118: usage();
119: }
120: argc -= optind;
121: argv += optind;
1.16 landry 122:
1.1 deraadt 123: /*
124: * If displaying in reverse, don't permit follow option, and convert
125: * style values.
126: */
127: if (rflag) {
128: if (fflag)
129: usage();
130: if (style == FBYTES)
131: style = RBYTES;
132: else if (style == FLINES)
133: style = RLINES;
134: }
135:
136: /*
137: * If style not specified, the default is the whole file for -r, and
138: * the last 10 lines if not -r.
139: */
1.5 millert 140: if (style == NOTSET) {
1.1 deraadt 141: if (rflag) {
142: off = 0;
143: style = REVERSE;
144: } else {
145: off = 10;
146: style = RLINES;
147: }
1.5 millert 148: }
1.15 landry 149:
1.20 tedu 150: if ((tf = reallocarray(NULL, argc ? argc : 1, sizeof(*tf))) == NULL)
151: err(1, "reallocarray");
152:
153: if (argc) {
1.21 halex 154: for (i = 0; *argv; i++) {
155: tf[i].fname = *argv++;
1.20 tedu 156: if ((tf[i].fp = fopen(tf[i].fname, "r")) == NULL ||
157: fstat(fileno(tf[i].fp), &(tf[i].sb))) {
158: ierr(tf[i].fname);
159: i--;
1.16 landry 160: continue;
1.1 deraadt 161: }
162: }
1.20 tedu 163: if (rflag)
164: reverse(tf, i, style, off);
165: else
166: forward(tf, i, style, off);
167: }
1.1 deraadt 168: else {
1.19 deraadt 169: if (pledge("stdio", NULL) == -1)
170: err(1, "pledge");
1.18 deraadt 171:
1.20 tedu 172: tf[0].fname = "stdin";
173: tf[0].fp = stdin;
1.1 deraadt 174:
1.20 tedu 175: if (fstat(fileno(stdin), &(tf[0].sb))) {
176: ierr(tf[0].fname);
1.1 deraadt 177: exit(1);
178: }
179:
180: /*
181: * Determine if input is a pipe. 4.4BSD will set the SOCKET
182: * bit in the st_mode field for pipes. Fix this then.
183: */
1.20 tedu 184: if (lseek(fileno(tf[0].fp), (off_t)0, SEEK_CUR) == -1 &&
1.1 deraadt 185: errno == ESPIPE) {
186: errno = 0;
187: fflag = 0; /* POSIX.2 requires this. */
188: }
189:
190: if (rflag)
1.20 tedu 191: reverse(tf, 1, style, off);
1.1 deraadt 192: else
1.20 tedu 193: forward(tf, 1, style, off);
1.1 deraadt 194: }
195: exit(rval);
196: }
197:
198: /*
199: * Convert the obsolete argument form into something that getopt can handle.
200: * This means that anything of the form [+-][0-9][0-9]*[lbc][fr] that isn't
201: * the option argument for a -b, -c or -n option gets converted.
202: */
203: static void
1.11 deraadt 204: obsolete(char *argv[])
1.1 deraadt 205: {
1.7 mpech 206: char *ap, *p, *t;
1.13 kjell 207: size_t len;
1.1 deraadt 208: char *start;
209:
1.3 millert 210: while ((ap = *++argv)) {
1.1 deraadt 211: /* Return if "--" or not an option of any form. */
212: if (ap[0] != '-') {
213: if (ap[0] != '+')
214: return;
215: } else if (ap[1] == '-')
216: return;
217:
218: switch(*++ap) {
219: /* Old-style option. */
220: case '0': case '1': case '2': case '3': case '4':
221: case '5': case '6': case '7': case '8': case '9':
222:
223: /* Malloc space for dash, new option and argument. */
224: len = strlen(*argv);
1.9 dhartmei 225: if ((start = p = malloc(len + 4)) == NULL)
1.3 millert 226: err(1, NULL);
1.1 deraadt 227: *p++ = '-';
228:
229: /*
230: * Go to the end of the option argument. Save off any
231: * trailing options (-3lf) and translate any trailing
232: * output style characters.
233: */
234: t = *argv + len - 1;
235: if (*t == 'f' || *t == 'r') {
236: *p++ = *t;
237: *t-- = '\0';
238: }
239: switch(*t) {
240: case 'b':
241: *p++ = 'b';
242: *t = '\0';
243: break;
244: case 'c':
245: *p++ = 'c';
246: *t = '\0';
247: break;
248: case 'l':
249: *t = '\0';
250: /* FALLTHROUGH */
251: case '0': case '1': case '2': case '3': case '4':
252: case '5': case '6': case '7': case '8': case '9':
253: *p++ = 'n';
254: break;
255: default:
1.3 millert 256: errx(1, "illegal option -- %s", *argv);
1.1 deraadt 257: }
258: *p++ = *argv[0];
1.9 dhartmei 259: (void)strlcpy(p, ap, start + len + 4 - p);
1.1 deraadt 260: *argv = start;
261: continue;
262:
263: /*
264: * Options w/ arguments, skip the argument and continue
265: * with the next option.
266: */
267: case 'b':
268: case 'c':
269: case 'n':
270: if (!ap[1])
271: ++argv;
272: /* FALLTHROUGH */
273: /* Options w/o arguments, continue with the next option. */
274: case 'f':
275: case 'r':
276: continue;
277:
278: /* Illegal option, return and let getopt handle it. */
279: default:
280: return;
281: }
282: }
283: }
284:
285: static void
1.11 deraadt 286: usage(void)
1.1 deraadt 287: {
288: (void)fprintf(stderr,
1.14 jmc 289: "usage: tail [-f | -r] "
290: "[-b number | -c number | -n number | -number] [file ...]\n");
1.1 deraadt 291: exit(1);
292: }