blob: 5198892b63df48a85f9ec0d0cc1c48f7b5d35fdf [file] [log] [blame]
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001#include "internal.h"
2/* This file contains _two_ implementations of tail. One is
3 * a bit more full featured, but costs 6k. The other (i.e. the
4 * SIMPLE_TAIL one) is less capable, but is good enough for about
5 * 99% of the things folks want to use tail for, and only costs 2k.
6 */
7
8
9#ifdef BB_FEATURE_SIMPLE_TAIL
10
11/* tail -- output the last part of file(s)
12 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2, or (at your option)
17 any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27
28 Original version by Paul Rubin <phr@ocf.berkeley.edu>.
29 Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
30 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
31
32 Rewrote the option parser, removed locales support,
33 and generally busyboxed, Erik Andersen <andersen@lineo.com>
34
35 Removed superfluous options and associated code ("-c", "-n", "-q").
36 Removed "tail -f" suport for multiple files.
37 Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>.
38
39 */
40
41
42#include <stdio.h>
43#include <stdarg.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <fcntl.h>
47#include <ctype.h>
48
49
50#define XWRITE(fd, buffer, n_bytes) \
51 do { \
52 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
53 error("write error"); \
54 } while (0)
55
56/* Number of items to tail. */
57#define DEFAULT_N_LINES 10
58
59/* Size of atomic reads. */
60#ifndef BUFSIZ
61#define BUFSIZ (512 * 8)
62#endif
63
64/* If nonzero, read from the end of one file until killed. */
65static int forever;
66
67/* If nonzero, print filename headers. */
68static int print_headers;
69
70const char tail_usage[] =
71 "tail [OPTION] [FILE]...\n\n"
72 "Print last 10 lines of each FILE to standard output.\n"
73 "With more than one FILE, precede each with a header giving the\n"
74 "file name. With no FILE, or when FILE is -, read standard input.\n\n"
75 "Options:\n"
76 "\t-n NUM\t\tPrint last NUM lines instead of first 10\n"
77 "\t-f\t\tOutput data as the file grows. This version\n"
78 "\t\t\tof 'tail -f' supports only one file at a time.\n";
79
80
81static void write_header(const char *filename)
82{
83 static int first_file = 1;
84
85 printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
86 first_file = 0;
87}
88
89/* Print the last N_LINES lines from the end of file FD.
90 Go backward through the file, reading `BUFSIZ' bytes at a time (except
91 probably the first), until we hit the start of the file or have
92 read NUMBER newlines.
93 POS starts out as the length of the file (the offset of the last
94 byte of the file + 1).
95 Return 0 if successful, 1 if an error occurred. */
96
97static int
98file_lines(const char *filename, int fd, long int n_lines, off_t pos)
99{
100 char buffer[BUFSIZ];
101 int bytes_read;
102 int i; /* Index into `buffer' for scanning. */
103
104 if (n_lines == 0)
105 return 0;
106
107 /* Set `bytes_read' to the size of the last, probably partial, buffer;
108 0 < `bytes_read' <= `BUFSIZ'. */
109 bytes_read = pos % BUFSIZ;
110 if (bytes_read == 0)
111 bytes_read = BUFSIZ;
112 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
113 reads will be on block boundaries, which might increase efficiency. */
114 pos -= bytes_read;
115 lseek(fd, pos, SEEK_SET);
116 bytes_read = fullRead(fd, buffer, bytes_read);
117 if (bytes_read == -1)
118 error("read error");
119
120 /* Count the incomplete line on files that don't end with a newline. */
121 if (bytes_read && buffer[bytes_read - 1] != '\n')
122 --n_lines;
123
124 do {
125 /* Scan backward, counting the newlines in this bufferfull. */
126 for (i = bytes_read - 1; i >= 0; i--) {
127 /* Have we counted the requested number of newlines yet? */
128 if (buffer[i] == '\n' && n_lines-- == 0) {
129 /* If this newline wasn't the last character in the buffer,
130 print the text after it. */
131 if (i != bytes_read - 1)
132 XWRITE(STDOUT_FILENO, &buffer[i + 1],
133 bytes_read - (i + 1));
134 return 0;
135 }
136 }
137 /* Not enough newlines in that bufferfull. */
138 if (pos == 0) {
139 /* Not enough lines in the file; print the entire file. */
140 lseek(fd, (off_t) 0, SEEK_SET);
141 return 0;
142 }
143 pos -= BUFSIZ;
144 lseek(fd, pos, SEEK_SET);
145 }
146 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
147 if (bytes_read == -1)
148 error("read error");
149
150 return 0;
151}
152
153/* Print the last N_LINES lines from the end of the standard input,
154 open for reading as pipe FD.
155 Buffer the text as a linked list of LBUFFERs, adding them as needed.
156 Return 0 if successful, 1 if an error occured. */
157
158static int pipe_lines(const char *filename, int fd, long int n_lines)
159{
160 struct linebuffer {
161 int nbytes, nlines;
162 char buffer[BUFSIZ];
163 struct linebuffer *next;
164 };
165 typedef struct linebuffer LBUFFER;
166 LBUFFER *first, *last, *tmp;
167 int i; /* Index into buffers. */
168 int total_lines = 0; /* Total number of newlines in all buffers. */
169 int errors = 0;
170
171 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
172 first->nbytes = first->nlines = 0;
173 first->next = NULL;
174 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
175
176 /* Input is always read into a fresh buffer. */
177 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
178 tmp->nlines = 0;
179 tmp->next = NULL;
180
181 /* Count the number of newlines just read. */
182 for (i = 0; i < tmp->nbytes; i++)
183 if (tmp->buffer[i] == '\n')
184 ++tmp->nlines;
185 total_lines += tmp->nlines;
186
187 /* If there is enough room in the last buffer read, just append the new
188 one to it. This is because when reading from a pipe, `nbytes' can
189 often be very small. */
190 if (tmp->nbytes + last->nbytes < BUFSIZ) {
191 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
192 last->nbytes += tmp->nbytes;
193 last->nlines += tmp->nlines;
194 } else {
195 /* If there's not enough room, link the new buffer onto the end of
196 the list, then either free up the oldest buffer for the next
197 read if that would leave enough lines, or else malloc a new one.
198 Some compaction mechanism is possible but probably not
199 worthwhile. */
200 last = last->next = tmp;
201 if (total_lines - first->nlines > n_lines) {
202 tmp = first;
203 total_lines -= first->nlines;
204 first = first->next;
205 } else
206 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
207 }
208 }
209 if (tmp->nbytes == -1)
210 error("read error");
211
212 free((char *) tmp);
213
214 /* This prevents a core dump when the pipe contains no newlines. */
215 if (n_lines == 0)
216 goto free_lbuffers;
217
218 /* Count the incomplete line on files that don't end with a newline. */
219 if (last->buffer[last->nbytes - 1] != '\n') {
220 ++last->nlines;
221 ++total_lines;
222 }
223
224 /* Run through the list, printing lines. First, skip over unneeded
225 buffers. */
226 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
227 total_lines -= tmp->nlines;
228
229 /* Find the correct beginning, then print the rest of the file. */
230 if (total_lines > n_lines) {
231 char *cp;
232
233 /* Skip `total_lines' - `n_lines' newlines. We made sure that
234 `total_lines' - `n_lines' <= `tmp->nlines'. */
235 cp = tmp->buffer;
236 for (i = total_lines - n_lines; i; --i)
237 while (*cp++ != '\n')
238 /* Do nothing. */ ;
239 i = cp - tmp->buffer;
240 } else
241 i = 0;
242 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
243
244 for (tmp = tmp->next; tmp; tmp = tmp->next)
245 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
246
247 free_lbuffers:
248 while (first) {
249 tmp = first->next;
250 free((char *) first);
251 first = tmp;
252 }
253 return errors;
254}
255
256/* Display file FILENAME from the current position in FD to the end.
257 If `forever' is nonzero, keep reading from the end of the file
258 until killed. Return the number of bytes read from the file. */
259
260static long dump_remainder(const char *filename, int fd)
261{
262 char buffer[BUFSIZ];
263 int bytes_read;
264 long total;
265
266 total = 0;
267 output:
268 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
269 XWRITE(STDOUT_FILENO, buffer, bytes_read);
270 total += bytes_read;
271 }
272 if (bytes_read == -1)
273 error("read error");
274 if (forever) {
275 fflush(stdout);
276 sleep(1);
277 goto output;
278 }
279
280 return total;
281}
282
283/* Output the last N_LINES lines of file FILENAME open for reading in FD.
284 Return 0 if successful, 1 if an error occurred. */
285
286static int tail_lines(const char *filename, int fd, long int n_lines)
287{
288 struct stat stats;
289 off_t length;
290
291 if (print_headers)
292 write_header(filename);
293
294 if (fstat(fd, &stats))
295 error("fstat error");
296
297 /* Use file_lines only if FD refers to a regular file with
298 its file pointer positioned at beginning of file. */
299 /* FIXME: adding the lseek conjunct is a kludge.
300 Once there's a reasonable test suite, fix the true culprit:
301 file_lines. file_lines shouldn't presume that the input
302 file pointer is initially positioned to beginning of file. */
303 if (S_ISREG(stats.st_mode)
304 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
305 length = lseek(fd, (off_t) 0, SEEK_END);
306 if (length != 0 && file_lines(filename, fd, n_lines, length))
307 return 1;
308 dump_remainder(filename, fd);
309 } else
310 return pipe_lines(filename, fd, n_lines);
311
312 return 0;
313}
314
315/* Display the last N_UNITS lines of file FILENAME.
316 "-" for FILENAME means the standard input.
317 Return 0 if successful, 1 if an error occurred. */
318
319static int tail_file(const char *filename, off_t n_units)
320{
321 int fd, errors;
322
323 if (!strcmp(filename, "-")) {
324 filename = "standard input";
325 errors = tail_lines(filename, 0, (long) n_units);
326 } else {
327 /* Not standard input. */
328 fd = open(filename, O_RDONLY);
329 if (fd == -1)
330 error("open error");
331
332 errors = tail_lines(filename, fd, (long) n_units);
333 close(fd);
334 }
335
336 return errors;
337}
338
339extern int tail_main(int argc, char **argv)
340{
341 int exit_status = 0;
342 int n_units = DEFAULT_N_LINES;
343 int n_tmp, i;
344 char opt;
345
346 forever = print_headers = 0;
347
348 /* parse argv[] */
349 for (i = 1; i < argc; i++) {
350 if (argv[i][0] == '-') {
351 opt = argv[i][1];
352 switch (opt) {
353 case 'f':
354 forever = 1;
355 break;
356 case 'n':
357 n_tmp = 0;
358 if (++i < argc)
359 n_tmp = atoi(argv[i]);
360 if (n_tmp < 1)
361 usage(tail_usage);
362 n_units = n_tmp;
363 break;
364 case '-':
365 case 'h':
366 usage(tail_usage);
367 default:
368 fprintf(stderr, "tail: invalid option -- %c\n", opt);
369 usage(tail_usage);
370 }
371 } else {
372 break;
373 }
374 }
375
376 if (i + 1 < argc) {
377 if (forever) {
378 fprintf(stderr,
379 "tail: option -f is invalid with multiple files\n");
380 usage(tail_usage);
381 }
382 print_headers = 1;
383 }
384
385 if (i >= argc) {
386 exit_status |= tail_file("-", n_units);
387 } else {
388 for (; i < argc; i++)
389 exit_status |= tail_file(argv[i], n_units);
390 }
391
392 exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
393}
394
395
396#else
397// Here follows the code for the full featured tail code
398
399
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000400/* tail -- output the last part of file(s)
401 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
402
403 This program is free software; you can redistribute it and/or modify
404 it under the terms of the GNU General Public License as published by
405 the Free Software Foundation; either version 2, or (at your option)
406 any later version.
407
408 This program is distributed in the hope that it will be useful,
409 but WITHOUT ANY WARRANTY; without even the implied warranty of
410 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
411 GNU General Public License for more details.
412
413 You should have received a copy of the GNU General Public License
414 along with this program; if not, write to the Free Software
Eric Andersen1792f8c1999-12-09 06:11:36 +0000415 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000416
417 Original version by Paul Rubin <phr@ocf.berkeley.edu>.
418 Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
Eric Andersen1792f8c1999-12-09 06:11:36 +0000419 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
420
421 Rewrote the option parser, removed locales support,
422 and generally busyboxed, Erik Andersen <andersen@lineo.com>
423
424 */
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000425
426#include "internal.h"
427
428#include <stdio.h>
Erik Andersen4d1d0111999-12-17 18:44:15 +0000429#include <stdarg.h>
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000430#include <assert.h>
431#include <errno.h>
432#include <sys/types.h>
Eric Andersen1792f8c1999-12-09 06:11:36 +0000433#include <sys/types.h>
434#include <sys/stat.h>
435#include <fcntl.h>
436#include <ctype.h>
437
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000438
439
440/* Disable assertions. Some systems have broken assert macros. */
441#define NDEBUG 1
442
443
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000444static void detailed_error(int i, int errnum, char* fmt, ...)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000445{
Eric Andersen1792f8c1999-12-09 06:11:36 +0000446 va_list arguments;
447
448 va_start(arguments, fmt);
449 vfprintf(stderr, fmt, arguments);
450 fprintf(stderr, "\n%s\n", strerror( errnum));
451 va_end(arguments);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000452 exit(i);
453}
454
455
456#define XWRITE(fd, buffer, n_bytes) \
457 do \
458 { \
459 assert ((fd) == 1); \
460 assert ((n_bytes) >= 0); \
461 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000462 detailed_error (EXIT_FAILURE, errno, "write error"); \
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000463 } \
464 while (0)
465
466/* Number of items to tail. */
467#define DEFAULT_N_LINES 10
468
469/* Size of atomic reads. */
470#ifndef BUFSIZ
471#define BUFSIZ (512 * 8)
472#endif
473
474/* If nonzero, interpret the numeric argument as the number of lines.
475 Otherwise, interpret it as the number of bytes. */
476static int count_lines;
477
478/* If nonzero, read from the end of one file until killed. */
479static int forever;
480
481/* If nonzero, read from the end of multiple files until killed. */
482static int forever_multiple;
483
484/* Array of file descriptors if forever_multiple is 1. */
485static int *file_descs;
486
487/* Array of file sizes if forever_multiple is 1. */
488static off_t *file_sizes;
489
490/* If nonzero, count from start of file instead of end. */
491static int from_start;
492
493/* If nonzero, print filename headers. */
494static int print_headers;
495
496/* When to print the filename banners. */
497enum header_mode
498{
499 multiple_files, always, never
500};
501
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000502/* The name this program was run with. */
503char *program_name;
504
505/* Nonzero if we have ever read standard input. */
506static int have_read_stdin;
507
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000508
509static const char tail_usage[] =
510"tail [OPTION]... [FILE]...\n\
Eric Andersen1792f8c1999-12-09 06:11:36 +0000511\n\
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000512Print last 10 lines of each FILE to standard output.\n\
513With more than one FILE, precede each with a header giving the file name.\n\
514With no FILE, or when FILE is -, read standard input.\n\
515\n\
Eric Andersen1792f8c1999-12-09 06:11:36 +0000516 -c=N[kbm] output the last N bytes\n\
517 -f output appended data as the file grows\n\
518 -n=N output the last N lines, instead of last 10\n\
519 -q never output headers giving file names\n\
520 -v always output headers giving file names\n\
521 --help display this help and exit\n\
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000522\n\
Eric Andersen1792f8c1999-12-09 06:11:36 +0000523If the first character of N (bytes or lines) is a `+', output begins with \n\
524the Nth item from the start of each file, otherwise, print the last N items\n\
525in the file. N bytes may be suffixed by k (x1024), b (x512), or m (1024^2).\n\n";
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000526
527static void
528write_header (const char *filename, const char *comment)
529{
530 static int first_file = 1;
531
532 printf ("%s==> %s%s%s <==\n", (first_file ? "" : "\n"), filename,
533 (comment ? ": " : ""),
534 (comment ? comment : ""));
535 first_file = 0;
536}
537
538/* Print the last N_LINES lines from the end of file FD.
539 Go backward through the file, reading `BUFSIZ' bytes at a time (except
540 probably the first), until we hit the start of the file or have
541 read NUMBER newlines.
542 POS starts out as the length of the file (the offset of the last
543 byte of the file + 1).
544 Return 0 if successful, 1 if an error occurred. */
545
546static int
547file_lines (const char *filename, int fd, long int n_lines, off_t pos)
548{
549 char buffer[BUFSIZ];
550 int bytes_read;
551 int i; /* Index into `buffer' for scanning. */
552
553 if (n_lines == 0)
554 return 0;
555
556 /* Set `bytes_read' to the size of the last, probably partial, buffer;
557 0 < `bytes_read' <= `BUFSIZ'. */
558 bytes_read = pos % BUFSIZ;
559 if (bytes_read == 0)
560 bytes_read = BUFSIZ;
561 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
562 reads will be on block boundaries, which might increase efficiency. */
563 pos -= bytes_read;
564 lseek (fd, pos, SEEK_SET);
Eric Andersen1792f8c1999-12-09 06:11:36 +0000565 bytes_read = fullRead (fd, buffer, bytes_read);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000566 if (bytes_read == -1)
567 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000568 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000569 return 1;
570 }
571
572 /* Count the incomplete line on files that don't end with a newline. */
573 if (bytes_read && buffer[bytes_read - 1] != '\n')
574 --n_lines;
575
576 do
577 {
578 /* Scan backward, counting the newlines in this bufferfull. */
579 for (i = bytes_read - 1; i >= 0; i--)
580 {
581 /* Have we counted the requested number of newlines yet? */
582 if (buffer[i] == '\n' && n_lines-- == 0)
583 {
584 /* If this newline wasn't the last character in the buffer,
585 print the text after it. */
586 if (i != bytes_read - 1)
587 XWRITE (STDOUT_FILENO, &buffer[i + 1], bytes_read - (i + 1));
588 return 0;
589 }
590 }
591 /* Not enough newlines in that bufferfull. */
592 if (pos == 0)
593 {
594 /* Not enough lines in the file; print the entire file. */
595 lseek (fd, (off_t) 0, SEEK_SET);
596 return 0;
597 }
598 pos -= BUFSIZ;
599 lseek (fd, pos, SEEK_SET);
600 }
Eric Andersen1792f8c1999-12-09 06:11:36 +0000601 while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000602 if (bytes_read == -1)
603 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000604 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000605 return 1;
606 }
607 return 0;
608}
609
610/* Print the last N_LINES lines from the end of the standard input,
611 open for reading as pipe FD.
612 Buffer the text as a linked list of LBUFFERs, adding them as needed.
613 Return 0 if successful, 1 if an error occured. */
614
615static int
616pipe_lines (const char *filename, int fd, long int n_lines)
617{
618 struct linebuffer
619 {
620 int nbytes, nlines;
621 char buffer[BUFSIZ];
622 struct linebuffer *next;
623 };
624 typedef struct linebuffer LBUFFER;
625 LBUFFER *first, *last, *tmp;
626 int i; /* Index into buffers. */
627 int total_lines = 0; /* Total number of newlines in all buffers. */
628 int errors = 0;
629
630 first = last = (LBUFFER *) xmalloc (sizeof (LBUFFER));
631 first->nbytes = first->nlines = 0;
632 first->next = NULL;
633 tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));
634
635 /* Input is always read into a fresh buffer. */
Eric Andersen1792f8c1999-12-09 06:11:36 +0000636 while ((tmp->nbytes = fullRead (fd, tmp->buffer, BUFSIZ)) > 0)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000637 {
638 tmp->nlines = 0;
639 tmp->next = NULL;
640
641 /* Count the number of newlines just read. */
642 for (i = 0; i < tmp->nbytes; i++)
643 if (tmp->buffer[i] == '\n')
644 ++tmp->nlines;
645 total_lines += tmp->nlines;
646
647 /* If there is enough room in the last buffer read, just append the new
648 one to it. This is because when reading from a pipe, `nbytes' can
649 often be very small. */
650 if (tmp->nbytes + last->nbytes < BUFSIZ)
651 {
652 memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
653 last->nbytes += tmp->nbytes;
654 last->nlines += tmp->nlines;
655 }
656 else
657 {
658 /* If there's not enough room, link the new buffer onto the end of
659 the list, then either free up the oldest buffer for the next
660 read if that would leave enough lines, or else malloc a new one.
661 Some compaction mechanism is possible but probably not
662 worthwhile. */
663 last = last->next = tmp;
664 if (total_lines - first->nlines > n_lines)
665 {
666 tmp = first;
667 total_lines -= first->nlines;
668 first = first->next;
669 }
670 else
671 tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));
672 }
673 }
674 if (tmp->nbytes == -1)
675 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000676 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000677 errors = 1;
678 free ((char *) tmp);
679 goto free_lbuffers;
680 }
681
682 free ((char *) tmp);
683
684 /* This prevents a core dump when the pipe contains no newlines. */
685 if (n_lines == 0)
686 goto free_lbuffers;
687
688 /* Count the incomplete line on files that don't end with a newline. */
689 if (last->buffer[last->nbytes - 1] != '\n')
690 {
691 ++last->nlines;
692 ++total_lines;
693 }
694
695 /* Run through the list, printing lines. First, skip over unneeded
696 buffers. */
697 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
698 total_lines -= tmp->nlines;
699
700 /* Find the correct beginning, then print the rest of the file. */
701 if (total_lines > n_lines)
702 {
703 char *cp;
704
705 /* Skip `total_lines' - `n_lines' newlines. We made sure that
706 `total_lines' - `n_lines' <= `tmp->nlines'. */
707 cp = tmp->buffer;
708 for (i = total_lines - n_lines; i; --i)
709 while (*cp++ != '\n')
710 /* Do nothing. */ ;
711 i = cp - tmp->buffer;
712 }
713 else
714 i = 0;
715 XWRITE (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
716
717 for (tmp = tmp->next; tmp; tmp = tmp->next)
718 XWRITE (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
719
720free_lbuffers:
721 while (first)
722 {
723 tmp = first->next;
724 free ((char *) first);
725 first = tmp;
726 }
727 return errors;
728}
729
730/* Print the last N_BYTES characters from the end of pipe FD.
731 This is a stripped down version of pipe_lines.
732 Return 0 if successful, 1 if an error occurred. */
733
734static int
735pipe_bytes (const char *filename, int fd, off_t n_bytes)
736{
737 struct charbuffer
738 {
739 int nbytes;
740 char buffer[BUFSIZ];
741 struct charbuffer *next;
742 };
743 typedef struct charbuffer CBUFFER;
744 CBUFFER *first, *last, *tmp;
745 int i; /* Index into buffers. */
746 int total_bytes = 0; /* Total characters in all buffers. */
747 int errors = 0;
748
749 first = last = (CBUFFER *) xmalloc (sizeof (CBUFFER));
750 first->nbytes = 0;
751 first->next = NULL;
752 tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));
753
754 /* Input is always read into a fresh buffer. */
Eric Andersen1792f8c1999-12-09 06:11:36 +0000755 while ((tmp->nbytes = fullRead (fd, tmp->buffer, BUFSIZ)) > 0)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000756 {
757 tmp->next = NULL;
758
759 total_bytes += tmp->nbytes;
760 /* If there is enough room in the last buffer read, just append the new
761 one to it. This is because when reading from a pipe, `nbytes' can
762 often be very small. */
763 if (tmp->nbytes + last->nbytes < BUFSIZ)
764 {
765 memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
766 last->nbytes += tmp->nbytes;
767 }
768 else
769 {
770 /* If there's not enough room, link the new buffer onto the end of
771 the list, then either free up the oldest buffer for the next
772 read if that would leave enough characters, or else malloc a new
773 one. Some compaction mechanism is possible but probably not
774 worthwhile. */
775 last = last->next = tmp;
776 if (total_bytes - first->nbytes > n_bytes)
777 {
778 tmp = first;
779 total_bytes -= first->nbytes;
780 first = first->next;
781 }
782 else
783 {
784 tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));
785 }
786 }
787 }
788 if (tmp->nbytes == -1)
789 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000790 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000791 errors = 1;
792 free ((char *) tmp);
793 goto free_cbuffers;
794 }
795
796 free ((char *) tmp);
797
798 /* Run through the list, printing characters. First, skip over unneeded
799 buffers. */
800 for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
801 total_bytes -= tmp->nbytes;
802
803 /* Find the correct beginning, then print the rest of the file.
804 We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
805 if (total_bytes > n_bytes)
806 i = total_bytes - n_bytes;
807 else
808 i = 0;
809 XWRITE (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
810
811 for (tmp = tmp->next; tmp; tmp = tmp->next)
812 XWRITE (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
813
814free_cbuffers:
815 while (first)
816 {
817 tmp = first->next;
818 free ((char *) first);
819 first = tmp;
820 }
821 return errors;
822}
823
824/* Skip N_BYTES characters from the start of pipe FD, and print
825 any extra characters that were read beyond that.
826 Return 1 on error, 0 if ok. */
827
828static int
829start_bytes (const char *filename, int fd, off_t n_bytes)
830{
831 char buffer[BUFSIZ];
832 int bytes_read = 0;
833
Eric Andersen1792f8c1999-12-09 06:11:36 +0000834 while (n_bytes > 0 && (bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000835 n_bytes -= bytes_read;
836 if (bytes_read == -1)
837 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000838 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000839 return 1;
840 }
841 else if (n_bytes < 0)
842 XWRITE (STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
843 return 0;
844}
845
846/* Skip N_LINES lines at the start of file or pipe FD, and print
847 any extra characters that were read beyond that.
848 Return 1 on error, 0 if ok. */
849
850static int
851start_lines (const char *filename, int fd, long int n_lines)
852{
853 char buffer[BUFSIZ];
854 int bytes_read = 0;
855 int bytes_to_skip = 0;
856
Eric Andersen1792f8c1999-12-09 06:11:36 +0000857 while (n_lines && (bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000858 {
859 bytes_to_skip = 0;
860 while (bytes_to_skip < bytes_read)
861 if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
862 break;
863 }
864 if (bytes_read == -1)
865 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000866 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000867 return 1;
868 }
869 else if (bytes_to_skip < bytes_read)
870 {
871 XWRITE (STDOUT_FILENO, &buffer[bytes_to_skip],
872 bytes_read - bytes_to_skip);
873 }
874 return 0;
875}
876
877/* Display file FILENAME from the current position in FD to the end.
878 If `forever' is nonzero, keep reading from the end of the file
879 until killed. Return the number of bytes read from the file. */
880
881static long
882dump_remainder (const char *filename, int fd)
883{
884 char buffer[BUFSIZ];
885 int bytes_read;
886 long total;
887
888 total = 0;
889output:
Eric Andersen1792f8c1999-12-09 06:11:36 +0000890 while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0)
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000891 {
892 XWRITE (STDOUT_FILENO, buffer, bytes_read);
893 total += bytes_read;
894 }
895 if (bytes_read == -1)
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000896 detailed_error (EXIT_FAILURE, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000897 if (forever)
898 {
899 fflush (stdout);
900 sleep (1);
901 goto output;
902 }
903 else
904 {
905 if (forever_multiple)
906 fflush (stdout);
907 }
908
909 return total;
910}
911
912/* Tail NFILES (>1) files forever until killed. The file names are in
913 NAMES. The open file descriptors are in `file_descs', and the size
914 at which we stopped tailing them is in `file_sizes'. We loop over
915 each of them, doing an fstat to see if they have changed size. If
916 none of them have changed size in one iteration, we sleep for a
917 second and try again. We do this until the user interrupts us. */
918
919static void
920tail_forever (char **names, int nfiles)
921{
922 int last;
923
924 last = -1;
925
926 while (1)
927 {
928 int i;
929 int changed;
930
931 changed = 0;
932 for (i = 0; i < nfiles; i++)
933 {
934 struct stat stats;
935
936 if (file_descs[i] < 0)
937 continue;
938 if (fstat (file_descs[i], &stats) < 0)
939 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000940 detailed_error (0, errno, "%s", names[i]);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000941 file_descs[i] = -1;
942 continue;
943 }
944 if (stats.st_size == file_sizes[i])
945 continue;
946
947 /* This file has changed size. Print out what we can, and
948 then keep looping. */
949
950 changed = 1;
951
952 if (stats.st_size < file_sizes[i])
953 {
954 write_header (names[i], "file truncated");
955 last = i;
956 lseek (file_descs[i], stats.st_size, SEEK_SET);
957 file_sizes[i] = stats.st_size;
958 continue;
959 }
960
961 if (i != last)
962 {
963 if (print_headers)
964 write_header (names[i], NULL);
965 last = i;
966 }
967 file_sizes[i] += dump_remainder (names[i], file_descs[i]);
968 }
969
970 /* If none of the files changed size, sleep. */
971 if (! changed)
972 sleep (1);
973 }
974}
975
976/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
977 Return 0 if successful, 1 if an error occurred. */
978
979static int
980tail_bytes (const char *filename, int fd, off_t n_bytes)
981{
982 struct stat stats;
983
984 /* FIXME: resolve this like in dd.c. */
985 /* Use fstat instead of checking for errno == ESPIPE because
986 lseek doesn't work on some special files but doesn't return an
987 error, either. */
988 if (fstat (fd, &stats))
989 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +0000990 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +0000991 return 1;
992 }
993
994 if (from_start)
995 {
996 if (S_ISREG (stats.st_mode))
997 lseek (fd, n_bytes, SEEK_CUR);
998 else if (start_bytes (filename, fd, n_bytes))
999 return 1;
1000 dump_remainder (filename, fd);
1001 }
1002 else
1003 {
1004 if (S_ISREG (stats.st_mode))
1005 {
1006 off_t current_pos, end_pos;
1007 size_t bytes_remaining;
1008
1009 if ((current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
1010 && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
1011 {
1012 off_t diff;
1013 /* Be careful here. The current position may actually be
1014 beyond the end of the file. */
1015 bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff;
1016 }
1017 else
1018 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001019 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001020 return 1;
1021 }
1022
1023 if (bytes_remaining <= n_bytes)
1024 {
1025 /* From the current position to end of file, there are no
1026 more bytes than have been requested. So reposition the
1027 file pointer to the incoming current position and print
1028 everything after that. */
1029 lseek (fd, current_pos, SEEK_SET);
1030 }
1031 else
1032 {
1033 /* There are more bytes remaining than were requested.
1034 Back up. */
1035 lseek (fd, -n_bytes, SEEK_END);
1036 }
1037 dump_remainder (filename, fd);
1038 }
1039 else
1040 return pipe_bytes (filename, fd, n_bytes);
1041 }
1042 return 0;
1043}
1044
1045/* Output the last N_LINES lines of file FILENAME open for reading in FD.
1046 Return 0 if successful, 1 if an error occurred. */
1047
1048static int
1049tail_lines (const char *filename, int fd, long int n_lines)
1050{
1051 struct stat stats;
1052 off_t length;
1053
1054 if (fstat (fd, &stats))
1055 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001056 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001057 return 1;
1058 }
1059
1060 if (from_start)
1061 {
1062 if (start_lines (filename, fd, n_lines))
1063 return 1;
1064 dump_remainder (filename, fd);
1065 }
1066 else
1067 {
1068 /* Use file_lines only if FD refers to a regular file with
1069 its file pointer positioned at beginning of file. */
1070 /* FIXME: adding the lseek conjunct is a kludge.
1071 Once there's a reasonable test suite, fix the true culprit:
1072 file_lines. file_lines shouldn't presume that the input
1073 file pointer is initially positioned to beginning of file. */
1074 if (S_ISREG (stats.st_mode)
1075 && lseek (fd, (off_t) 0, SEEK_CUR) == (off_t) 0)
1076 {
1077 length = lseek (fd, (off_t) 0, SEEK_END);
1078 if (length != 0 && file_lines (filename, fd, n_lines, length))
1079 return 1;
1080 dump_remainder (filename, fd);
1081 }
1082 else
1083 return pipe_lines (filename, fd, n_lines);
1084 }
1085 return 0;
1086}
1087
1088/* Display the last N_UNITS units of file FILENAME, open for reading
1089 in FD.
1090 Return 0 if successful, 1 if an error occurred. */
1091
1092static int
1093tail (const char *filename, int fd, off_t n_units)
1094{
1095 if (count_lines)
1096 return tail_lines (filename, fd, (long) n_units);
1097 else
1098 return tail_bytes (filename, fd, n_units);
1099}
1100
1101/* Display the last N_UNITS units of file FILENAME.
1102 "-" for FILENAME means the standard input.
1103 FILENUM is this file's index in the list of files the user gave.
1104 Return 0 if successful, 1 if an error occurred. */
1105
1106static int
1107tail_file (const char *filename, off_t n_units, int filenum)
1108{
1109 int fd, errors;
1110 struct stat stats;
1111
Eric Andersen1792f8c1999-12-09 06:11:36 +00001112 if (!strcmp (filename, "-"))
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001113 {
1114 have_read_stdin = 1;
1115 filename = "standard input";
1116 if (print_headers)
1117 write_header (filename, NULL);
1118 errors = tail (filename, 0, n_units);
1119 if (forever_multiple)
1120 {
1121 if (fstat (0, &stats) < 0)
1122 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001123 detailed_error (0, errno, "standard input");
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001124 errors = 1;
1125 }
1126 else if (!S_ISREG (stats.st_mode))
1127 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001128 detailed_error (0, 0,
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001129 "standard input: cannot follow end of non-regular file");
1130 errors = 1;
1131 }
1132 if (errors)
1133 file_descs[filenum] = -1;
1134 else
1135 {
1136 file_descs[filenum] = 0;
1137 file_sizes[filenum] = stats.st_size;
1138 }
1139 }
1140 }
1141 else
1142 {
1143 /* Not standard input. */
1144 fd = open (filename, O_RDONLY);
1145 if (fd == -1)
1146 {
1147 if (forever_multiple)
1148 file_descs[filenum] = -1;
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001149 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001150 errors = 1;
1151 }
1152 else
1153 {
1154 if (print_headers)
1155 write_header (filename, NULL);
1156 errors = tail (filename, fd, n_units);
1157 if (forever_multiple)
1158 {
1159 if (fstat (fd, &stats) < 0)
1160 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001161 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001162 errors = 1;
1163 }
1164 else if (!S_ISREG (stats.st_mode))
1165 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001166 detailed_error (0, 0, "%s: cannot follow end of non-regular file",
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001167 filename);
1168 errors = 1;
1169 }
1170 if (errors)
1171 {
1172 close (fd);
1173 file_descs[filenum] = -1;
1174 }
1175 else
1176 {
1177 file_descs[filenum] = fd;
1178 file_sizes[filenum] = stats.st_size;
1179 }
1180 }
1181 else
1182 {
1183 if (close (fd))
1184 {
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001185 detailed_error (0, errno, "%s", filename);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001186 errors = 1;
1187 }
1188 }
1189 }
1190 }
1191
1192 return errors;
1193}
1194
1195extern int
Eric Andersen1792f8c1999-12-09 06:11:36 +00001196tail_main (int argc, char **argv)
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001197{
Eric Andersen1792f8c1999-12-09 06:11:36 +00001198 int stopit = 0;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001199 enum header_mode header_mode = multiple_files;
1200 int exit_status = 0;
1201 /* If from_start, the number of items to skip before printing; otherwise,
1202 the number of items at the end of the file to print. Initially, -1
1203 means the value has not been set. */
1204 off_t n_units = -1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001205 int n_files;
1206 char **file;
1207
1208 program_name = argv[0];
1209 have_read_stdin = 0;
1210 count_lines = 1;
1211 forever = forever_multiple = from_start = print_headers = 0;
Eric Andersen1792f8c1999-12-09 06:11:36 +00001212
1213 /* Parse any options */
1214 //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv);
1215 while (--argc > 0 && ( **(++argv) == '-' || **argv == '+' )) {
1216 if (**argv == '+') {
1217 from_start = 1;
1218 }
1219 stopit = 0;
1220 while (stopit == 0 && *(++(*argv))) {
1221 switch (**argv) {
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001222 case 'c':
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001223 count_lines = 0;
Eric Andersen1792f8c1999-12-09 06:11:36 +00001224
1225 if (--argc < 1) {
1226 usage(tail_usage);
1227 }
1228 n_units = getNum(*(++argv));
1229 stopit = 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001230 break;
1231
1232 case 'f':
1233 forever = 1;
1234 break;
1235
Eric Andersen1792f8c1999-12-09 06:11:36 +00001236 case 'n':
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001237 count_lines = 1;
Eric Andersen1792f8c1999-12-09 06:11:36 +00001238
1239 if (--argc < 1) {
1240 usage(tail_usage);
1241 }
1242 n_units = atol(*(++argv));
1243 stopit = 1;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001244 break;
1245
1246 case 'q':
1247 header_mode = never;
1248 break;
1249
1250 case 'v':
1251 header_mode = always;
1252 break;
1253
1254 default:
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001255 usage (tail_usage);
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001256 }
1257 }
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001258 }
1259
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001260
1261 if (n_units == -1)
1262 n_units = DEFAULT_N_LINES;
1263
1264 /* To start printing with item N_UNITS from the start of the file, skip
1265 N_UNITS - 1 items. `tail +0' is actually meaningless, but for Unix
1266 compatibility it's treated the same as `tail +1'. */
1267 if (from_start)
1268 {
1269 if (n_units)
1270 --n_units;
1271 }
1272
Eric Andersen1792f8c1999-12-09 06:11:36 +00001273 n_files = argc;
1274 file = argv;
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001275
1276 if (n_files > 1 && forever)
1277 {
1278 forever_multiple = 1;
1279 forever = 0;
1280 file_descs = (int *) xmalloc (n_files * sizeof (int));
1281 file_sizes = (off_t *) xmalloc (n_files * sizeof (off_t));
1282 }
1283
1284 if (header_mode == always
1285 || (header_mode == multiple_files && n_files > 1))
1286 print_headers = 1;
1287
1288 if (n_files == 0)
1289 {
1290 exit_status |= tail_file ("-", n_units, 0);
1291 }
1292 else
1293 {
1294 int i;
1295 for (i = 0; i < n_files; i++)
1296 exit_status |= tail_file (file[i], n_units, i);
1297
1298 if (forever_multiple)
1299 tail_forever (file, n_files);
1300 }
1301
1302 if (have_read_stdin && close (0) < 0)
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001303 detailed_error (EXIT_FAILURE, errno, "-");
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001304 if (fclose (stdout) == EOF)
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001305 detailed_error (EXIT_FAILURE, errno, "write error");
Eric Andersenabc0f4f1999-12-08 23:19:36 +00001306 exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
1307}
Erik Andersen3fe39dc2000-01-25 18:13:53 +00001308
1309
1310#endif