blob: 601f0873d42079d250b54ac2e495ea421a81b4ce [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Erik Andersen3fe39dc2000-01-25 18:13:53 +00002#include "internal.h"
Erik Andersen9ffdaa62000-02-11 21:55:04 +00003
Erik Andersen3fe39dc2000-01-25 18:13:53 +00004/* This file contains _two_ implementations of tail. One is
5 * a bit more full featured, but costs 6k. The other (i.e. the
6 * SIMPLE_TAIL one) is less capable, but is good enough for about
7 * 99% of the things folks want to use tail for, and only costs 2k.
8 */
9
10
11#ifdef BB_FEATURE_SIMPLE_TAIL
12
13/* tail -- output the last part of file(s)
14 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
15
16 This program is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation; either version 2, or (at your option)
19 any later version.
20
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
30 Original version by Paul Rubin <phr@ocf.berkeley.edu>.
31 Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
32 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
33
34 Rewrote the option parser, removed locales support,
35 and generally busyboxed, Erik Andersen <andersen@lineo.com>
36
37 Removed superfluous options and associated code ("-c", "-n", "-q").
Erik Andersenfac10d72000-02-07 05:29:42 +000038 Removed "tail -f" support for multiple files.
Erik Andersen3fe39dc2000-01-25 18:13:53 +000039 Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>.
40
41 */
42
43
44#include <stdio.h>
45#include <stdarg.h>
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <fcntl.h>
49#include <ctype.h>
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000050#define BB_DECLARE_EXTERN
51#define bb_need_help
52#include "messages.c"
Erik Andersen3fe39dc2000-01-25 18:13:53 +000053
54
55#define XWRITE(fd, buffer, n_bytes) \
56 do { \
57 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
Erik Andersen9ffdaa62000-02-11 21:55:04 +000058 errorMsg("write error"); \
Erik Andersen3fe39dc2000-01-25 18:13:53 +000059 } while (0)
60
61/* Number of items to tail. */
62#define DEFAULT_N_LINES 10
63
64/* Size of atomic reads. */
65#ifndef BUFSIZ
66#define BUFSIZ (512 * 8)
67#endif
68
69/* If nonzero, read from the end of one file until killed. */
70static int forever;
71
72/* If nonzero, print filename headers. */
73static int print_headers;
74
75const char tail_usage[] =
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000076 "tail [OPTION] [FILE]...\n"
77#ifndef BB_FEATURE_TRIVIAL_HELP
78 "\nPrint last 10 lines of each FILE to standard output.\n"
Erik Andersene49d5ec2000-02-08 19:58:47 +000079 "With more than one FILE, precede each with a header giving the\n"
80 "file name. With no FILE, or when FILE is -, read standard input.\n\n"
81 "Options:\n"
82 "\t-n NUM\t\tPrint last NUM lines instead of first 10\n"
83
84 "\t-f\t\tOutput data as the file grows. This version\n"
Erik Andersen7ab9c7e2000-05-12 19:41:47 +000085 "\t\t\tof 'tail -f' supports only one file at a time.\n"
86#endif
87 ;
Erik Andersen3fe39dc2000-01-25 18:13:53 +000088
89
90static void write_header(const char *filename)
91{
Erik Andersene49d5ec2000-02-08 19:58:47 +000092 static int first_file = 1;
Erik Andersen3fe39dc2000-01-25 18:13:53 +000093
Erik Andersene49d5ec2000-02-08 19:58:47 +000094 printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
95 first_file = 0;
Erik Andersen3fe39dc2000-01-25 18:13:53 +000096}
97
98/* Print the last N_LINES lines from the end of file FD.
99 Go backward through the file, reading `BUFSIZ' bytes at a time (except
100 probably the first), until we hit the start of the file or have
101 read NUMBER newlines.
102 POS starts out as the length of the file (the offset of the last
103 byte of the file + 1).
104 Return 0 if successful, 1 if an error occurred. */
105
106static int
107file_lines(const char *filename, int fd, long int n_lines, off_t pos)
108{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000109 char buffer[BUFSIZ];
110 int bytes_read;
111 int i; /* Index into `buffer' for scanning. */
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000112
Erik Andersene49d5ec2000-02-08 19:58:47 +0000113 if (n_lines == 0)
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000114 return 0;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000115
Erik Andersene49d5ec2000-02-08 19:58:47 +0000116 /* Set `bytes_read' to the size of the last, probably partial, buffer;
117 0 < `bytes_read' <= `BUFSIZ'. */
118 bytes_read = pos % BUFSIZ;
119 if (bytes_read == 0)
120 bytes_read = BUFSIZ;
121 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
122 reads will be on block boundaries, which might increase efficiency. */
123 pos -= bytes_read;
124 lseek(fd, pos, SEEK_SET);
125 bytes_read = fullRead(fd, buffer, bytes_read);
126 if (bytes_read == -1)
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000127 errorMsg("read error");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000128
129 /* Count the incomplete line on files that don't end with a newline. */
130 if (bytes_read && buffer[bytes_read - 1] != '\n')
131 --n_lines;
132
133 do {
134 /* Scan backward, counting the newlines in this bufferfull. */
135 for (i = bytes_read - 1; i >= 0; i--) {
136 /* Have we counted the requested number of newlines yet? */
137 if (buffer[i] == '\n' && n_lines-- == 0) {
138 /* If this newline wasn't the last character in the buffer,
139 print the text after it. */
140 if (i != bytes_read - 1)
141 XWRITE(STDOUT_FILENO, &buffer[i + 1],
142 bytes_read - (i + 1));
143 return 0;
144 }
145 }
146 /* Not enough newlines in that bufferfull. */
147 if (pos == 0) {
148 /* Not enough lines in the file; print the entire file. */
149 lseek(fd, (off_t) 0, SEEK_SET);
150 return 0;
151 }
152 pos -= BUFSIZ;
153 lseek(fd, pos, SEEK_SET);
154 }
155 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
156 if (bytes_read == -1)
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000157 errorMsg("read error");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000158
159 return 0;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000160}
161
162/* Print the last N_LINES lines from the end of the standard input,
163 open for reading as pipe FD.
164 Buffer the text as a linked list of LBUFFERs, adding them as needed.
165 Return 0 if successful, 1 if an error occured. */
166
167static int pipe_lines(const char *filename, int fd, long int n_lines)
168{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000169 struct linebuffer {
170 int nbytes, nlines;
171 char buffer[BUFSIZ];
172 struct linebuffer *next;
173 };
174 typedef struct linebuffer LBUFFER;
175 LBUFFER *first, *last, *tmp;
176 int i; /* Index into buffers. */
177 int total_lines = 0; /* Total number of newlines in all buffers. */
178 int errors = 0;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000179
Erik Andersene49d5ec2000-02-08 19:58:47 +0000180 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
181 first->nbytes = first->nlines = 0;
182 first->next = NULL;
183 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000184
Erik Andersene49d5ec2000-02-08 19:58:47 +0000185 /* Input is always read into a fresh buffer. */
186 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
187 tmp->nlines = 0;
188 tmp->next = NULL;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000189
Erik Andersene49d5ec2000-02-08 19:58:47 +0000190 /* Count the number of newlines just read. */
191 for (i = 0; i < tmp->nbytes; i++)
192 if (tmp->buffer[i] == '\n')
193 ++tmp->nlines;
194 total_lines += tmp->nlines;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000195
Erik Andersene49d5ec2000-02-08 19:58:47 +0000196 /* If there is enough room in the last buffer read, just append the new
197 one to it. This is because when reading from a pipe, `nbytes' can
198 often be very small. */
199 if (tmp->nbytes + last->nbytes < BUFSIZ) {
200 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
201 last->nbytes += tmp->nbytes;
202 last->nlines += tmp->nlines;
203 } else {
204 /* If there's not enough room, link the new buffer onto the end of
205 the list, then either free up the oldest buffer for the next
206 read if that would leave enough lines, or else malloc a new one.
207 Some compaction mechanism is possible but probably not
208 worthwhile. */
209 last = last->next = tmp;
210 if (total_lines - first->nlines > n_lines) {
211 tmp = first;
212 total_lines -= first->nlines;
213 first = first->next;
214 } else
215 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
216 }
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000217 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000218 if (tmp->nbytes == -1)
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000219 errorMsg("read error");
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000220
Erik Andersene49d5ec2000-02-08 19:58:47 +0000221 free((char *) tmp);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000222
Erik Andersene49d5ec2000-02-08 19:58:47 +0000223 /* This prevents a core dump when the pipe contains no newlines. */
224 if (n_lines == 0)
225 goto free_lbuffers;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000226
Erik Andersene49d5ec2000-02-08 19:58:47 +0000227 /* Count the incomplete line on files that don't end with a newline. */
228 if (last->buffer[last->nbytes - 1] != '\n') {
229 ++last->nlines;
230 ++total_lines;
231 }
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000232
Erik Andersene49d5ec2000-02-08 19:58:47 +0000233 /* Run through the list, printing lines. First, skip over unneeded
234 buffers. */
235 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
236 total_lines -= tmp->nlines;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000237
Erik Andersene49d5ec2000-02-08 19:58:47 +0000238 /* Find the correct beginning, then print the rest of the file. */
239 if (total_lines > n_lines) {
240 char *cp;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000241
Erik Andersene49d5ec2000-02-08 19:58:47 +0000242 /* Skip `total_lines' - `n_lines' newlines. We made sure that
243 `total_lines' - `n_lines' <= `tmp->nlines'. */
244 cp = tmp->buffer;
245 for (i = total_lines - n_lines; i; --i)
246 while (*cp++ != '\n')
247 /* Do nothing. */ ;
248 i = cp - tmp->buffer;
249 } else
250 i = 0;
251 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000252
Erik Andersene49d5ec2000-02-08 19:58:47 +0000253 for (tmp = tmp->next; tmp; tmp = tmp->next)
254 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000255
256 free_lbuffers:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000257 while (first) {
258 tmp = first->next;
259 free((char *) first);
260 first = tmp;
261 }
262 return errors;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000263}
264
265/* Display file FILENAME from the current position in FD to the end.
266 If `forever' is nonzero, keep reading from the end of the file
267 until killed. Return the number of bytes read from the file. */
268
269static long dump_remainder(const char *filename, int fd)
270{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000271 char buffer[BUFSIZ];
272 int bytes_read;
273 long total;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000274
Erik Andersene49d5ec2000-02-08 19:58:47 +0000275 total = 0;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000276 output:
Erik Andersene49d5ec2000-02-08 19:58:47 +0000277 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
278 XWRITE(STDOUT_FILENO, buffer, bytes_read);
279 total += bytes_read;
280 }
281 if (bytes_read == -1)
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000282 errorMsg("read error");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000283 if (forever) {
284 fflush(stdout);
285 sleep(1);
286 goto output;
287 }
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000288
Erik Andersene49d5ec2000-02-08 19:58:47 +0000289 return total;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000290}
291
292/* Output the last N_LINES lines of file FILENAME open for reading in FD.
293 Return 0 if successful, 1 if an error occurred. */
294
295static int tail_lines(const char *filename, int fd, long int n_lines)
296{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000297 struct stat stats;
298 off_t length;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000299
Erik Andersene49d5ec2000-02-08 19:58:47 +0000300 if (print_headers)
301 write_header(filename);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000302
Erik Andersene49d5ec2000-02-08 19:58:47 +0000303 if (fstat(fd, &stats))
Erik Andersen9ffdaa62000-02-11 21:55:04 +0000304 errorMsg("fstat error");
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000305
Erik Andersene49d5ec2000-02-08 19:58:47 +0000306 /* Use file_lines only if FD refers to a regular file with
307 its file pointer positioned at beginning of file. */
308 /* FIXME: adding the lseek conjunct is a kludge.
309 Once there's a reasonable test suite, fix the true culprit:
310 file_lines. file_lines shouldn't presume that the input
311 file pointer is initially positioned to beginning of file. */
312 if (S_ISREG(stats.st_mode)
313 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
314 length = lseek(fd, (off_t) 0, SEEK_END);
315 if (length != 0 && file_lines(filename, fd, n_lines, length))
316 return 1;
317 dump_remainder(filename, fd);
318 } else
319 return pipe_lines(filename, fd, n_lines);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000320
Erik Andersene49d5ec2000-02-08 19:58:47 +0000321 return 0;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000322}
323
324/* Display the last N_UNITS lines of file FILENAME.
325 "-" for FILENAME means the standard input.
326 Return 0 if successful, 1 if an error occurred. */
327
328static int tail_file(const char *filename, off_t n_units)
329{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000330 int fd, errors;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000331
Erik Andersene49d5ec2000-02-08 19:58:47 +0000332 if (!strcmp(filename, "-")) {
333 filename = "standard input";
334 errors = tail_lines(filename, 0, (long) n_units);
335 } else {
336 /* Not standard input. */
337 fd = open(filename, O_RDONLY);
338 if (fd == -1)
Eric Andersen86ab8a32000-06-02 03:21:42 +0000339 perror(filename);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000340
Erik Andersene49d5ec2000-02-08 19:58:47 +0000341 errors = tail_lines(filename, fd, (long) n_units);
342 close(fd);
343 }
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000344
Erik Andersene49d5ec2000-02-08 19:58:47 +0000345 return errors;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000346}
347
348extern int tail_main(int argc, char **argv)
349{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000350 int exit_status = 0;
351 int n_units = DEFAULT_N_LINES;
352 int n_tmp, i;
353 char opt;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000354
Erik Andersene49d5ec2000-02-08 19:58:47 +0000355 forever = print_headers = 0;
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000356
Erik Andersene49d5ec2000-02-08 19:58:47 +0000357 /* parse argv[] */
358 for (i = 1; i < argc; i++) {
359 if (argv[i][0] == '-') {
360 opt = argv[i][1];
361 switch (opt) {
362 case 'f':
363 forever = 1;
364 break;
365 case 'n':
366 n_tmp = 0;
367 if (++i < argc)
368 n_tmp = atoi(argv[i]);
369 if (n_tmp < 1)
370 usage(tail_usage);
371 n_units = n_tmp;
372 break;
373 case '-':
374 case 'h':
375 usage(tail_usage);
376 default:
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000377 if ((n_units = atoi(&argv[i][1])) < 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000378 errorMsg("invalid option -- %c\n", opt);
Erik Andersen94f5e0b2000-05-01 19:10:52 +0000379 usage(tail_usage);
380 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000381 }
382 } else {
383 break;
384 }
385 }
386
387 if (i + 1 < argc) {
388 if (forever) {
Matt Kraaid537a952000-07-14 01:51:25 +0000389 errorMsg("option -f is invalid with multiple files\n");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000390 usage(tail_usage);
391 }
392 print_headers = 1;
393 }
394
395 if (i >= argc) {
396 exit_status |= tail_file("-", n_units);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000397 } else {
Erik Andersene49d5ec2000-02-08 19:58:47 +0000398 for (; i < argc; i++)
399 exit_status |= tail_file(argv[i], n_units);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000400 }
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000401
Eric Andersenb6106152000-06-19 17:25:40 +0000402 return(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000403}
404
405
406#else
407// Here follows the code for the full featured tail code
408
409
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000410/* tail -- output the last part of file(s)
411 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
412
413 This program is free software; you can redistribute it and/or modify
414 it under the terms of the GNU General Public License as published by
415 the Free Software Foundation; either version 2, or (at your option)
416 any later version.
417
418 This program is distributed in the hope that it will be useful,
419 but WITHOUT ANY WARRANTY; without even the implied warranty of
420 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
421 GNU General Public License for more details.
422
423 You should have received a copy of the GNU General Public License
424 along with this program; if not, write to the Free Software
Eric Andersen1792f8c1999-12-09 06:11:36 +0000425 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000426
427 Original version by Paul Rubin <phr@ocf.berkeley.edu>.
428 Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
Eric Andersen1792f8c1999-12-09 06:11:36 +0000429 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
430
431 Rewrote the option parser, removed locales support,
432 and generally busyboxed, Erik Andersen <andersen@lineo.com>
433
434 */
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000435
436#include "internal.h"
437
438#include <stdio.h>
Erik Andersen4d1d0111999-12-17 18:44:15 +0000439#include <stdarg.h>
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000440#include <assert.h>
441#include <errno.h>
442#include <sys/types.h>
Eric Andersen1792f8c1999-12-09 06:11:36 +0000443#include <sys/types.h>
444#include <sys/stat.h>
445#include <fcntl.h>
446#include <ctype.h>
447
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000448
449
450/* Disable assertions. Some systems have broken assert macros. */
451#define NDEBUG 1
452
453
Erik Andersene49d5ec2000-02-08 19:58:47 +0000454static void detailed_error(int i, int errnum, char *fmt, ...)
Erik Andersen93d65132000-04-06 08:06:36 +0000455 __attribute__ ((format (printf, 3, 4)));
456static void detailed_error(int i, int errnum, char *fmt, ...)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000457{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000458 va_list arguments;
Eric Andersen1792f8c1999-12-09 06:11:36 +0000459
Erik Andersene49d5ec2000-02-08 19:58:47 +0000460 va_start(arguments, fmt);
461 vfprintf(stderr, fmt, arguments);
462 fprintf(stderr, "\n%s\n", strerror(errnum));
463 va_end(arguments);
464 exit(i);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000465}
466
467
468#define XWRITE(fd, buffer, n_bytes) \
469 do \
470 { \
471 assert ((fd) == 1); \
472 assert ((n_bytes) >= 0); \
473 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000474 detailed_error (EXIT_FAILURE, errno, "write error"); \
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000475 } \
476 while (0)
477
478/* Number of items to tail. */
479#define DEFAULT_N_LINES 10
480
481/* Size of atomic reads. */
482#ifndef BUFSIZ
483#define BUFSIZ (512 * 8)
484#endif
485
486/* If nonzero, interpret the numeric argument as the number of lines.
487 Otherwise, interpret it as the number of bytes. */
488static int count_lines;
489
490/* If nonzero, read from the end of one file until killed. */
491static int forever;
492
493/* If nonzero, read from the end of multiple files until killed. */
494static int forever_multiple;
495
496/* Array of file descriptors if forever_multiple is 1. */
497static int *file_descs;
498
499/* Array of file sizes if forever_multiple is 1. */
500static off_t *file_sizes;
501
502/* If nonzero, count from start of file instead of end. */
503static int from_start;
504
505/* If nonzero, print filename headers. */
506static int print_headers;
507
508/* When to print the filename banners. */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000509enum header_mode {
510 multiple_files, always, never
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000511};
512
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000513/* The name this program was run with. */
514char *program_name;
515
516/* Nonzero if we have ever read standard input. */
517static int have_read_stdin;
518
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000519
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000520static const char tail_usage[] = "tail [OPTION]... [FILE]...\n"
521#ifndef BB_FEATURE_TRIVIAL_HELP
522"\nPrint last 10 lines of each FILE to standard output.\n\
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000523With more than one FILE, precede each with a header giving the file name.\n\
524With no FILE, or when FILE is -, read standard input.\n\
525\n\
Eric Andersen1792f8c1999-12-09 06:11:36 +0000526 -c=N[kbm] output the last N bytes\n\
527 -f output appended data as the file grows\n\
528 -n=N output the last N lines, instead of last 10\n\
529 -q never output headers giving file names\n\
530 -v always output headers giving file names\n\
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000531\n\
Eric Andersen1792f8c1999-12-09 06:11:36 +0000532If the first character of N (bytes or lines) is a `+', output begins with \n\
533the Nth item from the start of each file, otherwise, print the last N items\n\
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000534in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2).\n"
535#endif
536;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000537
Erik Andersene49d5ec2000-02-08 19:58:47 +0000538static void write_header(const char *filename, const char *comment)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000539{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000540 static int first_file = 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000541
Erik Andersene49d5ec2000-02-08 19:58:47 +0000542 printf("%s==> %s%s%s <==\n", (first_file ? "" : "\n"), filename,
543 (comment ? ": " : ""), (comment ? comment : ""));
544 first_file = 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000545}
546
547/* Print the last N_LINES lines from the end of file FD.
548 Go backward through the file, reading `BUFSIZ' bytes at a time (except
549 probably the first), until we hit the start of the file or have
550 read NUMBER newlines.
551 POS starts out as the length of the file (the offset of the last
552 byte of the file + 1).
553 Return 0 if successful, 1 if an error occurred. */
554
555static int
Erik Andersene49d5ec2000-02-08 19:58:47 +0000556file_lines(const char *filename, int fd, long int n_lines, off_t pos)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000557{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000558 char buffer[BUFSIZ];
559 int bytes_read;
560 int i; /* Index into `buffer' for scanning. */
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000561
Erik Andersene49d5ec2000-02-08 19:58:47 +0000562 if (n_lines == 0)
563 return 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000564
Erik Andersene49d5ec2000-02-08 19:58:47 +0000565 /* Set `bytes_read' to the size of the last, probably partial, buffer;
566 0 < `bytes_read' <= `BUFSIZ'. */
567 bytes_read = pos % BUFSIZ;
568 if (bytes_read == 0)
569 bytes_read = BUFSIZ;
570 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
571 reads will be on block boundaries, which might increase efficiency. */
572 pos -= bytes_read;
573 lseek(fd, pos, SEEK_SET);
574 bytes_read = fullRead(fd, buffer, bytes_read);
575 if (bytes_read == -1) {
576 detailed_error(0, errno, "%s", filename);
577 return 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000578 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000579
580 /* Count the incomplete line on files that don't end with a newline. */
581 if (bytes_read && buffer[bytes_read - 1] != '\n')
582 --n_lines;
583
584 do {
585 /* Scan backward, counting the newlines in this bufferfull. */
586 for (i = bytes_read - 1; i >= 0; i--) {
587 /* Have we counted the requested number of newlines yet? */
588 if (buffer[i] == '\n' && n_lines-- == 0) {
589 /* If this newline wasn't the last character in the buffer,
590 print the text after it. */
591 if (i != bytes_read - 1)
592 XWRITE(STDOUT_FILENO, &buffer[i + 1],
593 bytes_read - (i + 1));
594 return 0;
595 }
596 }
597 /* Not enough newlines in that bufferfull. */
598 if (pos == 0) {
599 /* Not enough lines in the file; print the entire file. */
600 lseek(fd, (off_t) 0, SEEK_SET);
601 return 0;
602 }
603 pos -= BUFSIZ;
604 lseek(fd, pos, SEEK_SET);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000605 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000606 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
607 if (bytes_read == -1) {
608 detailed_error(0, errno, "%s", filename);
609 return 1;
610 }
611 return 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000612}
613
614/* Print the last N_LINES lines from the end of the standard input,
615 open for reading as pipe FD.
616 Buffer the text as a linked list of LBUFFERs, adding them as needed.
617 Return 0 if successful, 1 if an error occured. */
618
Erik Andersene49d5ec2000-02-08 19:58:47 +0000619static int pipe_lines(const char *filename, int fd, long int n_lines)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000620{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000621 struct linebuffer {
622 int nbytes, nlines;
623 char buffer[BUFSIZ];
624 struct linebuffer *next;
625 };
626 typedef struct linebuffer LBUFFER;
627 LBUFFER *first, *last, *tmp;
628 int i; /* Index into buffers. */
629 int total_lines = 0; /* Total number of newlines in all buffers. */
630 int errors = 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000631
Erik Andersene49d5ec2000-02-08 19:58:47 +0000632 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
633 first->nbytes = first->nlines = 0;
634 first->next = NULL;
635 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000636
Erik Andersene49d5ec2000-02-08 19:58:47 +0000637 /* Input is always read into a fresh buffer. */
638 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
639 tmp->nlines = 0;
640 tmp->next = NULL;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000641
Erik Andersene49d5ec2000-02-08 19:58:47 +0000642 /* Count the number of newlines just read. */
643 for (i = 0; i < tmp->nbytes; i++)
644 if (tmp->buffer[i] == '\n')
645 ++tmp->nlines;
646 total_lines += tmp->nlines;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000647
Erik Andersene49d5ec2000-02-08 19:58:47 +0000648 /* If there is enough room in the last buffer read, just append the new
649 one to it. This is because when reading from a pipe, `nbytes' can
650 often be very small. */
651 if (tmp->nbytes + last->nbytes < BUFSIZ) {
652 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
653 last->nbytes += tmp->nbytes;
654 last->nlines += tmp->nlines;
655 } else {
656 /* If there's not enough room, link the new buffer onto the end of
657 the list, then either free up the oldest buffer for the next
658 read if that would leave enough lines, or else malloc a new one.
659 Some compaction mechanism is possible but probably not
660 worthwhile. */
661 last = last->next = tmp;
662 if (total_lines - first->nlines > n_lines) {
663 tmp = first;
664 total_lines -= first->nlines;
665 first = first->next;
666 } else
667 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
668 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000669 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000670 if (tmp->nbytes == -1) {
671 detailed_error(0, errno, "%s", filename);
672 errors = 1;
673 free((char *) tmp);
674 goto free_lbuffers;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000675 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000676
Erik Andersene49d5ec2000-02-08 19:58:47 +0000677 free((char *) tmp);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000678
Erik Andersene49d5ec2000-02-08 19:58:47 +0000679 /* This prevents a core dump when the pipe contains no newlines. */
680 if (n_lines == 0)
681 goto free_lbuffers;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000682
Erik Andersene49d5ec2000-02-08 19:58:47 +0000683 /* Count the incomplete line on files that don't end with a newline. */
684 if (last->buffer[last->nbytes - 1] != '\n') {
685 ++last->nlines;
686 ++total_lines;
687 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000688
Erik Andersene49d5ec2000-02-08 19:58:47 +0000689 /* Run through the list, printing lines. First, skip over unneeded
690 buffers. */
691 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
692 total_lines -= tmp->nlines;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000693
Erik Andersene49d5ec2000-02-08 19:58:47 +0000694 /* Find the correct beginning, then print the rest of the file. */
695 if (total_lines > n_lines) {
696 char *cp;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000697
Erik Andersene49d5ec2000-02-08 19:58:47 +0000698 /* Skip `total_lines' - `n_lines' newlines. We made sure that
699 `total_lines' - `n_lines' <= `tmp->nlines'. */
700 cp = tmp->buffer;
701 for (i = total_lines - n_lines; i; --i)
702 while (*cp++ != '\n')
703 /* Do nothing. */ ;
704 i = cp - tmp->buffer;
705 } else
706 i = 0;
707 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000708
Erik Andersene49d5ec2000-02-08 19:58:47 +0000709 for (tmp = tmp->next; tmp; tmp = tmp->next)
710 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000711
Erik Andersene49d5ec2000-02-08 19:58:47 +0000712 free_lbuffers:
713 while (first) {
714 tmp = first->next;
715 free((char *) first);
716 first = tmp;
717 }
718 return errors;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000719}
720
721/* Print the last N_BYTES characters from the end of pipe FD.
722 This is a stripped down version of pipe_lines.
723 Return 0 if successful, 1 if an error occurred. */
724
Erik Andersene49d5ec2000-02-08 19:58:47 +0000725static int pipe_bytes(const char *filename, int fd, off_t n_bytes)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000726{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000727 struct charbuffer {
728 int nbytes;
729 char buffer[BUFSIZ];
730 struct charbuffer *next;
731 };
732 typedef struct charbuffer CBUFFER;
733 CBUFFER *first, *last, *tmp;
734 int i; /* Index into buffers. */
735 int total_bytes = 0; /* Total characters in all buffers. */
736 int errors = 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000737
Erik Andersene49d5ec2000-02-08 19:58:47 +0000738 first = last = (CBUFFER *) xmalloc(sizeof(CBUFFER));
739 first->nbytes = 0;
740 first->next = NULL;
741 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000742
Erik Andersene49d5ec2000-02-08 19:58:47 +0000743 /* Input is always read into a fresh buffer. */
744 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
745 tmp->next = NULL;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000746
Erik Andersene49d5ec2000-02-08 19:58:47 +0000747 total_bytes += tmp->nbytes;
748 /* If there is enough room in the last buffer read, just append the new
749 one to it. This is because when reading from a pipe, `nbytes' can
750 often be very small. */
751 if (tmp->nbytes + last->nbytes < BUFSIZ) {
752 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
753 last->nbytes += tmp->nbytes;
754 } else {
755 /* If there's not enough room, link the new buffer onto the end of
756 the list, then either free up the oldest buffer for the next
757 read if that would leave enough characters, or else malloc a new
758 one. Some compaction mechanism is possible but probably not
759 worthwhile. */
760 last = last->next = tmp;
761 if (total_bytes - first->nbytes > n_bytes) {
762 tmp = first;
763 total_bytes -= first->nbytes;
764 first = first->next;
765 } else {
766 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
767 }
768 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000769 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000770 if (tmp->nbytes == -1) {
771 detailed_error(0, errno, "%s", filename);
772 errors = 1;
773 free((char *) tmp);
774 goto free_cbuffers;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000775 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000776
Erik Andersene49d5ec2000-02-08 19:58:47 +0000777 free((char *) tmp);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000778
Erik Andersene49d5ec2000-02-08 19:58:47 +0000779 /* Run through the list, printing characters. First, skip over unneeded
780 buffers. */
781 for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
782 total_bytes -= tmp->nbytes;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000783
Erik Andersene49d5ec2000-02-08 19:58:47 +0000784 /* Find the correct beginning, then print the rest of the file.
785 We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
786 if (total_bytes > n_bytes)
787 i = total_bytes - n_bytes;
788 else
789 i = 0;
790 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000791
Erik Andersene49d5ec2000-02-08 19:58:47 +0000792 for (tmp = tmp->next; tmp; tmp = tmp->next)
793 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000794
Erik Andersene49d5ec2000-02-08 19:58:47 +0000795 free_cbuffers:
796 while (first) {
797 tmp = first->next;
798 free((char *) first);
799 first = tmp;
800 }
801 return errors;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000802}
803
804/* Skip N_BYTES characters from the start of pipe FD, and print
805 any extra characters that were read beyond that.
806 Return 1 on error, 0 if ok. */
807
Erik Andersene49d5ec2000-02-08 19:58:47 +0000808static int start_bytes(const char *filename, int fd, off_t n_bytes)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000809{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000810 char buffer[BUFSIZ];
811 int bytes_read = 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000812
Erik Andersene49d5ec2000-02-08 19:58:47 +0000813 while (n_bytes > 0 && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0)
814 n_bytes -= bytes_read;
815 if (bytes_read == -1) {
816 detailed_error(0, errno, "%s", filename);
817 return 1;
818 } else if (n_bytes < 0)
819 XWRITE(STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
820 return 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000821}
822
823/* Skip N_LINES lines at the start of file or pipe FD, and print
824 any extra characters that were read beyond that.
825 Return 1 on error, 0 if ok. */
826
Erik Andersene49d5ec2000-02-08 19:58:47 +0000827static int start_lines(const char *filename, int fd, long int n_lines)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000828{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000829 char buffer[BUFSIZ];
830 int bytes_read = 0;
831 int bytes_to_skip = 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000832
Erik Andersene49d5ec2000-02-08 19:58:47 +0000833 while (n_lines && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
834 bytes_to_skip = 0;
835 while (bytes_to_skip < bytes_read)
836 if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
837 break;
838 }
839 if (bytes_read == -1) {
840 detailed_error(0, errno, "%s", filename);
841 return 1;
842 } else if (bytes_to_skip < bytes_read) {
843 XWRITE(STDOUT_FILENO, &buffer[bytes_to_skip],
844 bytes_read - bytes_to_skip);
845 }
846 return 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000847}
848
849/* Display file FILENAME from the current position in FD to the end.
850 If `forever' is nonzero, keep reading from the end of the file
851 until killed. Return the number of bytes read from the file. */
852
Erik Andersene49d5ec2000-02-08 19:58:47 +0000853static long dump_remainder(const char *filename, int fd)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000854{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000855 char buffer[BUFSIZ];
856 int bytes_read;
857 long total;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000858
Erik Andersene49d5ec2000-02-08 19:58:47 +0000859 total = 0;
860 output:
861 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
862 XWRITE(STDOUT_FILENO, buffer, bytes_read);
863 total += bytes_read;
864 }
865 if (bytes_read == -1)
866 detailed_error(EXIT_FAILURE, errno, "%s", filename);
867 if (forever) {
868 fflush(stdout);
869 sleep(1);
870 goto output;
871 } else {
872 if (forever_multiple)
873 fflush(stdout);
874 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000875
Erik Andersene49d5ec2000-02-08 19:58:47 +0000876 return total;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000877}
878
879/* Tail NFILES (>1) files forever until killed. The file names are in
880 NAMES. The open file descriptors are in `file_descs', and the size
881 at which we stopped tailing them is in `file_sizes'. We loop over
882 each of them, doing an fstat to see if they have changed size. If
883 none of them have changed size in one iteration, we sleep for a
884 second and try again. We do this until the user interrupts us. */
885
Erik Andersene49d5ec2000-02-08 19:58:47 +0000886static void tail_forever(char **names, int nfiles)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000887{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000888 int last;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000889
Erik Andersene49d5ec2000-02-08 19:58:47 +0000890 last = -1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000891
Erik Andersene49d5ec2000-02-08 19:58:47 +0000892 while (1) {
893 int i;
894 int changed;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000895
Erik Andersene49d5ec2000-02-08 19:58:47 +0000896 changed = 0;
897 for (i = 0; i < nfiles; i++) {
898 struct stat stats;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000899
Erik Andersene49d5ec2000-02-08 19:58:47 +0000900 if (file_descs[i] < 0)
901 continue;
902 if (fstat(file_descs[i], &stats) < 0) {
903 detailed_error(0, errno, "%s", names[i]);
904 file_descs[i] = -1;
905 continue;
906 }
907 if (stats.st_size == file_sizes[i])
908 continue;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000909
Erik Andersene49d5ec2000-02-08 19:58:47 +0000910 /* This file has changed size. Print out what we can, and
911 then keep looping. */
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000912
Erik Andersene49d5ec2000-02-08 19:58:47 +0000913 changed = 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000914
Erik Andersene49d5ec2000-02-08 19:58:47 +0000915 if (stats.st_size < file_sizes[i]) {
916 write_header(names[i], "file truncated");
917 last = i;
918 lseek(file_descs[i], stats.st_size, SEEK_SET);
919 file_sizes[i] = stats.st_size;
920 continue;
921 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000922
Erik Andersene49d5ec2000-02-08 19:58:47 +0000923 if (i != last) {
924 if (print_headers)
925 write_header(names[i], NULL);
926 last = i;
927 }
928 file_sizes[i] += dump_remainder(names[i], file_descs[i]);
929 }
930
931 /* If none of the files changed size, sleep. */
932 if (!changed)
933 sleep(1);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000934 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000935}
936
937/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
938 Return 0 if successful, 1 if an error occurred. */
939
Erik Andersene49d5ec2000-02-08 19:58:47 +0000940static int tail_bytes(const char *filename, int fd, off_t n_bytes)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000941{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000942 struct stat stats;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000943
Erik Andersene49d5ec2000-02-08 19:58:47 +0000944 /* FIXME: resolve this like in dd.c. */
945 /* Use fstat instead of checking for errno == ESPIPE because
946 lseek doesn't work on some special files but doesn't return an
947 error, either. */
948 if (fstat(fd, &stats)) {
949 detailed_error(0, errno, "%s", filename);
950 return 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000951 }
Erik Andersene49d5ec2000-02-08 19:58:47 +0000952
953 if (from_start) {
954 if (S_ISREG(stats.st_mode))
955 lseek(fd, n_bytes, SEEK_CUR);
956 else if (start_bytes(filename, fd, n_bytes))
957 return 1;
958 dump_remainder(filename, fd);
959 } else {
960 if (S_ISREG(stats.st_mode)) {
961 off_t current_pos, end_pos;
962 size_t bytes_remaining;
963
964 if ((current_pos = lseek(fd, (off_t) 0, SEEK_CUR)) != -1
965 && (end_pos = lseek(fd, (off_t) 0, SEEK_END)) != -1) {
966 off_t diff;
967
968 /* Be careful here. The current position may actually be
969 beyond the end of the file. */
970 bytes_remaining = (diff =
971 end_pos - current_pos) < 0 ? 0 : diff;
972 } else {
973 detailed_error(0, errno, "%s", filename);
974 return 1;
975 }
976
977 if (bytes_remaining <= n_bytes) {
978 /* From the current position to end of file, there are no
979 more bytes than have been requested. So reposition the
980 file pointer to the incoming current position and print
981 everything after that. */
982 lseek(fd, current_pos, SEEK_SET);
983 } else {
984 /* There are more bytes remaining than were requested.
985 Back up. */
986 lseek(fd, -n_bytes, SEEK_END);
987 }
988 dump_remainder(filename, fd);
989 } else
990 return pipe_bytes(filename, fd, n_bytes);
991 }
992 return 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000993}
994
995/* Output the last N_LINES lines of file FILENAME open for reading in FD.
996 Return 0 if successful, 1 if an error occurred. */
997
Erik Andersene49d5ec2000-02-08 19:58:47 +0000998static int tail_lines(const char *filename, int fd, long int n_lines)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000999{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001000 struct stat stats;
1001 off_t length;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001002
Erik Andersene49d5ec2000-02-08 19:58:47 +00001003 if (fstat(fd, &stats)) {
1004 detailed_error(0, errno, "%s", filename);
1005 return 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001006 }
Erik Andersene49d5ec2000-02-08 19:58:47 +00001007
1008 if (from_start) {
1009 if (start_lines(filename, fd, n_lines))
1010 return 1;
1011 dump_remainder(filename, fd);
1012 } else {
1013 /* Use file_lines only if FD refers to a regular file with
1014 its file pointer positioned at beginning of file. */
1015 /* FIXME: adding the lseek conjunct is a kludge.
1016 Once there's a reasonable test suite, fix the true culprit:
1017 file_lines. file_lines shouldn't presume that the input
1018 file pointer is initially positioned to beginning of file. */
1019 if (S_ISREG(stats.st_mode)
1020 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
1021 length = lseek(fd, (off_t) 0, SEEK_END);
1022 if (length != 0 && file_lines(filename, fd, n_lines, length))
1023 return 1;
1024 dump_remainder(filename, fd);
1025 } else
1026 return pipe_lines(filename, fd, n_lines);
1027 }
1028 return 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001029}
1030
1031/* Display the last N_UNITS units of file FILENAME, open for reading
1032 in FD.
1033 Return 0 if successful, 1 if an error occurred. */
1034
Erik Andersene49d5ec2000-02-08 19:58:47 +00001035static int tail(const char *filename, int fd, off_t n_units)
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001036{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001037 if (count_lines)
1038 return tail_lines(filename, fd, (long) n_units);
1039 else
1040 return tail_bytes(filename, fd, n_units);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001041}
1042
1043/* Display the last N_UNITS units of file FILENAME.
1044 "-" for FILENAME means the standard input.
1045 FILENUM is this file's index in the list of files the user gave.
1046 Return 0 if successful, 1 if an error occurred. */
1047
Erik Andersene49d5ec2000-02-08 19:58:47 +00001048static int tail_file(const char *filename, off_t n_units, int filenum)
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001049{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001050 int fd, errors;
1051 struct stat stats;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001052
Erik Andersene49d5ec2000-02-08 19:58:47 +00001053 if (!strcmp(filename, "-")) {
1054 have_read_stdin = 1;
1055 filename = "standard input";
1056 if (print_headers)
1057 write_header(filename, NULL);
1058 errors = tail(filename, 0, n_units);
1059 if (forever_multiple) {
1060 if (fstat(0, &stats) < 0) {
1061 detailed_error(0, errno, "standard input");
1062 errors = 1;
1063 } else if (!S_ISREG(stats.st_mode)) {
1064 detailed_error(0, 0,
1065 "standard input: cannot follow end of non-regular file");
1066 errors = 1;
1067 }
1068 if (errors)
1069 file_descs[filenum] = -1;
1070 else {
1071 file_descs[filenum] = 0;
1072 file_sizes[filenum] = stats.st_size;
1073 }
1074 }
1075 } else {
1076 /* Not standard input. */
1077 fd = open(filename, O_RDONLY);
1078 if (fd == -1) {
1079 if (forever_multiple)
1080 file_descs[filenum] = -1;
1081 detailed_error(0, errno, "%s", filename);
1082 errors = 1;
1083 } else {
1084 if (print_headers)
1085 write_header(filename, NULL);
1086 errors = tail(filename, fd, n_units);
1087 if (forever_multiple) {
1088 if (fstat(fd, &stats) < 0) {
1089 detailed_error(0, errno, "%s", filename);
1090 errors = 1;
1091 } else if (!S_ISREG(stats.st_mode)) {
1092 detailed_error(0, 0,
1093 "%s: cannot follow end of non-regular file",
1094 filename);
1095 errors = 1;
1096 }
1097 if (errors) {
1098 close(fd);
1099 file_descs[filenum] = -1;
1100 } else {
1101 file_descs[filenum] = fd;
1102 file_sizes[filenum] = stats.st_size;
1103 }
1104 } else {
1105 if (close(fd)) {
1106 detailed_error(0, errno, "%s", filename);
1107 errors = 1;
1108 }
1109 }
1110 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001111 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001112
Erik Andersene49d5ec2000-02-08 19:58:47 +00001113 return errors;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001114}
1115
Erik Andersene49d5ec2000-02-08 19:58:47 +00001116extern int tail_main(int argc, char **argv)
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001117{
Erik Andersene49d5ec2000-02-08 19:58:47 +00001118 int stopit = 0;
1119 enum header_mode header_mode = multiple_files;
1120 int exit_status = 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001121
Erik Andersene49d5ec2000-02-08 19:58:47 +00001122 /* If from_start, the number of items to skip before printing; otherwise,
1123 the number of items at the end of the file to print. Initially, -1
1124 means the value has not been set. */
1125 off_t n_units = -1;
1126 int n_files;
1127 char **file;
1128
1129 program_name = argv[0];
1130 have_read_stdin = 0;
1131 count_lines = 1;
1132 forever = forever_multiple = from_start = print_headers = 0;
1133
1134 /* Parse any options */
1135 //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv);
1136 while (--argc > 0 && (**(++argv) == '-' || **argv == '+')) {
1137 if (**argv == '+') {
1138 from_start = 1;
1139 }
1140 stopit = 0;
1141 while (stopit == 0 && *(++(*argv))) {
1142 switch (**argv) {
1143 case 'c':
1144 count_lines = 0;
1145
1146 if (--argc < 1) {
1147 usage(tail_usage);
1148 }
1149 n_units = getNum(*(++argv));
1150 stopit = 1;
1151 break;
1152
1153 case 'f':
1154 forever = 1;
1155 break;
1156
1157 case 'n':
1158 count_lines = 1;
1159
1160 if (--argc < 1) {
1161 usage(tail_usage);
1162 }
1163 n_units = atol(*(++argv));
1164 stopit = 1;
1165 break;
1166
1167 case 'q':
1168 header_mode = never;
1169 break;
1170
1171 case 'v':
1172 header_mode = always;
1173 break;
1174
1175 default:
1176 usage(tail_usage);
1177 }
1178 }
Eric Andersen1792f8c1999-12-09 06:11:36 +00001179 }
Eric Andersen1792f8c1999-12-09 06:11:36 +00001180
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001181
Erik Andersene49d5ec2000-02-08 19:58:47 +00001182 if (n_units == -1)
1183 n_units = DEFAULT_N_LINES;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001184
Erik Andersene49d5ec2000-02-08 19:58:47 +00001185 /* To start printing with item N_UNITS from the start of the file, skip
1186 N_UNITS - 1 items. `tail +0' is actually meaningless, but for Unix
1187 compatibility it's treated the same as `tail +1'. */
1188 if (from_start) {
1189 if (n_units)
1190 --n_units;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001191 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001192
Erik Andersene49d5ec2000-02-08 19:58:47 +00001193 n_files = argc;
1194 file = argv;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001195
Erik Andersene49d5ec2000-02-08 19:58:47 +00001196 if (n_files > 1 && forever) {
1197 forever_multiple = 1;
1198 forever = 0;
1199 file_descs = (int *) xmalloc(n_files * sizeof(int));
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001200
Erik Andersene49d5ec2000-02-08 19:58:47 +00001201 file_sizes = (off_t *) xmalloc(n_files * sizeof(off_t));
1202 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001203
Erik Andersene49d5ec2000-02-08 19:58:47 +00001204 if (header_mode == always
1205 || (header_mode == multiple_files && n_files > 1))
1206 print_headers = 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001207
Erik Andersene49d5ec2000-02-08 19:58:47 +00001208 if (n_files == 0) {
1209 exit_status |= tail_file("-", n_units, 0);
1210 } else {
1211 int i;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001212
Erik Andersene49d5ec2000-02-08 19:58:47 +00001213 for (i = 0; i < n_files; i++)
1214 exit_status |= tail_file(file[i], n_units, i);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001215
Erik Andersene49d5ec2000-02-08 19:58:47 +00001216 if (forever_multiple)
1217 tail_forever(file, n_files);
1218 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001219
Erik Andersene49d5ec2000-02-08 19:58:47 +00001220 if (have_read_stdin && close(0) < 0)
1221 detailed_error(EXIT_FAILURE, errno, "-");
1222 if (fclose(stdout) == EOF)
1223 detailed_error(EXIT_FAILURE, errno, "write error");
1224 exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001225}
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001226
1227
1228#endif