aboutsummaryrefslogtreecommitdiff
path: root/coreutils/tail.c
diff options
context:
space:
mode:
authorErik Andersen <andersen@codepoet.org>2000-01-25 18:13:53 +0000
committerErik Andersen <andersen@codepoet.org>2000-01-25 18:13:53 +0000
commit3fe39dce5d1a0b0946878c66bbd7f694c5aa38ea (patch)
tree32b6129967a8b5c922b72843efc7fc6683287181 /coreutils/tail.c
parentbf3a838aaca4ab34d2739438fa44d0dbb04e9862 (diff)
downloadbusybox-w32-3fe39dce5d1a0b0946878c66bbd7f694c5aa38ea.tar.gz
busybox-w32-3fe39dce5d1a0b0946878c66bbd7f694c5aa38ea.tar.bz2
busybox-w32-3fe39dce5d1a0b0946878c66bbd7f694c5aa38ea.zip
Some busybox updates. See the changelog for details if you care.
-Erik
Diffstat (limited to 'coreutils/tail.c')
-rw-r--r--coreutils/tail.c446
1 files changed, 423 insertions, 23 deletions
diff --git a/coreutils/tail.c b/coreutils/tail.c
index 697177dc7..5198892b6 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -1,3 +1,402 @@
1#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
1/* tail -- output the last part of file(s) 400/* tail -- output the last part of file(s)
2 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc. 401 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
3 402
@@ -42,7 +441,7 @@
42#define NDEBUG 1 441#define NDEBUG 1
43 442
44 443
45static void error(int i, int errnum, char* fmt, ...) 444static void detailed_error(int i, int errnum, char* fmt, ...)
46{ 445{
47 va_list arguments; 446 va_list arguments;
48 447
@@ -60,7 +459,7 @@ static void error(int i, int errnum, char* fmt, ...)
60 assert ((fd) == 1); \ 459 assert ((fd) == 1); \
61 assert ((n_bytes) >= 0); \ 460 assert ((n_bytes) >= 0); \
62 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \ 461 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
63 error (EXIT_FAILURE, errno, "write error"); \ 462 detailed_error (EXIT_FAILURE, errno, "write error"); \
64 } \ 463 } \
65 while (0) 464 while (0)
66 465
@@ -100,8 +499,6 @@ enum header_mode
100 multiple_files, always, never 499 multiple_files, always, never
101}; 500};
102 501
103char *xmalloc ();
104
105/* The name this program was run with. */ 502/* The name this program was run with. */
106char *program_name; 503char *program_name;
107 504
@@ -168,7 +565,7 @@ file_lines (const char *filename, int fd, long int n_lines, off_t pos)
168 bytes_read = fullRead (fd, buffer, bytes_read); 565 bytes_read = fullRead (fd, buffer, bytes_read);
169 if (bytes_read == -1) 566 if (bytes_read == -1)
170 { 567 {
171 error (0, errno, "%s", filename); 568 detailed_error (0, errno, "%s", filename);
172 return 1; 569 return 1;
173 } 570 }
174 571
@@ -204,7 +601,7 @@ file_lines (const char *filename, int fd, long int n_lines, off_t pos)
204 while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0); 601 while ((bytes_read = fullRead (fd, buffer, BUFSIZ)) > 0);
205 if (bytes_read == -1) 602 if (bytes_read == -1)
206 { 603 {
207 error (0, errno, "%s", filename); 604 detailed_error (0, errno, "%s", filename);
208 return 1; 605 return 1;
209 } 606 }
210 return 0; 607 return 0;
@@ -276,7 +673,7 @@ pipe_lines (const char *filename, int fd, long int n_lines)
276 } 673 }
277 if (tmp->nbytes == -1) 674 if (tmp->nbytes == -1)
278 { 675 {
279 error (0, errno, "%s", filename); 676 detailed_error (0, errno, "%s", filename);
280 errors = 1; 677 errors = 1;
281 free ((char *) tmp); 678 free ((char *) tmp);
282 goto free_lbuffers; 679 goto free_lbuffers;
@@ -390,7 +787,7 @@ pipe_bytes (const char *filename, int fd, off_t n_bytes)
390 } 787 }
391 if (tmp->nbytes == -1) 788 if (tmp->nbytes == -1)
392 { 789 {
393 error (0, errno, "%s", filename); 790 detailed_error (0, errno, "%s", filename);
394 errors = 1; 791 errors = 1;
395 free ((char *) tmp); 792 free ((char *) tmp);
396 goto free_cbuffers; 793 goto free_cbuffers;
@@ -438,7 +835,7 @@ start_bytes (const char *filename, int fd, off_t n_bytes)
438 n_bytes -= bytes_read; 835 n_bytes -= bytes_read;
439 if (bytes_read == -1) 836 if (bytes_read == -1)
440 { 837 {
441 error (0, errno, "%s", filename); 838 detailed_error (0, errno, "%s", filename);
442 return 1; 839 return 1;
443 } 840 }
444 else if (n_bytes < 0) 841 else if (n_bytes < 0)
@@ -466,7 +863,7 @@ start_lines (const char *filename, int fd, long int n_lines)
466 } 863 }
467 if (bytes_read == -1) 864 if (bytes_read == -1)
468 { 865 {
469 error (0, errno, "%s", filename); 866 detailed_error (0, errno, "%s", filename);
470 return 1; 867 return 1;
471 } 868 }
472 else if (bytes_to_skip < bytes_read) 869 else if (bytes_to_skip < bytes_read)
@@ -496,7 +893,7 @@ output:
496 total += bytes_read; 893 total += bytes_read;
497 } 894 }
498 if (bytes_read == -1) 895 if (bytes_read == -1)
499 error (EXIT_FAILURE, errno, "%s", filename); 896 detailed_error (EXIT_FAILURE, errno, "%s", filename);
500 if (forever) 897 if (forever)
501 { 898 {
502 fflush (stdout); 899 fflush (stdout);
@@ -540,7 +937,7 @@ tail_forever (char **names, int nfiles)
540 continue; 937 continue;
541 if (fstat (file_descs[i], &stats) < 0) 938 if (fstat (file_descs[i], &stats) < 0)
542 { 939 {
543 error (0, errno, "%s", names[i]); 940 detailed_error (0, errno, "%s", names[i]);
544 file_descs[i] = -1; 941 file_descs[i] = -1;
545 continue; 942 continue;
546 } 943 }
@@ -590,7 +987,7 @@ tail_bytes (const char *filename, int fd, off_t n_bytes)
590 error, either. */ 987 error, either. */
591 if (fstat (fd, &stats)) 988 if (fstat (fd, &stats))
592 { 989 {
593 error (0, errno, "%s", filename); 990 detailed_error (0, errno, "%s", filename);
594 return 1; 991 return 1;
595 } 992 }
596 993
@@ -619,7 +1016,7 @@ tail_bytes (const char *filename, int fd, off_t n_bytes)
619 } 1016 }
620 else 1017 else
621 { 1018 {
622 error (0, errno, "%s", filename); 1019 detailed_error (0, errno, "%s", filename);
623 return 1; 1020 return 1;
624 } 1021 }
625 1022
@@ -656,7 +1053,7 @@ tail_lines (const char *filename, int fd, long int n_lines)
656 1053
657 if (fstat (fd, &stats)) 1054 if (fstat (fd, &stats))
658 { 1055 {
659 error (0, errno, "%s", filename); 1056 detailed_error (0, errno, "%s", filename);
660 return 1; 1057 return 1;
661 } 1058 }
662 1059
@@ -723,12 +1120,12 @@ tail_file (const char *filename, off_t n_units, int filenum)
723 { 1120 {
724 if (fstat (0, &stats) < 0) 1121 if (fstat (0, &stats) < 0)
725 { 1122 {
726 error (0, errno, "standard input"); 1123 detailed_error (0, errno, "standard input");
727 errors = 1; 1124 errors = 1;
728 } 1125 }
729 else if (!S_ISREG (stats.st_mode)) 1126 else if (!S_ISREG (stats.st_mode))
730 { 1127 {
731 error (0, 0, 1128 detailed_error (0, 0,
732 "standard input: cannot follow end of non-regular file"); 1129 "standard input: cannot follow end of non-regular file");
733 errors = 1; 1130 errors = 1;
734 } 1131 }
@@ -749,7 +1146,7 @@ tail_file (const char *filename, off_t n_units, int filenum)
749 { 1146 {
750 if (forever_multiple) 1147 if (forever_multiple)
751 file_descs[filenum] = -1; 1148 file_descs[filenum] = -1;
752 error (0, errno, "%s", filename); 1149 detailed_error (0, errno, "%s", filename);
753 errors = 1; 1150 errors = 1;
754 } 1151 }
755 else 1152 else
@@ -761,12 +1158,12 @@ tail_file (const char *filename, off_t n_units, int filenum)
761 { 1158 {
762 if (fstat (fd, &stats) < 0) 1159 if (fstat (fd, &stats) < 0)
763 { 1160 {
764 error (0, errno, "%s", filename); 1161 detailed_error (0, errno, "%s", filename);
765 errors = 1; 1162 errors = 1;
766 } 1163 }
767 else if (!S_ISREG (stats.st_mode)) 1164 else if (!S_ISREG (stats.st_mode))
768 { 1165 {
769 error (0, 0, "%s: cannot follow end of non-regular file", 1166 detailed_error (0, 0, "%s: cannot follow end of non-regular file",
770 filename); 1167 filename);
771 errors = 1; 1168 errors = 1;
772 } 1169 }
@@ -785,7 +1182,7 @@ tail_file (const char *filename, off_t n_units, int filenum)
785 { 1182 {
786 if (close (fd)) 1183 if (close (fd))
787 { 1184 {
788 error (0, errno, "%s", filename); 1185 detailed_error (0, errno, "%s", filename);
789 errors = 1; 1186 errors = 1;
790 } 1187 }
791 } 1188 }
@@ -903,8 +1300,11 @@ tail_main (int argc, char **argv)
903 } 1300 }
904 1301
905 if (have_read_stdin && close (0) < 0) 1302 if (have_read_stdin && close (0) < 0)
906 error (EXIT_FAILURE, errno, "-"); 1303 detailed_error (EXIT_FAILURE, errno, "-");
907 if (fclose (stdout) == EOF) 1304 if (fclose (stdout) == EOF)
908 error (EXIT_FAILURE, errno, "write error"); 1305 detailed_error (EXIT_FAILURE, errno, "write error");
909 exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 1306 exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
910} 1307}
1308
1309
1310#endif