]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/tail/tail.c
Include eventhandler.h in more compilation units
[FreeBSD/FreeBSD.git] / usr.bin / tail / tail.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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.
18  * 3. Neither the name of the University nor the names of its contributors
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/cdefs.h>
36
37 __FBSDID("$FreeBSD$");
38
39 #ifndef lint
40 static const char copyright[] =
41 "@(#) Copyright (c) 1991, 1993\n\
42         The Regents of the University of California.  All rights reserved.\n";
43 #endif
44
45 #ifndef lint
46 static const char sccsid[] = "@(#)tail.c        8.1 (Berkeley) 6/6/93";
47 #endif
48
49 #include <sys/types.h>
50 #include <sys/stat.h>
51
52 #include <err.h>
53 #include <errno.h>
54 #include <getopt.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 #include "extern.h"
61
62 int Fflag, fflag, qflag, rflag, rval, no_files;
63
64 static file_info_t *files;
65
66 static void obsolete(char **);
67 static void usage(void);
68
69 static const struct option long_opts[] =
70 {
71         {"blocks",      required_argument,      NULL, 'b'},
72         {"bytes",       required_argument,      NULL, 'c'},
73         {"lines",       required_argument,      NULL, 'n'},
74         {NULL,          no_argument,            NULL, 0}
75 };
76
77 int
78 main(int argc, char *argv[])
79 {
80         struct stat sb;
81         const char *fn;
82         FILE *fp;
83         off_t off;
84         enum STYLE style;
85         int i, ch, first;
86         file_info_t *file;
87         char *p;
88
89         /*
90          * Tail's options are weird.  First, -n10 is the same as -n-10, not
91          * -n+10.  Second, the number options are 1 based and not offsets,
92          * so -n+1 is the first line, and -c-1 is the last byte.  Third, the
93          * number options for the -r option specify the number of things that
94          * get displayed, not the starting point in the file.  The one major
95          * incompatibility in this version as compared to historical versions
96          * is that the 'r' option couldn't be modified by the -lbc options,
97          * i.e. it was always done in lines.  This version treats -rc as a
98          * number of characters in reverse order.  Finally, the default for
99          * -r is the entire file, not 10 lines.
100          */
101 #define ARG(units, forward, backward) {                                 \
102         if (style)                                                      \
103                 usage();                                                \
104         off = strtoll(optarg, &p, 10) * (units);                        \
105         if (*p)                                                         \
106                 errx(1, "illegal offset -- %s", optarg);                \
107         switch(optarg[0]) {                                             \
108         case '+':                                                       \
109                 if (off)                                                \
110                         off -= (units);                                 \
111                         style = (forward);                              \
112                 break;                                                  \
113         case '-':                                                       \
114                 off = -off;                                             \
115                 /* FALLTHROUGH */                                       \
116         default:                                                        \
117                 style = (backward);                                     \
118                 break;                                                  \
119         }                                                               \
120 }
121
122         obsolete(argv);
123         style = NOTSET;
124         off = 0;
125         while ((ch = getopt_long(argc, argv, "+Fb:c:fn:qr", long_opts, NULL)) !=
126             -1)
127                 switch(ch) {
128                 case 'F':       /* -F is superset of (and implies) -f */
129                         Fflag = fflag = 1;
130                         break;
131                 case 'b':
132                         ARG(512, FBYTES, RBYTES);
133                         break;
134                 case 'c':
135                         ARG(1, FBYTES, RBYTES);
136                         break;
137                 case 'f':
138                         fflag = 1;
139                         break;
140                 case 'n':
141                         ARG(1, FLINES, RLINES);
142                         break;
143                 case 'q':
144                         qflag = 1;
145                         break;
146                 case 'r':
147                         rflag = 1;
148                         break;
149                 case '?':
150                 default:
151                         usage();
152                 }
153         argc -= optind;
154         argv += optind;
155
156         no_files = argc ? argc : 1;
157
158         /*
159          * If displaying in reverse, don't permit follow option, and convert
160          * style values.
161          */
162         if (rflag) {
163                 if (fflag)
164                         usage();
165                 if (style == FBYTES)
166                         style = RBYTES;
167                 else if (style == FLINES)
168                         style = RLINES;
169         }
170
171         /*
172          * If style not specified, the default is the whole file for -r, and
173          * the last 10 lines if not -r.
174          */
175         if (style == NOTSET) {
176                 if (rflag) {
177                         off = 0;
178                         style = REVERSE;
179                 } else {
180                         off = 10;
181                         style = RLINES;
182                 }
183         }
184
185         if (*argv && fflag) {
186                 files = (struct file_info *) malloc(no_files *
187                     sizeof(struct file_info));
188                 if (!files)
189                         err(1, "Couldn't malloc space for file descriptors.");
190
191                 for (file = files; (fn = *argv++); file++) {
192                         file->file_name = strdup(fn);
193                         if (! file->file_name)
194                                 errx(1, "Couldn't malloc space for file name.");
195                         if ((file->fp = fopen(file->file_name, "r")) == NULL ||
196                             fstat(fileno(file->fp), &file->st)) {
197                                 if (file->fp != NULL) {
198                                         fclose(file->fp);
199                                         file->fp = NULL;
200                                 }
201                                 if (!Fflag || errno != ENOENT)
202                                         ierr(file->file_name);
203                         }
204                 }
205                 follow(files, style, off);
206                 for (i = 0, file = files; i < no_files; i++, file++) {
207                     free(file->file_name);
208                 }
209                 free(files);
210         } else if (*argv) {
211                 for (first = 1; (fn = *argv++);) {
212                         if ((fp = fopen(fn, "r")) == NULL ||
213                             fstat(fileno(fp), &sb)) {
214                                 ierr(fn);
215                                 continue;
216                         }
217                         if (argc > 1 && !qflag) {
218                                 printfn(fn, !first);
219                                 first = 0;
220                         }
221
222                         if (rflag)
223                                 reverse(fp, fn, style, off, &sb);
224                         else
225                                 forward(fp, fn, style, off, &sb);
226                 }
227         } else {
228                 fn = "stdin";
229
230                 if (fstat(fileno(stdin), &sb)) {
231                         ierr(fn);
232                         exit(1);
233                 }
234
235                 /*
236                  * Determine if input is a pipe.  4.4BSD will set the SOCKET
237                  * bit in the st_mode field for pipes.  Fix this then.
238                  */
239                 if (lseek(fileno(stdin), (off_t)0, SEEK_CUR) == -1 &&
240                     errno == ESPIPE) {
241                         errno = 0;
242                         fflag = 0;              /* POSIX.2 requires this. */
243                 }
244
245                 if (rflag)
246                         reverse(stdin, fn, style, off, &sb);
247                 else
248                         forward(stdin, fn, style, off, &sb);
249         }
250         exit(rval);
251 }
252
253 /*
254  * Convert the obsolete argument form into something that getopt can handle.
255  * This means that anything of the form [+-][0-9][0-9]*[lbc][Ffr] that isn't
256  * the option argument for a -b, -c or -n option gets converted.
257  */
258 static void
259 obsolete(char *argv[])
260 {
261         char *ap, *p, *t;
262         size_t len;
263         char *start;
264
265         while ((ap = *++argv)) {
266                 /* Return if "--" or not an option of any form. */
267                 if (ap[0] != '-') {
268                         if (ap[0] != '+')
269                                 return;
270                 } else if (ap[1] == '-')
271                         return;
272
273                 switch(*++ap) {
274                 /* Old-style option. */
275                 case '0': case '1': case '2': case '3': case '4':
276                 case '5': case '6': case '7': case '8': case '9':
277
278                         /* Malloc space for dash, new option and argument. */
279                         len = strlen(*argv);
280                         if ((start = p = malloc(len + 3)) == NULL)
281                                 err(1, "malloc");
282                         *p++ = '-';
283
284                         /*
285                          * Go to the end of the option argument.  Save off any
286                          * trailing options (-3lf) and translate any trailing
287                          * output style characters.
288                          */
289                         t = *argv + len - 1;
290                         if (*t == 'F' || *t == 'f' || *t == 'r') {
291                                 *p++ = *t;
292                                 *t-- = '\0';
293                         }
294                         switch(*t) {
295                         case 'b':
296                                 *p++ = 'b';
297                                 *t = '\0';
298                                 break;
299                         case 'c':
300                                 *p++ = 'c';
301                                 *t = '\0';
302                                 break;
303                         case 'l':
304                                 *t = '\0';
305                                 /* FALLTHROUGH */
306                         case '0': case '1': case '2': case '3': case '4':
307                         case '5': case '6': case '7': case '8': case '9':
308                                 *p++ = 'n';
309                                 break;
310                         default:
311                                 errx(1, "illegal option -- %s", *argv);
312                         }
313                         *p++ = *argv[0];
314                         (void)strcpy(p, ap);
315                         *argv = start;
316                         continue;
317
318                 /*
319                  * Options w/ arguments, skip the argument and continue
320                  * with the next option.
321                  */
322                 case 'b':
323                 case 'c':
324                 case 'n':
325                         if (!ap[1])
326                                 ++argv;
327                         /* FALLTHROUGH */
328                 /* Options w/o arguments, continue with the next option. */
329                 case 'F':
330                 case 'f':
331                 case 'r':
332                         continue;
333
334                 /* Illegal option, return and let getopt handle it. */
335                 default:
336                         return;
337                 }
338         }
339 }
340
341 static void
342 usage(void)
343 {
344         (void)fprintf(stderr,
345             "usage: tail [-F | -f | -r] [-q] [-b # | -c # | -n #]"
346             " [file ...]\n");
347         exit(1);
348 }