aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2000-07-31 17:05:58 +0000
committerEric Andersen <andersen@codepoet.org>2000-07-31 17:05:58 +0000
commit98bbd688a80627ec4071b7c819ee2116f5ecc6d4 (patch)
tree20b6560f2bbd14e5d9b550d9f87f7e13e3cc02ac
parent43c17b3d9649a2717961287b14b48960b611df82 (diff)
downloadbusybox-w32-98bbd688a80627ec4071b7c819ee2116f5ecc6d4.tar.gz
busybox-w32-98bbd688a80627ec4071b7c819ee2116f5ecc6d4.tar.bz2
busybox-w32-98bbd688a80627ec4071b7c819ee2116f5ecc6d4.zip
Commit the rewrite of tail by Allen Soard
<esp-software@mail.hypermart.net>. Thanks Allen for you work at getting this into shape, -Erik
-rw-r--r--Changelog3
-rw-r--r--busybox.def.h4
-rw-r--r--coreutils/tail.c1364
-rw-r--r--tail.c1364
4 files changed, 495 insertions, 2240 deletions
diff --git a/Changelog b/Changelog
index ac7dba32c..f415e5d43 100644
--- a/Changelog
+++ b/Changelog
@@ -6,6 +6,9 @@
6 'make install-hardlinks' target to (tada) install hardlinks. 6 'make install-hardlinks' target to (tada) install hardlinks.
7 * syslogd can now log messages to a remote host -- patch thanks 7 * syslogd can now log messages to a remote host -- patch thanks
8 to Gyepi Sam <gyepi@praxis-sw.com> 8 to Gyepi Sam <gyepi@praxis-sw.com>
9 * Rewrite of 'tail' to make it simpler, smaller, and more robust.
10 It now weighs only 2.25k (3k when full featured). The code it
11 much cleaner, thanks to "Allen Soard" <esp-software@mail.hypermart.net>
9 12
10 13
11 -Erik Andersen 14 -Erik Andersen
diff --git a/busybox.def.h b/busybox.def.h
index 26588e98f..41ba0ce85 100644
--- a/busybox.def.h
+++ b/busybox.def.h
@@ -190,8 +190,8 @@
190// enable syslogd -R remotehost 190// enable syslogd -R remotehost
191#define BB_FEATURE_REMOTE_LOG 191#define BB_FEATURE_REMOTE_LOG
192// 192//
193//Simple tail implementation (2k vs 6k for the full one). Still 193//Simple tail implementation (2.25k vs 3k for the full one).
194//provides 'tail -f' support -- but for only one file at a time. 194//Both provide 'tail -f' support (only one file at a time.)
195#define BB_FEATURE_SIMPLE_TAIL 195#define BB_FEATURE_SIMPLE_TAIL
196// 196//
197// Enable support for loop devices in mount 197// Enable support for loop devices in mount
diff --git a/coreutils/tail.c b/coreutils/tail.c
index 2746af83b..4a1aa8436 100644
--- a/coreutils/tail.c
+++ b/coreutils/tail.c
@@ -1,15 +1,4 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2#include "internal.h"
3
4/* 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) 2/* tail -- output the last part of file(s)
14 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc. 3 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
15 4
@@ -32,1163 +21,300 @@
32 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>. 21 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
33 22
34 Rewrote the option parser, removed locales support, 23 Rewrote the option parser, removed locales support,
35 and generally busyboxed, Erik Andersen <andersen@lineo.com> 24 and generally busyboxed, Erik Andersen <andersen@lineo.com>
36 25
37 Removed superfluous options and associated code ("-c", "-n", "-q"). 26 Removed superfluous options and associated code ("-c", "-n", "-q").
38 Removed "tail -f" support for multiple files. 27 Removed "tail -f" support for multiple files.
39 Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>. 28 Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>.
40 29
41 */ 30 Compleate Rewrite to correctly support "-NUM", "+NUM", and "-s" by
42 31 E.Allen Soard (esp@espsw.net).
43 32
44#include <stdio.h> 33 */
45#include <stdarg.h>
46#include <sys/types.h> 34#include <sys/types.h>
47#include <sys/stat.h> 35#include <sys/stat.h>
48#include <fcntl.h> 36#include <fcntl.h>
49#include <ctype.h> 37#include <stdio.h>
50#define BB_DECLARE_EXTERN 38#include <stdlib.h>
51#define bb_need_help 39#include <unistd.h>
52#include "messages.c" 40#include <string.h>
41#include <getopt.h>
42#include "internal.h"
53 43
44#define STDIN "standard input"
45#define LINES 0
46#define BYTES 1
54 47
55#define XWRITE(fd, buffer, n_bytes) \ 48static int n_files = 0;
56 do { \ 49static char **files = NULL;
57 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
58 errorMsg("write error"); \
59 } while (0)
60 50
61/* Number of items to tail. */ 51static char follow=0;
62#define DEFAULT_N_LINES 10
63 52
64/* Size of atomic reads. */ 53#ifdef BB_FEATURE_SIMPLE_TAIL
65#ifndef BUFSIZ 54static const char unit_type=LINES;
66#define BUFSIZ (512 * 8) 55static const char sleep_int=1;
56#else
57static char unit_type=LINES;
58static int sleep_int=1;
59static char verbose = 0;
67#endif 60#endif
68 61
69/* If nonzero, read from the end of one file until killed. */ 62//static off_t units=-11;
70static int forever; 63static off_t units=0;
71
72/* If nonzero, print filename headers. */
73static int print_headers;
74 64
75static void write_header(const char *filename) 65int tail_stream(int file_id)
76{ 66{
77 static int first_file = 1; 67 int fd;
78 68 ssize_t bytes_read=0;
79 printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename); 69 ssize_t bs=BUFSIZ;
80 first_file = 0; 70 ssize_t startpoint=bs;
81} 71 ssize_t endpoint=0;
82 72 ssize_t count=0;
83/* Print the last N_LINES lines from the end of file FD. 73 ssize_t filesize=0;
84 Go backward through the file, reading `BUFSIZ' bytes at a time (except 74 ssize_t filelocation=0;
85 probably the first), until we hit the start of the file or have 75 char direction=1;
86 read NUMBER newlines. 76 char * buffer;
87 POS starts out as the length of the file (the offset of the last 77 char pipe;
88 byte of the file + 1). 78
89 Return 0 if successful, 1 if an error occurred. */ 79
90 80 if (!strcmp(files[file_id], STDIN))
91static int 81 fd = 0;
92file_lines(int fd, long int n_lines, off_t pos) 82 else
93{ 83 fd = open(files[file_id], O_RDONLY);
94 char buffer[BUFSIZ]; 84 if (fd == -1)
95 int bytes_read; 85 fatalError("Unable to open file %s.\n", files[file_id]);
96 int i; /* Index into `buffer' for scanning. */ 86
97 87 buffer=malloc(bs);
98 if (n_lines == 0) 88
99 return 0; 89 filesize=lseek(fd, -1, SEEK_END)+1;
100 90 pipe=(filesize<=0);
101 /* Set `bytes_read' to the size of the last, probably partial, buffer; 91
102 0 < `bytes_read' <= `BUFSIZ'. */ 92 if(units>=0)
103 bytes_read = pos % BUFSIZ; 93 lseek(fd,0,SEEK_SET);
104 if (bytes_read == 0) 94 else {
105 bytes_read = BUFSIZ; 95 direction=-1;
106 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all 96 count=1;
107 reads will be on block boundaries, which might increase efficiency. */ 97 }
108 pos -= bytes_read; 98 while(units != 0) {
109 lseek(fd, pos, SEEK_SET); 99 if (pipe) {
110 bytes_read = fullRead(fd, buffer, bytes_read); 100 char * line;
111 if (bytes_read == -1) 101 ssize_t f_size=0;
112 errorMsg("read error"); 102
113 103 bs=BUFSIZ;
114 /* Count the incomplete line on files that don't end with a newline. */ 104 line=malloc(bs);
115 if (bytes_read && buffer[bytes_read - 1] != '\n') 105 while(1) {
116 --n_lines; 106 bytes_read=read(fd,line,bs);
117 107 if(bytes_read<=0)
118 do { 108 break;
119 /* Scan backward, counting the newlines in this bufferfull. */ 109 buffer=realloc(buffer,f_size+bytes_read);
120 for (i = bytes_read - 1; i >= 0; i--) { 110 memcpy(&buffer[f_size],line,bytes_read);
121 /* Have we counted the requested number of newlines yet? */ 111 filelocation=f_size+=bytes_read;
122 if (buffer[i] == '\n' && n_lines-- == 0) {
123 /* If this newline wasn't the last character in the buffer,
124 print the text after it. */
125 if (i != bytes_read - 1)
126 XWRITE(STDOUT_FILENO, &buffer[i + 1],
127 bytes_read - (i + 1));
128 return 0;
129 } 112 }
130 } 113 bs=f_size;
131 /* Not enough newlines in that bufferfull. */ 114 if(direction<0)
132 if (pos == 0) { 115 bs--;
133 /* Not enough lines in the file; print the entire file. */ 116 if (line)
134 lseek(fd, (off_t) 0, SEEK_SET); 117 free(line);
135 return 0;
136 }
137 pos -= BUFSIZ;
138 lseek(fd, pos, SEEK_SET);
139 }
140 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
141 if (bytes_read == -1)
142 errorMsg("read error");
143
144 return 0;
145}
146
147/* Print the last N_LINES lines from the end of the standard input,
148 open for reading as pipe FD.
149 Buffer the text as a linked list of LBUFFERs, adding them as needed.
150 Return 0 if successful, 1 if an error occured. */
151
152static int pipe_lines(int fd, long int n_lines)
153{
154 struct linebuffer {
155 int nbytes, nlines;
156 char buffer[BUFSIZ];
157 struct linebuffer *next;
158 };
159 typedef struct linebuffer LBUFFER;
160 LBUFFER *first, *last, *tmp;
161 int i; /* Index into buffers. */
162 int total_lines = 0; /* Total number of newlines in all buffers. */
163 int errors = 0;
164
165 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
166 first->nbytes = first->nlines = 0;
167 first->next = NULL;
168 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
169
170 /* Input is always read into a fresh buffer. */
171 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
172 tmp->nlines = 0;
173 tmp->next = NULL;
174
175 /* Count the number of newlines just read. */
176 for (i = 0; i < tmp->nbytes; i++)
177 if (tmp->buffer[i] == '\n')
178 ++tmp->nlines;
179 total_lines += tmp->nlines;
180
181 /* If there is enough room in the last buffer read, just append the new
182 one to it. This is because when reading from a pipe, `nbytes' can
183 often be very small. */
184 if (tmp->nbytes + last->nbytes < BUFSIZ) {
185 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
186 last->nbytes += tmp->nbytes;
187 last->nlines += tmp->nlines;
188 } else { 118 } else {
189 /* If there's not enough room, link the new buffer onto the end of 119 filelocation = lseek(fd, 0, SEEK_CUR);
190 the list, then either free up the oldest buffer for the next 120 if(direction<0) {
191 read if that would leave enough lines, or else malloc a new one. 121 if(filelocation<bs)
192 Some compaction mechanism is possible but probably not 122 bs=filelocation;
193 worthwhile. */ 123 filelocation = lseek(fd, -bs, SEEK_CUR);
194 last = last->next = tmp;
195 if (total_lines - first->nlines > n_lines) {
196 tmp = first;
197 total_lines -= first->nlines;
198 first = first->next;
199 } else
200 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
201 }
202 }
203 if (tmp->nbytes == -1)
204 errorMsg("read error");
205
206 free((char *) tmp);
207
208 /* This prevents a core dump when the pipe contains no newlines. */
209 if (n_lines == 0)
210 goto free_lbuffers;
211
212 /* Count the incomplete line on files that don't end with a newline. */
213 if (last->buffer[last->nbytes - 1] != '\n') {
214 ++last->nlines;
215 ++total_lines;
216 }
217
218 /* Run through the list, printing lines. First, skip over unneeded
219 buffers. */
220 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
221 total_lines -= tmp->nlines;
222
223 /* Find the correct beginning, then print the rest of the file. */
224 if (total_lines > n_lines) {
225 char *cp;
226
227 /* Skip `total_lines' - `n_lines' newlines. We made sure that
228 `total_lines' - `n_lines' <= `tmp->nlines'. */
229 cp = tmp->buffer;
230 for (i = total_lines - n_lines; i; --i)
231 while (*cp++ != '\n')
232 /* Do nothing. */ ;
233 i = cp - tmp->buffer;
234 } else
235 i = 0;
236 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
237
238 for (tmp = tmp->next; tmp; tmp = tmp->next)
239 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
240
241 free_lbuffers:
242 while (first) {
243 tmp = first->next;
244 free((char *) first);
245 first = tmp;
246 }
247 return errors;
248}
249
250/* Display file FILENAME from the current position in FD to the end.
251 If `forever' is nonzero, keep reading from the end of the file
252 until killed. Return the number of bytes read from the file. */
253
254static long dump_remainder(int fd)
255{
256 char buffer[BUFSIZ];
257 int bytes_read;
258 long total;
259
260 total = 0;
261 output:
262 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
263 XWRITE(STDOUT_FILENO, buffer, bytes_read);
264 total += bytes_read;
265 }
266 if (bytes_read == -1)
267 errorMsg("read error");
268 if (forever) {
269 fflush(stdout);
270 sleep(1);
271 goto output;
272 }
273
274 return total;
275}
276
277/* Output the last N_LINES lines of file FILENAME open for reading in FD.
278 Return 0 if successful, 1 if an error occurred. */
279
280static int tail_lines(const char *filename, int fd, long int n_lines)
281{
282 struct stat stats;
283 off_t length;
284
285 if (print_headers)
286 write_header(filename);
287
288 if (fstat(fd, &stats))
289 errorMsg("fstat error");
290
291 /* Use file_lines only if FD refers to a regular file with
292 its file pointer positioned at beginning of file. */
293 /* FIXME: adding the lseek conjunct is a kludge.
294 Once there's a reasonable test suite, fix the true culprit:
295 file_lines. file_lines shouldn't presume that the input
296 file pointer is initially positioned to beginning of file. */
297 if (S_ISREG(stats.st_mode)
298 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
299 length = lseek(fd, (off_t) 0, SEEK_END);
300 if (length != 0 && file_lines(fd, n_lines, length))
301 return 1;
302 dump_remainder(fd);
303 } else
304 return pipe_lines(fd, n_lines);
305
306 return 0;
307}
308
309/* Display the last N_UNITS lines of file FILENAME.
310 "-" for FILENAME means the standard input.
311 Return 0 if successful, 1 if an error occurred. */
312
313static int tail_file(const char *filename, off_t n_units)
314{
315 int fd, errors;
316
317 if (!strcmp(filename, "-")) {
318 filename = "standard input";
319 errors = tail_lines(filename, 0, (long) n_units);
320 } else {
321 /* Not standard input. */
322 fd = open(filename, O_RDONLY);
323 if (fd == -1)
324 perror(filename);
325
326 errors = tail_lines(filename, fd, (long) n_units);
327 close(fd);
328 }
329
330 return errors;
331}
332
333extern int tail_main(int argc, char **argv)
334{
335 int exit_status = 0;
336 int n_units = DEFAULT_N_LINES;
337 int n_tmp, i;
338 char opt;
339
340 forever = print_headers = 0;
341
342 /* parse argv[] */
343 for (i = 1; i < argc; i++) {
344 if (argv[i][0] == '-') {
345 opt = argv[i][1];
346 switch (opt) {
347 case 'f':
348 forever = 1;
349 break;
350 case 'n':
351 n_tmp = 0;
352 if (++i < argc)
353 n_tmp = atoi(argv[i]);
354 if (n_tmp < 1)
355 usage(tail_usage);
356 n_units = n_tmp;
357 break;
358 case '-':
359 case 'h':
360 usage(tail_usage);
361 default:
362 if ((n_units = atoi(&argv[i][1])) < 1) {
363 errorMsg("invalid option -- %c\n", opt);
364 usage(tail_usage);
365 }
366 } 124 }
367 } else { 125 bytes_read = read(fd, buffer, bs);
368 break; 126 if (bytes_read <= 0)
127 break;
128 bs=bytes_read;
369 } 129 }
370 } 130 startpoint=bs;
371 131 if(direction>0) {
372 if (i + 1 < argc) { 132 endpoint=startpoint;
373 if (forever) { 133 startpoint=0;
374 errorMsg("option -f is invalid with multiple files\n");
375 usage(tail_usage);
376 } 134 }
377 print_headers = 1; 135 for(;startpoint!=endpoint;startpoint+=direction) {
378 } 136#ifndef BB_FEATURE_SIMPLE_TAIL
379 137 if(unit_type==BYTES)
380 if (i >= argc) { 138 count++;
381 exit_status |= tail_file("-", n_units); 139 else
382 } else {
383 for (; i < argc; i++)
384 exit_status |= tail_file(argv[i], n_units);
385 }
386
387 return(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
388}
389
390
391#else
392// Here follows the code for the full featured tail code
393
394
395/* tail -- output the last part of file(s)
396 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
397
398 This program is free software; you can redistribute it and/or modify
399 it under the terms of the GNU General Public License as published by
400 the Free Software Foundation; either version 2, or (at your option)
401 any later version.
402
403 This program is distributed in the hope that it will be useful,
404 but WITHOUT ANY WARRANTY; without even the implied warranty of
405 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
406 GNU General Public License for more details.
407
408 You should have received a copy of the GNU General Public License
409 along with this program; if not, write to the Free Software
410 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
411
412 Original version by Paul Rubin <phr@ocf.berkeley.edu>.
413 Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
414 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
415
416 Rewrote the option parser, removed locales support,
417 and generally busyboxed, Erik Andersen <andersen@lineo.com>
418
419 */
420
421#include "internal.h"
422
423#include <stdio.h>
424#include <stdarg.h>
425#include <assert.h>
426#include <errno.h>
427#include <sys/types.h>
428#include <sys/types.h>
429#include <sys/stat.h>
430#include <fcntl.h>
431#include <ctype.h>
432
433
434
435/* Disable assertions. Some systems have broken assert macros. */
436#define NDEBUG 1
437
438
439static void detailed_error(int i, int errnum, char *fmt, ...)
440 __attribute__ ((format (printf, 3, 4)));
441static void detailed_error(int i, int errnum, char *fmt, ...)
442{
443 va_list arguments;
444
445 va_start(arguments, fmt);
446 vfprintf(stderr, fmt, arguments);
447 fprintf(stderr, "\n%s\n", strerror(errnum));
448 va_end(arguments);
449 exit(i);
450}
451
452
453#define XWRITE(fd, buffer, n_bytes) \
454 do \
455 { \
456 assert ((fd) == 1); \
457 assert ((n_bytes) >= 0); \
458 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
459 detailed_error (EXIT_FAILURE, errno, "write error"); \
460 } \
461 while (0)
462
463/* Number of items to tail. */
464#define DEFAULT_N_LINES 10
465
466/* Size of atomic reads. */
467#ifndef BUFSIZ
468#define BUFSIZ (512 * 8)
469#endif 140#endif
470 141 if(buffer[startpoint-1]=='\n')
471/* If nonzero, interpret the numeric argument as the number of lines. 142 count++;
472 Otherwise, interpret it as the number of bytes. */ 143 if (!pipe)
473static int count_lines; 144 filelocation=lseek(fd,0,SEEK_CUR);
474 145 if(count==abs(units))
475/* If nonzero, read from the end of one file until killed. */ 146 break;
476static int forever;
477
478/* If nonzero, read from the end of multiple files until killed. */
479static int forever_multiple;
480
481/* Array of file descriptors if forever_multiple is 1. */
482static int *file_descs;
483
484/* Array of file sizes if forever_multiple is 1. */
485static off_t *file_sizes;
486
487/* If nonzero, count from start of file instead of end. */
488static int from_start;
489
490/* If nonzero, print filename headers. */
491static int print_headers;
492
493/* When to print the filename banners. */
494enum header_mode {
495 multiple_files, always, never
496};
497
498/* The name this program was run with. */
499char *program_name;
500
501/* Nonzero if we have ever read standard input. */
502static int have_read_stdin;
503
504static void write_header(const char *filename, const char *comment)
505{
506 static int first_file = 1;
507
508 printf("%s==> %s%s%s <==\n", (first_file ? "" : "\n"), filename,
509 (comment ? ": " : ""), (comment ? comment : ""));
510 first_file = 0;
511}
512
513/* Print the last N_LINES lines from the end of file FD.
514 Go backward through the file, reading `BUFSIZ' bytes at a time (except
515 probably the first), until we hit the start of the file or have
516 read NUMBER newlines.
517 POS starts out as the length of the file (the offset of the last
518 byte of the file + 1).
519 Return 0 if successful, 1 if an error occurred. */
520
521static int
522file_lines(const char *filename, int fd, long int n_lines, off_t pos)
523{
524 char buffer[BUFSIZ];
525 int bytes_read;
526 int i; /* Index into `buffer' for scanning. */
527
528 if (n_lines == 0)
529 return 0;
530
531 /* Set `bytes_read' to the size of the last, probably partial, buffer;
532 0 < `bytes_read' <= `BUFSIZ'. */
533 bytes_read = pos % BUFSIZ;
534 if (bytes_read == 0)
535 bytes_read = BUFSIZ;
536 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
537 reads will be on block boundaries, which might increase efficiency. */
538 pos -= bytes_read;
539 lseek(fd, pos, SEEK_SET);
540 bytes_read = fullRead(fd, buffer, bytes_read);
541 if (bytes_read == -1) {
542 detailed_error(0, errno, "%s", filename);
543 return 1;
544 }
545
546 /* Count the incomplete line on files that don't end with a newline. */
547 if (bytes_read && buffer[bytes_read - 1] != '\n')
548 --n_lines;
549
550 do {
551 /* Scan backward, counting the newlines in this bufferfull. */
552 for (i = bytes_read - 1; i >= 0; i--) {
553 /* Have we counted the requested number of newlines yet? */
554 if (buffer[i] == '\n' && n_lines-- == 0) {
555 /* If this newline wasn't the last character in the buffer,
556 print the text after it. */
557 if (i != bytes_read - 1)
558 XWRITE(STDOUT_FILENO, &buffer[i + 1],
559 bytes_read - (i + 1));
560 return 0;
561 }
562 }
563 /* Not enough newlines in that bufferfull. */
564 if (pos == 0) {
565 /* Not enough lines in the file; print the entire file. */
566 lseek(fd, (off_t) 0, SEEK_SET);
567 return 0;
568 } 147 }
569 pos -= BUFSIZ; 148 if((count==abs(units)) | pipe)
570 lseek(fd, pos, SEEK_SET); 149 break;
571 } 150 if(direction<0){
572 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0); 151 filelocation = lseek(fd, -bytes_read, SEEK_CUR);
573 if (bytes_read == -1) { 152 if(filelocation==0)
574 detailed_error(0, errno, "%s", filename); 153 break;
575 return 1;
576 }
577 return 0;
578}
579
580/* Print the last N_LINES lines from the end of the standard input,
581 open for reading as pipe FD.
582 Buffer the text as a linked list of LBUFFERs, adding them as needed.
583 Return 0 if successful, 1 if an error occured. */
584
585static int pipe_lines(const char *filename, int fd, long int n_lines)
586{
587 struct linebuffer {
588 int nbytes, nlines;
589 char buffer[BUFSIZ];
590 struct linebuffer *next;
591 };
592 typedef struct linebuffer LBUFFER;
593 LBUFFER *first, *last, *tmp;
594 int i; /* Index into buffers. */
595 int total_lines = 0; /* Total number of newlines in all buffers. */
596 int errors = 0;
597
598 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
599 first->nbytes = first->nlines = 0;
600 first->next = NULL;
601 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
602
603 /* Input is always read into a fresh buffer. */
604 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
605 tmp->nlines = 0;
606 tmp->next = NULL;
607
608 /* Count the number of newlines just read. */
609 for (i = 0; i < tmp->nbytes; i++)
610 if (tmp->buffer[i] == '\n')
611 ++tmp->nlines;
612 total_lines += tmp->nlines;
613
614 /* If there is enough room in the last buffer read, just append the new
615 one to it. This is because when reading from a pipe, `nbytes' can
616 often be very small. */
617 if (tmp->nbytes + last->nbytes < BUFSIZ) {
618 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
619 last->nbytes += tmp->nbytes;
620 last->nlines += tmp->nlines;
621 } else {
622 /* If there's not enough room, link the new buffer onto the end of
623 the list, then either free up the oldest buffer for the next
624 read if that would leave enough lines, or else malloc a new one.
625 Some compaction mechanism is possible but probably not
626 worthwhile. */
627 last = last->next = tmp;
628 if (total_lines - first->nlines > n_lines) {
629 tmp = first;
630 total_lines -= first->nlines;
631 first = first->next;
632 } else
633 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
634 } 154 }
635 } 155 }
636 if (tmp->nbytes == -1) { 156 if(pipe && (direction<0))
637 detailed_error(0, errno, "%s", filename); 157 bs++;
638 errors = 1; 158 bytes_read=bs-startpoint;
639 free((char *) tmp); 159 memcpy(&buffer[0],&buffer[startpoint],bytes_read);
640 goto free_lbuffers;
641 }
642
643 free((char *) tmp);
644
645 /* This prevents a core dump when the pipe contains no newlines. */
646 if (n_lines == 0)
647 goto free_lbuffers;
648
649 /* Count the incomplete line on files that don't end with a newline. */
650 if (last->buffer[last->nbytes - 1] != '\n') {
651 ++last->nlines;
652 ++total_lines;
653 }
654 160
655 /* Run through the list, printing lines. First, skip over unneeded 161 bs=BUFSIZ;
656 buffers. */ 162 while (1) {
657 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next) 163 if((filelocation>0 || pipe)){
658 total_lines -= tmp->nlines; 164 write(1,buffer,bytes_read);
659
660 /* Find the correct beginning, then print the rest of the file. */
661 if (total_lines > n_lines) {
662 char *cp;
663
664 /* Skip `total_lines' - `n_lines' newlines. We made sure that
665 `total_lines' - `n_lines' <= `tmp->nlines'. */
666 cp = tmp->buffer;
667 for (i = total_lines - n_lines; i; --i)
668 while (*cp++ != '\n')
669 /* Do nothing. */ ;
670 i = cp - tmp->buffer;
671 } else
672 i = 0;
673 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
674
675 for (tmp = tmp->next; tmp; tmp = tmp->next)
676 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
677
678 free_lbuffers:
679 while (first) {
680 tmp = first->next;
681 free((char *) first);
682 first = tmp;
683 }
684 return errors;
685}
686
687/* Print the last N_BYTES characters from the end of pipe FD.
688 This is a stripped down version of pipe_lines.
689 Return 0 if successful, 1 if an error occurred. */
690
691static int pipe_bytes(const char *filename, int fd, off_t n_bytes)
692{
693 struct charbuffer {
694 int nbytes;
695 char buffer[BUFSIZ];
696 struct charbuffer *next;
697 };
698 typedef struct charbuffer CBUFFER;
699 CBUFFER *first, *last, *tmp;
700 int i; /* Index into buffers. */
701 int total_bytes = 0; /* Total characters in all buffers. */
702 int errors = 0;
703
704 first = last = (CBUFFER *) xmalloc(sizeof(CBUFFER));
705 first->nbytes = 0;
706 first->next = NULL;
707 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
708
709 /* Input is always read into a fresh buffer. */
710 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
711 tmp->next = NULL;
712
713 total_bytes += tmp->nbytes;
714 /* If there is enough room in the last buffer read, just append the new
715 one to it. This is because when reading from a pipe, `nbytes' can
716 often be very small. */
717 if (tmp->nbytes + last->nbytes < BUFSIZ) {
718 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
719 last->nbytes += tmp->nbytes;
720 } else {
721 /* If there's not enough room, link the new buffer onto the end of
722 the list, then either free up the oldest buffer for the next
723 read if that would leave enough characters, or else malloc a new
724 one. Some compaction mechanism is possible but probably not
725 worthwhile. */
726 last = last->next = tmp;
727 if (total_bytes - first->nbytes > n_bytes) {
728 tmp = first;
729 total_bytes -= first->nbytes;
730 first = first->next;
731 } else {
732 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
733 }
734 } 165 }
735 } 166 bytes_read = read(fd, buffer, bs);
736 if (tmp->nbytes == -1) { 167 filelocation+=bytes_read;
737 detailed_error(0, errno, "%s", filename); 168 if (bytes_read <= 0) {
738 errors = 1; 169 if (!follow) {
739 free((char *) tmp); 170 close(fd);
740 goto free_cbuffers;
741 }
742
743 free((char *) tmp);
744
745 /* Run through the list, printing characters. First, skip over unneeded
746 buffers. */
747 for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
748 total_bytes -= tmp->nbytes;
749
750 /* Find the correct beginning, then print the rest of the file.
751 We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
752 if (total_bytes > n_bytes)
753 i = total_bytes - n_bytes;
754 else
755 i = 0;
756 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
757
758 for (tmp = tmp->next; tmp; tmp = tmp->next)
759 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
760
761 free_cbuffers:
762 while (first) {
763 tmp = first->next;
764 free((char *) first);
765 first = tmp;
766 }
767 return errors;
768}
769
770/* Skip N_BYTES characters from the start of pipe FD, and print
771 any extra characters that were read beyond that.
772 Return 1 on error, 0 if ok. */
773
774static int start_bytes(const char *filename, int fd, off_t n_bytes)
775{
776 char buffer[BUFSIZ];
777 int bytes_read = 0;
778
779 while (n_bytes > 0 && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0)
780 n_bytes -= bytes_read;
781 if (bytes_read == -1) {
782 detailed_error(0, errno, "%s", filename);
783 return 1;
784 } else if (n_bytes < 0)
785 XWRITE(STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
786 return 0;
787}
788
789/* Skip N_LINES lines at the start of file or pipe FD, and print
790 any extra characters that were read beyond that.
791 Return 1 on error, 0 if ok. */
792
793static int start_lines(const char *filename, int fd, long int n_lines)
794{
795 char buffer[BUFSIZ];
796 int bytes_read = 0;
797 int bytes_to_skip = 0;
798
799 while (n_lines && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
800 bytes_to_skip = 0;
801 while (bytes_to_skip < bytes_read)
802 if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
803 break; 171 break;
804 }
805 if (bytes_read == -1) {
806 detailed_error(0, errno, "%s", filename);
807 return 1;
808 } else if (bytes_to_skip < bytes_read) {
809 XWRITE(STDOUT_FILENO, &buffer[bytes_to_skip],
810 bytes_read - bytes_to_skip);
811 }
812 return 0;
813}
814
815/* Display file FILENAME from the current position in FD to the end.
816 If `forever' is nonzero, keep reading from the end of the file
817 until killed. Return the number of bytes read from the file. */
818
819static long dump_remainder(const char *filename, int fd)
820{
821 char buffer[BUFSIZ];
822 int bytes_read;
823 long total;
824
825 total = 0;
826 output:
827 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
828 XWRITE(STDOUT_FILENO, buffer, bytes_read);
829 total += bytes_read;
830 }
831 if (bytes_read == -1)
832 detailed_error(EXIT_FAILURE, errno, "%s", filename);
833 if (forever) {
834 fflush(stdout);
835 sleep(1);
836 goto output;
837 } else {
838 if (forever_multiple)
839 fflush(stdout);
840 }
841
842 return total;
843}
844
845/* Tail NFILES (>1) files forever until killed. The file names are in
846 NAMES. The open file descriptors are in `file_descs', and the size
847 at which we stopped tailing them is in `file_sizes'. We loop over
848 each of them, doing an fstat to see if they have changed size. If
849 none of them have changed size in one iteration, we sleep for a
850 second and try again. We do this until the user interrupts us. */
851
852static void tail_forever(char **names, int nfiles)
853{
854 int last;
855
856 last = -1;
857
858 while (1) {
859 int i;
860 int changed;
861
862 changed = 0;
863 for (i = 0; i < nfiles; i++) {
864 struct stat stats;
865
866 if (file_descs[i] < 0)
867 continue;
868 if (fstat(file_descs[i], &stats) < 0) {
869 detailed_error(0, errno, "%s", names[i]);
870 file_descs[i] = -1;
871 continue;
872 }
873 if (stats.st_size == file_sizes[i])
874 continue;
875
876 /* This file has changed size. Print out what we can, and
877 then keep looping. */
878
879 changed = 1;
880
881 if (stats.st_size < file_sizes[i]) {
882 write_header(names[i], "file truncated");
883 last = i;
884 lseek(file_descs[i], stats.st_size, SEEK_SET);
885 file_sizes[i] = stats.st_size;
886 continue;
887 }
888
889 if (i != last) {
890 if (print_headers)
891 write_header(names[i], NULL);
892 last = i;
893 } 172 }
894 file_sizes[i] += dump_remainder(names[i], file_descs[i]); 173 sleep(sleep_int);
895 } 174 }
896 175 usleep(sleep_int * 1000);
897 /* If none of the files changed size, sleep. */
898 if (!changed)
899 sleep(1);
900 }
901}
902
903/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
904 Return 0 if successful, 1 if an error occurred. */
905
906static int tail_bytes(const char *filename, int fd, off_t n_bytes)
907{
908 struct stat stats;
909
910 /* FIXME: resolve this like in dd.c. */
911 /* Use fstat instead of checking for errno == ESPIPE because
912 lseek doesn't work on some special files but doesn't return an
913 error, either. */
914 if (fstat(fd, &stats)) {
915 detailed_error(0, errno, "%s", filename);
916 return 1;
917 }
918
919 if (from_start) {
920 if (S_ISREG(stats.st_mode))
921 lseek(fd, n_bytes, SEEK_CUR);
922 else if (start_bytes(filename, fd, n_bytes))
923 return 1;
924 dump_remainder(filename, fd);
925 } else {
926 if (S_ISREG(stats.st_mode)) {
927 off_t current_pos, end_pos;
928 size_t bytes_remaining;
929
930 if ((current_pos = lseek(fd, (off_t) 0, SEEK_CUR)) != -1
931 && (end_pos = lseek(fd, (off_t) 0, SEEK_END)) != -1) {
932 off_t diff;
933
934 /* Be careful here. The current position may actually be
935 beyond the end of the file. */
936 bytes_remaining = (diff =
937 end_pos - current_pos) < 0 ? 0 : diff;
938 } else {
939 detailed_error(0, errno, "%s", filename);
940 return 1;
941 }
942
943 if (bytes_remaining <= n_bytes) {
944 /* From the current position to end of file, there are no
945 more bytes than have been requested. So reposition the
946 file pointer to the incoming current position and print
947 everything after that. */
948 lseek(fd, current_pos, SEEK_SET);
949 } else {
950 /* There are more bytes remaining than were requested.
951 Back up. */
952 lseek(fd, -n_bytes, SEEK_END);
953 }
954 dump_remainder(filename, fd);
955 } else
956 return pipe_bytes(filename, fd, n_bytes);
957 } 176 }
177 if (buffer)
178 free(buffer);
958 return 0; 179 return 0;
959} 180}
960 181
961/* Output the last N_LINES lines of file FILENAME open for reading in FD. 182void add_file(char *name)
962 Return 0 if successful, 1 if an error occurred. */
963
964static int tail_lines(const char *filename, int fd, long int n_lines)
965{ 183{
966 struct stat stats; 184 ++n_files;
967 off_t length; 185 files = realloc(files, n_files);
968 186 files[n_files - 1] = (char *) malloc(strlen(name) + 1);
969 if (fstat(fd, &stats)) { 187 strcpy(files[n_files - 1], name);
970 detailed_error(0, errno, "%s", filename);
971 return 1;
972 }
973
974 if (from_start) {
975 if (start_lines(filename, fd, n_lines))
976 return 1;
977 dump_remainder(filename, fd);
978 } else {
979 /* Use file_lines only if FD refers to a regular file with
980 its file pointer positioned at beginning of file. */
981 /* FIXME: adding the lseek conjunct is a kludge.
982 Once there's a reasonable test suite, fix the true culprit:
983 file_lines. file_lines shouldn't presume that the input
984 file pointer is initially positioned to beginning of file. */
985 if (S_ISREG(stats.st_mode)
986 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
987 length = lseek(fd, (off_t) 0, SEEK_END);
988 if (length != 0 && file_lines(filename, fd, n_lines, length))
989 return 1;
990 dump_remainder(filename, fd);
991 } else
992 return pipe_lines(filename, fd, n_lines);
993 }
994 return 0;
995} 188}
996 189
997/* Display the last N_UNITS units of file FILENAME, open for reading
998 in FD.
999 Return 0 if successful, 1 if an error occurred. */
1000 190
1001static int tail(const char *filename, int fd, off_t n_units) 191int tail_main(int argc, char **argv)
1002{ 192{
1003 if (count_lines) 193 int show_headers = 1;
1004 return tail_lines(filename, fd, (long) n_units); 194 int test;
1005 else 195 int c;
1006 return tail_bytes(filename, fd, n_units); 196 int nargs=0;
1007} 197 char **argn=NULL;
1008 198
1009/* Display the last N_UNITS units of file FILENAME. 199 opterr = 0;
1010 "-" for FILENAME means the standard input. 200
1011 FILENUM is this file's index in the list of files the user gave. 201 for(c=0;c<argc;c++){
1012 Return 0 if successful, 1 if an error occurred. */ 202 test=atoi(argv[c]);
1013 203 if(test){
1014static int tail_file(const char *filename, off_t n_units, int filenum) 204 units=test;
1015{ 205 if(units<0)
1016 int fd, errors; 206 units=units-1;
1017 struct stat stats; 207 }else{
1018 208 nargs++;
1019 if (!strcmp(filename, "-")) { 209 argn = realloc(argn, nargs);
1020 have_read_stdin = 1; 210 argn[nargs - 1] = (char *) malloc(strlen(argv[c]) + 1);
1021 filename = "standard input"; 211 strcpy(argn[nargs - 1], argv[c]);
1022 if (print_headers)
1023 write_header(filename, NULL);
1024 errors = tail(filename, 0, n_units);
1025 if (forever_multiple) {
1026 if (fstat(0, &stats) < 0) {
1027 detailed_error(0, errno, "standard input");
1028 errors = 1;
1029 } else if (!S_ISREG(stats.st_mode)) {
1030 detailed_error(0, 0,
1031 "standard input: cannot follow end of non-regular file");
1032 errors = 1;
1033 }
1034 if (errors)
1035 file_descs[filenum] = -1;
1036 else {
1037 file_descs[filenum] = 0;
1038 file_sizes[filenum] = stats.st_size;
1039 }
1040 }
1041 } else {
1042 /* Not standard input. */
1043 fd = open(filename, O_RDONLY);
1044 if (fd == -1) {
1045 if (forever_multiple)
1046 file_descs[filenum] = -1;
1047 detailed_error(0, errno, "%s", filename);
1048 errors = 1;
1049 } else {
1050 if (print_headers)
1051 write_header(filename, NULL);
1052 errors = tail(filename, fd, n_units);
1053 if (forever_multiple) {
1054 if (fstat(fd, &stats) < 0) {
1055 detailed_error(0, errno, "%s", filename);
1056 errors = 1;
1057 } else if (!S_ISREG(stats.st_mode)) {
1058 detailed_error(0, 0,
1059 "%s: cannot follow end of non-regular file",
1060 filename);
1061 errors = 1;
1062 }
1063 if (errors) {
1064 close(fd);
1065 file_descs[filenum] = -1;
1066 } else {
1067 file_descs[filenum] = fd;
1068 file_sizes[filenum] = stats.st_size;
1069 }
1070 } else {
1071 if (close(fd)) {
1072 detailed_error(0, errno, "%s", filename);
1073 errors = 1;
1074 }
1075 }
1076 } 212 }
1077 } 213 }
214 while (1) {
215 int opt_index = 0;
1078 216
1079 return errors; 217 c = getopt_long_only(nargs, argn,
1080} 218 "c:fhn:s:qv", NULL, &opt_index);
1081 219 if (c == -1)
1082extern int tail_main(int argc, char **argv) 220 break;
1083{ 221 switch (c) {
1084 int stopit = 0;
1085 enum header_mode header_mode = multiple_files;
1086 int exit_status = 0;
1087
1088 /* If from_start, the number of items to skip before printing; otherwise,
1089 the number of items at the end of the file to print. Initially, -1
1090 means the value has not been set. */
1091 off_t n_units = -1;
1092 int n_files;
1093 char **file;
1094
1095 program_name = argv[0];
1096 have_read_stdin = 0;
1097 count_lines = 1;
1098 forever = forever_multiple = from_start = print_headers = 0;
1099
1100 /* Parse any options */
1101 //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv);
1102 while (--argc > 0 && (**(++argv) == '-' || **argv == '+')) {
1103 if (**argv == '+') {
1104 from_start = 1;
1105 }
1106 stopit = 0;
1107 while (stopit == 0 && *(++(*argv))) {
1108 switch (**argv) {
1109 case 'c':
1110 count_lines = 0;
1111
1112 if (--argc < 1) {
1113 usage(tail_usage);
1114 }
1115 n_units = getNum(*(++argv));
1116 stopit = 1;
1117 break;
1118
1119 case 'f':
1120 forever = 1;
1121 break;
1122 222
1123 case 'n': 223#ifndef BB_FEATURE_SIMPLE_TAIL
1124 count_lines = 1;
1125 224
1126 if (--argc < 1) { 225 case 'c':
226 unit_type = BYTES;
227 test = atoi(optarg);
228 if(test==0)
229 usage(tail_usage);
230 if(optarg[strlen(optarg)-1]>'9') {
231 switch (optarg[strlen(optarg)-1]) {
232 case 'b':
233 test *= 512;
234 break;
235 case 'k':
236 test *= 1024;
237 break;
238 case 'm':
239 test *= (1024 * 1024);
240 break;
241 default:
242 fprintf(stderr,"Size must be b,k, or m.");
1127 usage(tail_usage); 243 usage(tail_usage);
1128 } 244 }
1129 n_units = atol(*(++argv));
1130 stopit = 1;
1131 break;
1132
1133 case 'q':
1134 header_mode = never;
1135 break;
1136
1137 case 'v':
1138 header_mode = always;
1139 break;
1140
1141 default:
1142 usage(tail_usage);
1143 } 245 }
246 if(optarg[0]=='+')
247 units=test+1;
248 else
249 units=-(test+1);
250 break;
251 case 'q':
252 show_headers = 0;
253 break;
254 case 's':
255 sleep_int = atoi(optarg);
256 if(sleep_int<1)
257 sleep_int=1;
258 break;
259 case 'v':
260 verbose = 1;
261 break;
262#endif
263 case 'f':
264 follow = 1;
265 break;
266 case 'h':
267 usage(tail_usage);
268 break;
269 case 'n':
270 test = atoi(optarg);
271 if (test) {
272 if (optarg[0] == '+')
273 units = test;
274 else
275 units = -(test+1);
276 } else
277 usage(tail_usage);
278 break;
279 default:
280 errorMsg("\nUnknown arg: %c.\n\n",c);
281 usage(tail_usage);
1144 } 282 }
1145 } 283 }
1146 284 while (optind < nargs) {
1147 285 if (!strcmp(argn[optind], "-"))
1148 if (n_units == -1) 286 add_file(STDIN);
1149 n_units = DEFAULT_N_LINES; 287 else
1150 288 add_file(argn[optind]);
1151 /* To start printing with item N_UNITS from the start of the file, skip 289 optind++;
1152 N_UNITS - 1 items. `tail +0' is actually meaningless, but for Unix 290 }
1153 compatibility it's treated the same as `tail +1'. */ 291 if(units==0)
1154 if (from_start) { 292 units=-11;
1155 if (n_units) 293 if(units>0)
1156 --n_units; 294 units--;
1157 } 295 if (n_files == 0)
1158 296 add_file(STDIN);
1159 n_files = argc; 297 if (n_files == 1)
1160 file = argv; 298#ifndef BB_FEATURE_SIMPLE_TAIL
1161 299 if (!verbose)
1162 if (n_files > 1 && forever) { 300#endif
1163 forever_multiple = 1; 301 show_headers = 0;
1164 forever = 0; 302 for (test = 0; test < n_files; test++) {
1165 file_descs = (int *) xmalloc(n_files * sizeof(int)); 303 if (show_headers)
1166 304 printf("==> %s <==\n", files[test]);
1167 file_sizes = (off_t *) xmalloc(n_files * sizeof(off_t)); 305 tail_stream(test);
1168 } 306 }
1169 307 if(files)
1170 if (header_mode == always 308 free(files);
1171 || (header_mode == multiple_files && n_files > 1)) 309 if(argn)
1172 print_headers = 1; 310 free(argn);
1173 311 return 0;
1174 if (n_files == 0) {
1175 exit_status |= tail_file("-", n_units, 0);
1176 } else {
1177 int i;
1178
1179 for (i = 0; i < n_files; i++)
1180 exit_status |= tail_file(file[i], n_units, i);
1181
1182 if (forever_multiple)
1183 tail_forever(file, n_files);
1184 }
1185
1186 if (have_read_stdin && close(0) < 0)
1187 detailed_error(EXIT_FAILURE, errno, "-");
1188 if (fclose(stdout) == EOF)
1189 detailed_error(EXIT_FAILURE, errno, "write error");
1190 exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
1191} 312}
1192 313
1193 314/*
1194#endif 315Local Variables:
316c-file-style: "linux"
317c-basic-offset: 4
318tab-width: 4
319End:
320*/
diff --git a/tail.c b/tail.c
index 2746af83b..4a1aa8436 100644
--- a/tail.c
+++ b/tail.c
@@ -1,15 +1,4 @@
1/* vi: set sw=4 ts=4: */ 1/* vi: set sw=4 ts=4: */
2#include "internal.h"
3
4/* 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) 2/* tail -- output the last part of file(s)
14 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc. 3 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
15 4
@@ -32,1163 +21,300 @@
32 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>. 21 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
33 22
34 Rewrote the option parser, removed locales support, 23 Rewrote the option parser, removed locales support,
35 and generally busyboxed, Erik Andersen <andersen@lineo.com> 24 and generally busyboxed, Erik Andersen <andersen@lineo.com>
36 25
37 Removed superfluous options and associated code ("-c", "-n", "-q"). 26 Removed superfluous options and associated code ("-c", "-n", "-q").
38 Removed "tail -f" support for multiple files. 27 Removed "tail -f" support for multiple files.
39 Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>. 28 Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>.
40 29
41 */ 30 Compleate Rewrite to correctly support "-NUM", "+NUM", and "-s" by
42 31 E.Allen Soard (esp@espsw.net).
43 32
44#include <stdio.h> 33 */
45#include <stdarg.h>
46#include <sys/types.h> 34#include <sys/types.h>
47#include <sys/stat.h> 35#include <sys/stat.h>
48#include <fcntl.h> 36#include <fcntl.h>
49#include <ctype.h> 37#include <stdio.h>
50#define BB_DECLARE_EXTERN 38#include <stdlib.h>
51#define bb_need_help 39#include <unistd.h>
52#include "messages.c" 40#include <string.h>
41#include <getopt.h>
42#include "internal.h"
53 43
44#define STDIN "standard input"
45#define LINES 0
46#define BYTES 1
54 47
55#define XWRITE(fd, buffer, n_bytes) \ 48static int n_files = 0;
56 do { \ 49static char **files = NULL;
57 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
58 errorMsg("write error"); \
59 } while (0)
60 50
61/* Number of items to tail. */ 51static char follow=0;
62#define DEFAULT_N_LINES 10
63 52
64/* Size of atomic reads. */ 53#ifdef BB_FEATURE_SIMPLE_TAIL
65#ifndef BUFSIZ 54static const char unit_type=LINES;
66#define BUFSIZ (512 * 8) 55static const char sleep_int=1;
56#else
57static char unit_type=LINES;
58static int sleep_int=1;
59static char verbose = 0;
67#endif 60#endif
68 61
69/* If nonzero, read from the end of one file until killed. */ 62//static off_t units=-11;
70static int forever; 63static off_t units=0;
71
72/* If nonzero, print filename headers. */
73static int print_headers;
74 64
75static void write_header(const char *filename) 65int tail_stream(int file_id)
76{ 66{
77 static int first_file = 1; 67 int fd;
78 68 ssize_t bytes_read=0;
79 printf("%s==> %s <==\n", (first_file ? "" : "\n"), filename); 69 ssize_t bs=BUFSIZ;
80 first_file = 0; 70 ssize_t startpoint=bs;
81} 71 ssize_t endpoint=0;
82 72 ssize_t count=0;
83/* Print the last N_LINES lines from the end of file FD. 73 ssize_t filesize=0;
84 Go backward through the file, reading `BUFSIZ' bytes at a time (except 74 ssize_t filelocation=0;
85 probably the first), until we hit the start of the file or have 75 char direction=1;
86 read NUMBER newlines. 76 char * buffer;
87 POS starts out as the length of the file (the offset of the last 77 char pipe;
88 byte of the file + 1). 78
89 Return 0 if successful, 1 if an error occurred. */ 79
90 80 if (!strcmp(files[file_id], STDIN))
91static int 81 fd = 0;
92file_lines(int fd, long int n_lines, off_t pos) 82 else
93{ 83 fd = open(files[file_id], O_RDONLY);
94 char buffer[BUFSIZ]; 84 if (fd == -1)
95 int bytes_read; 85 fatalError("Unable to open file %s.\n", files[file_id]);
96 int i; /* Index into `buffer' for scanning. */ 86
97 87 buffer=malloc(bs);
98 if (n_lines == 0) 88
99 return 0; 89 filesize=lseek(fd, -1, SEEK_END)+1;
100 90 pipe=(filesize<=0);
101 /* Set `bytes_read' to the size of the last, probably partial, buffer; 91
102 0 < `bytes_read' <= `BUFSIZ'. */ 92 if(units>=0)
103 bytes_read = pos % BUFSIZ; 93 lseek(fd,0,SEEK_SET);
104 if (bytes_read == 0) 94 else {
105 bytes_read = BUFSIZ; 95 direction=-1;
106 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all 96 count=1;
107 reads will be on block boundaries, which might increase efficiency. */ 97 }
108 pos -= bytes_read; 98 while(units != 0) {
109 lseek(fd, pos, SEEK_SET); 99 if (pipe) {
110 bytes_read = fullRead(fd, buffer, bytes_read); 100 char * line;
111 if (bytes_read == -1) 101 ssize_t f_size=0;
112 errorMsg("read error"); 102
113 103 bs=BUFSIZ;
114 /* Count the incomplete line on files that don't end with a newline. */ 104 line=malloc(bs);
115 if (bytes_read && buffer[bytes_read - 1] != '\n') 105 while(1) {
116 --n_lines; 106 bytes_read=read(fd,line,bs);
117 107 if(bytes_read<=0)
118 do { 108 break;
119 /* Scan backward, counting the newlines in this bufferfull. */ 109 buffer=realloc(buffer,f_size+bytes_read);
120 for (i = bytes_read - 1; i >= 0; i--) { 110 memcpy(&buffer[f_size],line,bytes_read);
121 /* Have we counted the requested number of newlines yet? */ 111 filelocation=f_size+=bytes_read;
122 if (buffer[i] == '\n' && n_lines-- == 0) {
123 /* If this newline wasn't the last character in the buffer,
124 print the text after it. */
125 if (i != bytes_read - 1)
126 XWRITE(STDOUT_FILENO, &buffer[i + 1],
127 bytes_read - (i + 1));
128 return 0;
129 } 112 }
130 } 113 bs=f_size;
131 /* Not enough newlines in that bufferfull. */ 114 if(direction<0)
132 if (pos == 0) { 115 bs--;
133 /* Not enough lines in the file; print the entire file. */ 116 if (line)
134 lseek(fd, (off_t) 0, SEEK_SET); 117 free(line);
135 return 0;
136 }
137 pos -= BUFSIZ;
138 lseek(fd, pos, SEEK_SET);
139 }
140 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0);
141 if (bytes_read == -1)
142 errorMsg("read error");
143
144 return 0;
145}
146
147/* Print the last N_LINES lines from the end of the standard input,
148 open for reading as pipe FD.
149 Buffer the text as a linked list of LBUFFERs, adding them as needed.
150 Return 0 if successful, 1 if an error occured. */
151
152static int pipe_lines(int fd, long int n_lines)
153{
154 struct linebuffer {
155 int nbytes, nlines;
156 char buffer[BUFSIZ];
157 struct linebuffer *next;
158 };
159 typedef struct linebuffer LBUFFER;
160 LBUFFER *first, *last, *tmp;
161 int i; /* Index into buffers. */
162 int total_lines = 0; /* Total number of newlines in all buffers. */
163 int errors = 0;
164
165 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
166 first->nbytes = first->nlines = 0;
167 first->next = NULL;
168 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
169
170 /* Input is always read into a fresh buffer. */
171 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
172 tmp->nlines = 0;
173 tmp->next = NULL;
174
175 /* Count the number of newlines just read. */
176 for (i = 0; i < tmp->nbytes; i++)
177 if (tmp->buffer[i] == '\n')
178 ++tmp->nlines;
179 total_lines += tmp->nlines;
180
181 /* If there is enough room in the last buffer read, just append the new
182 one to it. This is because when reading from a pipe, `nbytes' can
183 often be very small. */
184 if (tmp->nbytes + last->nbytes < BUFSIZ) {
185 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
186 last->nbytes += tmp->nbytes;
187 last->nlines += tmp->nlines;
188 } else { 118 } else {
189 /* If there's not enough room, link the new buffer onto the end of 119 filelocation = lseek(fd, 0, SEEK_CUR);
190 the list, then either free up the oldest buffer for the next 120 if(direction<0) {
191 read if that would leave enough lines, or else malloc a new one. 121 if(filelocation<bs)
192 Some compaction mechanism is possible but probably not 122 bs=filelocation;
193 worthwhile. */ 123 filelocation = lseek(fd, -bs, SEEK_CUR);
194 last = last->next = tmp;
195 if (total_lines - first->nlines > n_lines) {
196 tmp = first;
197 total_lines -= first->nlines;
198 first = first->next;
199 } else
200 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
201 }
202 }
203 if (tmp->nbytes == -1)
204 errorMsg("read error");
205
206 free((char *) tmp);
207
208 /* This prevents a core dump when the pipe contains no newlines. */
209 if (n_lines == 0)
210 goto free_lbuffers;
211
212 /* Count the incomplete line on files that don't end with a newline. */
213 if (last->buffer[last->nbytes - 1] != '\n') {
214 ++last->nlines;
215 ++total_lines;
216 }
217
218 /* Run through the list, printing lines. First, skip over unneeded
219 buffers. */
220 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
221 total_lines -= tmp->nlines;
222
223 /* Find the correct beginning, then print the rest of the file. */
224 if (total_lines > n_lines) {
225 char *cp;
226
227 /* Skip `total_lines' - `n_lines' newlines. We made sure that
228 `total_lines' - `n_lines' <= `tmp->nlines'. */
229 cp = tmp->buffer;
230 for (i = total_lines - n_lines; i; --i)
231 while (*cp++ != '\n')
232 /* Do nothing. */ ;
233 i = cp - tmp->buffer;
234 } else
235 i = 0;
236 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
237
238 for (tmp = tmp->next; tmp; tmp = tmp->next)
239 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
240
241 free_lbuffers:
242 while (first) {
243 tmp = first->next;
244 free((char *) first);
245 first = tmp;
246 }
247 return errors;
248}
249
250/* Display file FILENAME from the current position in FD to the end.
251 If `forever' is nonzero, keep reading from the end of the file
252 until killed. Return the number of bytes read from the file. */
253
254static long dump_remainder(int fd)
255{
256 char buffer[BUFSIZ];
257 int bytes_read;
258 long total;
259
260 total = 0;
261 output:
262 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
263 XWRITE(STDOUT_FILENO, buffer, bytes_read);
264 total += bytes_read;
265 }
266 if (bytes_read == -1)
267 errorMsg("read error");
268 if (forever) {
269 fflush(stdout);
270 sleep(1);
271 goto output;
272 }
273
274 return total;
275}
276
277/* Output the last N_LINES lines of file FILENAME open for reading in FD.
278 Return 0 if successful, 1 if an error occurred. */
279
280static int tail_lines(const char *filename, int fd, long int n_lines)
281{
282 struct stat stats;
283 off_t length;
284
285 if (print_headers)
286 write_header(filename);
287
288 if (fstat(fd, &stats))
289 errorMsg("fstat error");
290
291 /* Use file_lines only if FD refers to a regular file with
292 its file pointer positioned at beginning of file. */
293 /* FIXME: adding the lseek conjunct is a kludge.
294 Once there's a reasonable test suite, fix the true culprit:
295 file_lines. file_lines shouldn't presume that the input
296 file pointer is initially positioned to beginning of file. */
297 if (S_ISREG(stats.st_mode)
298 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
299 length = lseek(fd, (off_t) 0, SEEK_END);
300 if (length != 0 && file_lines(fd, n_lines, length))
301 return 1;
302 dump_remainder(fd);
303 } else
304 return pipe_lines(fd, n_lines);
305
306 return 0;
307}
308
309/* Display the last N_UNITS lines of file FILENAME.
310 "-" for FILENAME means the standard input.
311 Return 0 if successful, 1 if an error occurred. */
312
313static int tail_file(const char *filename, off_t n_units)
314{
315 int fd, errors;
316
317 if (!strcmp(filename, "-")) {
318 filename = "standard input";
319 errors = tail_lines(filename, 0, (long) n_units);
320 } else {
321 /* Not standard input. */
322 fd = open(filename, O_RDONLY);
323 if (fd == -1)
324 perror(filename);
325
326 errors = tail_lines(filename, fd, (long) n_units);
327 close(fd);
328 }
329
330 return errors;
331}
332
333extern int tail_main(int argc, char **argv)
334{
335 int exit_status = 0;
336 int n_units = DEFAULT_N_LINES;
337 int n_tmp, i;
338 char opt;
339
340 forever = print_headers = 0;
341
342 /* parse argv[] */
343 for (i = 1; i < argc; i++) {
344 if (argv[i][0] == '-') {
345 opt = argv[i][1];
346 switch (opt) {
347 case 'f':
348 forever = 1;
349 break;
350 case 'n':
351 n_tmp = 0;
352 if (++i < argc)
353 n_tmp = atoi(argv[i]);
354 if (n_tmp < 1)
355 usage(tail_usage);
356 n_units = n_tmp;
357 break;
358 case '-':
359 case 'h':
360 usage(tail_usage);
361 default:
362 if ((n_units = atoi(&argv[i][1])) < 1) {
363 errorMsg("invalid option -- %c\n", opt);
364 usage(tail_usage);
365 }
366 } 124 }
367 } else { 125 bytes_read = read(fd, buffer, bs);
368 break; 126 if (bytes_read <= 0)
127 break;
128 bs=bytes_read;
369 } 129 }
370 } 130 startpoint=bs;
371 131 if(direction>0) {
372 if (i + 1 < argc) { 132 endpoint=startpoint;
373 if (forever) { 133 startpoint=0;
374 errorMsg("option -f is invalid with multiple files\n");
375 usage(tail_usage);
376 } 134 }
377 print_headers = 1; 135 for(;startpoint!=endpoint;startpoint+=direction) {
378 } 136#ifndef BB_FEATURE_SIMPLE_TAIL
379 137 if(unit_type==BYTES)
380 if (i >= argc) { 138 count++;
381 exit_status |= tail_file("-", n_units); 139 else
382 } else {
383 for (; i < argc; i++)
384 exit_status |= tail_file(argv[i], n_units);
385 }
386
387 return(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
388}
389
390
391#else
392// Here follows the code for the full featured tail code
393
394
395/* tail -- output the last part of file(s)
396 Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
397
398 This program is free software; you can redistribute it and/or modify
399 it under the terms of the GNU General Public License as published by
400 the Free Software Foundation; either version 2, or (at your option)
401 any later version.
402
403 This program is distributed in the hope that it will be useful,
404 but WITHOUT ANY WARRANTY; without even the implied warranty of
405 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
406 GNU General Public License for more details.
407
408 You should have received a copy of the GNU General Public License
409 along with this program; if not, write to the Free Software
410 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
411
412 Original version by Paul Rubin <phr@ocf.berkeley.edu>.
413 Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
414 tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.
415
416 Rewrote the option parser, removed locales support,
417 and generally busyboxed, Erik Andersen <andersen@lineo.com>
418
419 */
420
421#include "internal.h"
422
423#include <stdio.h>
424#include <stdarg.h>
425#include <assert.h>
426#include <errno.h>
427#include <sys/types.h>
428#include <sys/types.h>
429#include <sys/stat.h>
430#include <fcntl.h>
431#include <ctype.h>
432
433
434
435/* Disable assertions. Some systems have broken assert macros. */
436#define NDEBUG 1
437
438
439static void detailed_error(int i, int errnum, char *fmt, ...)
440 __attribute__ ((format (printf, 3, 4)));
441static void detailed_error(int i, int errnum, char *fmt, ...)
442{
443 va_list arguments;
444
445 va_start(arguments, fmt);
446 vfprintf(stderr, fmt, arguments);
447 fprintf(stderr, "\n%s\n", strerror(errnum));
448 va_end(arguments);
449 exit(i);
450}
451
452
453#define XWRITE(fd, buffer, n_bytes) \
454 do \
455 { \
456 assert ((fd) == 1); \
457 assert ((n_bytes) >= 0); \
458 if (n_bytes > 0 && fwrite ((buffer), 1, (n_bytes), stdout) == 0) \
459 detailed_error (EXIT_FAILURE, errno, "write error"); \
460 } \
461 while (0)
462
463/* Number of items to tail. */
464#define DEFAULT_N_LINES 10
465
466/* Size of atomic reads. */
467#ifndef BUFSIZ
468#define BUFSIZ (512 * 8)
469#endif 140#endif
470 141 if(buffer[startpoint-1]=='\n')
471/* If nonzero, interpret the numeric argument as the number of lines. 142 count++;
472 Otherwise, interpret it as the number of bytes. */ 143 if (!pipe)
473static int count_lines; 144 filelocation=lseek(fd,0,SEEK_CUR);
474 145 if(count==abs(units))
475/* If nonzero, read from the end of one file until killed. */ 146 break;
476static int forever;
477
478/* If nonzero, read from the end of multiple files until killed. */
479static int forever_multiple;
480
481/* Array of file descriptors if forever_multiple is 1. */
482static int *file_descs;
483
484/* Array of file sizes if forever_multiple is 1. */
485static off_t *file_sizes;
486
487/* If nonzero, count from start of file instead of end. */
488static int from_start;
489
490/* If nonzero, print filename headers. */
491static int print_headers;
492
493/* When to print the filename banners. */
494enum header_mode {
495 multiple_files, always, never
496};
497
498/* The name this program was run with. */
499char *program_name;
500
501/* Nonzero if we have ever read standard input. */
502static int have_read_stdin;
503
504static void write_header(const char *filename, const char *comment)
505{
506 static int first_file = 1;
507
508 printf("%s==> %s%s%s <==\n", (first_file ? "" : "\n"), filename,
509 (comment ? ": " : ""), (comment ? comment : ""));
510 first_file = 0;
511}
512
513/* Print the last N_LINES lines from the end of file FD.
514 Go backward through the file, reading `BUFSIZ' bytes at a time (except
515 probably the first), until we hit the start of the file or have
516 read NUMBER newlines.
517 POS starts out as the length of the file (the offset of the last
518 byte of the file + 1).
519 Return 0 if successful, 1 if an error occurred. */
520
521static int
522file_lines(const char *filename, int fd, long int n_lines, off_t pos)
523{
524 char buffer[BUFSIZ];
525 int bytes_read;
526 int i; /* Index into `buffer' for scanning. */
527
528 if (n_lines == 0)
529 return 0;
530
531 /* Set `bytes_read' to the size of the last, probably partial, buffer;
532 0 < `bytes_read' <= `BUFSIZ'. */
533 bytes_read = pos % BUFSIZ;
534 if (bytes_read == 0)
535 bytes_read = BUFSIZ;
536 /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
537 reads will be on block boundaries, which might increase efficiency. */
538 pos -= bytes_read;
539 lseek(fd, pos, SEEK_SET);
540 bytes_read = fullRead(fd, buffer, bytes_read);
541 if (bytes_read == -1) {
542 detailed_error(0, errno, "%s", filename);
543 return 1;
544 }
545
546 /* Count the incomplete line on files that don't end with a newline. */
547 if (bytes_read && buffer[bytes_read - 1] != '\n')
548 --n_lines;
549
550 do {
551 /* Scan backward, counting the newlines in this bufferfull. */
552 for (i = bytes_read - 1; i >= 0; i--) {
553 /* Have we counted the requested number of newlines yet? */
554 if (buffer[i] == '\n' && n_lines-- == 0) {
555 /* If this newline wasn't the last character in the buffer,
556 print the text after it. */
557 if (i != bytes_read - 1)
558 XWRITE(STDOUT_FILENO, &buffer[i + 1],
559 bytes_read - (i + 1));
560 return 0;
561 }
562 }
563 /* Not enough newlines in that bufferfull. */
564 if (pos == 0) {
565 /* Not enough lines in the file; print the entire file. */
566 lseek(fd, (off_t) 0, SEEK_SET);
567 return 0;
568 } 147 }
569 pos -= BUFSIZ; 148 if((count==abs(units)) | pipe)
570 lseek(fd, pos, SEEK_SET); 149 break;
571 } 150 if(direction<0){
572 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0); 151 filelocation = lseek(fd, -bytes_read, SEEK_CUR);
573 if (bytes_read == -1) { 152 if(filelocation==0)
574 detailed_error(0, errno, "%s", filename); 153 break;
575 return 1;
576 }
577 return 0;
578}
579
580/* Print the last N_LINES lines from the end of the standard input,
581 open for reading as pipe FD.
582 Buffer the text as a linked list of LBUFFERs, adding them as needed.
583 Return 0 if successful, 1 if an error occured. */
584
585static int pipe_lines(const char *filename, int fd, long int n_lines)
586{
587 struct linebuffer {
588 int nbytes, nlines;
589 char buffer[BUFSIZ];
590 struct linebuffer *next;
591 };
592 typedef struct linebuffer LBUFFER;
593 LBUFFER *first, *last, *tmp;
594 int i; /* Index into buffers. */
595 int total_lines = 0; /* Total number of newlines in all buffers. */
596 int errors = 0;
597
598 first = last = (LBUFFER *) xmalloc(sizeof(LBUFFER));
599 first->nbytes = first->nlines = 0;
600 first->next = NULL;
601 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
602
603 /* Input is always read into a fresh buffer. */
604 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
605 tmp->nlines = 0;
606 tmp->next = NULL;
607
608 /* Count the number of newlines just read. */
609 for (i = 0; i < tmp->nbytes; i++)
610 if (tmp->buffer[i] == '\n')
611 ++tmp->nlines;
612 total_lines += tmp->nlines;
613
614 /* If there is enough room in the last buffer read, just append the new
615 one to it. This is because when reading from a pipe, `nbytes' can
616 often be very small. */
617 if (tmp->nbytes + last->nbytes < BUFSIZ) {
618 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
619 last->nbytes += tmp->nbytes;
620 last->nlines += tmp->nlines;
621 } else {
622 /* If there's not enough room, link the new buffer onto the end of
623 the list, then either free up the oldest buffer for the next
624 read if that would leave enough lines, or else malloc a new one.
625 Some compaction mechanism is possible but probably not
626 worthwhile. */
627 last = last->next = tmp;
628 if (total_lines - first->nlines > n_lines) {
629 tmp = first;
630 total_lines -= first->nlines;
631 first = first->next;
632 } else
633 tmp = (LBUFFER *) xmalloc(sizeof(LBUFFER));
634 } 154 }
635 } 155 }
636 if (tmp->nbytes == -1) { 156 if(pipe && (direction<0))
637 detailed_error(0, errno, "%s", filename); 157 bs++;
638 errors = 1; 158 bytes_read=bs-startpoint;
639 free((char *) tmp); 159 memcpy(&buffer[0],&buffer[startpoint],bytes_read);
640 goto free_lbuffers;
641 }
642
643 free((char *) tmp);
644
645 /* This prevents a core dump when the pipe contains no newlines. */
646 if (n_lines == 0)
647 goto free_lbuffers;
648
649 /* Count the incomplete line on files that don't end with a newline. */
650 if (last->buffer[last->nbytes - 1] != '\n') {
651 ++last->nlines;
652 ++total_lines;
653 }
654 160
655 /* Run through the list, printing lines. First, skip over unneeded 161 bs=BUFSIZ;
656 buffers. */ 162 while (1) {
657 for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next) 163 if((filelocation>0 || pipe)){
658 total_lines -= tmp->nlines; 164 write(1,buffer,bytes_read);
659
660 /* Find the correct beginning, then print the rest of the file. */
661 if (total_lines > n_lines) {
662 char *cp;
663
664 /* Skip `total_lines' - `n_lines' newlines. We made sure that
665 `total_lines' - `n_lines' <= `tmp->nlines'. */
666 cp = tmp->buffer;
667 for (i = total_lines - n_lines; i; --i)
668 while (*cp++ != '\n')
669 /* Do nothing. */ ;
670 i = cp - tmp->buffer;
671 } else
672 i = 0;
673 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
674
675 for (tmp = tmp->next; tmp; tmp = tmp->next)
676 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
677
678 free_lbuffers:
679 while (first) {
680 tmp = first->next;
681 free((char *) first);
682 first = tmp;
683 }
684 return errors;
685}
686
687/* Print the last N_BYTES characters from the end of pipe FD.
688 This is a stripped down version of pipe_lines.
689 Return 0 if successful, 1 if an error occurred. */
690
691static int pipe_bytes(const char *filename, int fd, off_t n_bytes)
692{
693 struct charbuffer {
694 int nbytes;
695 char buffer[BUFSIZ];
696 struct charbuffer *next;
697 };
698 typedef struct charbuffer CBUFFER;
699 CBUFFER *first, *last, *tmp;
700 int i; /* Index into buffers. */
701 int total_bytes = 0; /* Total characters in all buffers. */
702 int errors = 0;
703
704 first = last = (CBUFFER *) xmalloc(sizeof(CBUFFER));
705 first->nbytes = 0;
706 first->next = NULL;
707 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
708
709 /* Input is always read into a fresh buffer. */
710 while ((tmp->nbytes = fullRead(fd, tmp->buffer, BUFSIZ)) > 0) {
711 tmp->next = NULL;
712
713 total_bytes += tmp->nbytes;
714 /* If there is enough room in the last buffer read, just append the new
715 one to it. This is because when reading from a pipe, `nbytes' can
716 often be very small. */
717 if (tmp->nbytes + last->nbytes < BUFSIZ) {
718 memcpy(&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
719 last->nbytes += tmp->nbytes;
720 } else {
721 /* If there's not enough room, link the new buffer onto the end of
722 the list, then either free up the oldest buffer for the next
723 read if that would leave enough characters, or else malloc a new
724 one. Some compaction mechanism is possible but probably not
725 worthwhile. */
726 last = last->next = tmp;
727 if (total_bytes - first->nbytes > n_bytes) {
728 tmp = first;
729 total_bytes -= first->nbytes;
730 first = first->next;
731 } else {
732 tmp = (CBUFFER *) xmalloc(sizeof(CBUFFER));
733 }
734 } 165 }
735 } 166 bytes_read = read(fd, buffer, bs);
736 if (tmp->nbytes == -1) { 167 filelocation+=bytes_read;
737 detailed_error(0, errno, "%s", filename); 168 if (bytes_read <= 0) {
738 errors = 1; 169 if (!follow) {
739 free((char *) tmp); 170 close(fd);
740 goto free_cbuffers;
741 }
742
743 free((char *) tmp);
744
745 /* Run through the list, printing characters. First, skip over unneeded
746 buffers. */
747 for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
748 total_bytes -= tmp->nbytes;
749
750 /* Find the correct beginning, then print the rest of the file.
751 We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
752 if (total_bytes > n_bytes)
753 i = total_bytes - n_bytes;
754 else
755 i = 0;
756 XWRITE(STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
757
758 for (tmp = tmp->next; tmp; tmp = tmp->next)
759 XWRITE(STDOUT_FILENO, tmp->buffer, tmp->nbytes);
760
761 free_cbuffers:
762 while (first) {
763 tmp = first->next;
764 free((char *) first);
765 first = tmp;
766 }
767 return errors;
768}
769
770/* Skip N_BYTES characters from the start of pipe FD, and print
771 any extra characters that were read beyond that.
772 Return 1 on error, 0 if ok. */
773
774static int start_bytes(const char *filename, int fd, off_t n_bytes)
775{
776 char buffer[BUFSIZ];
777 int bytes_read = 0;
778
779 while (n_bytes > 0 && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0)
780 n_bytes -= bytes_read;
781 if (bytes_read == -1) {
782 detailed_error(0, errno, "%s", filename);
783 return 1;
784 } else if (n_bytes < 0)
785 XWRITE(STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
786 return 0;
787}
788
789/* Skip N_LINES lines at the start of file or pipe FD, and print
790 any extra characters that were read beyond that.
791 Return 1 on error, 0 if ok. */
792
793static int start_lines(const char *filename, int fd, long int n_lines)
794{
795 char buffer[BUFSIZ];
796 int bytes_read = 0;
797 int bytes_to_skip = 0;
798
799 while (n_lines && (bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
800 bytes_to_skip = 0;
801 while (bytes_to_skip < bytes_read)
802 if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
803 break; 171 break;
804 }
805 if (bytes_read == -1) {
806 detailed_error(0, errno, "%s", filename);
807 return 1;
808 } else if (bytes_to_skip < bytes_read) {
809 XWRITE(STDOUT_FILENO, &buffer[bytes_to_skip],
810 bytes_read - bytes_to_skip);
811 }
812 return 0;
813}
814
815/* Display file FILENAME from the current position in FD to the end.
816 If `forever' is nonzero, keep reading from the end of the file
817 until killed. Return the number of bytes read from the file. */
818
819static long dump_remainder(const char *filename, int fd)
820{
821 char buffer[BUFSIZ];
822 int bytes_read;
823 long total;
824
825 total = 0;
826 output:
827 while ((bytes_read = fullRead(fd, buffer, BUFSIZ)) > 0) {
828 XWRITE(STDOUT_FILENO, buffer, bytes_read);
829 total += bytes_read;
830 }
831 if (bytes_read == -1)
832 detailed_error(EXIT_FAILURE, errno, "%s", filename);
833 if (forever) {
834 fflush(stdout);
835 sleep(1);
836 goto output;
837 } else {
838 if (forever_multiple)
839 fflush(stdout);
840 }
841
842 return total;
843}
844
845/* Tail NFILES (>1) files forever until killed. The file names are in
846 NAMES. The open file descriptors are in `file_descs', and the size
847 at which we stopped tailing them is in `file_sizes'. We loop over
848 each of them, doing an fstat to see if they have changed size. If
849 none of them have changed size in one iteration, we sleep for a
850 second and try again. We do this until the user interrupts us. */
851
852static void tail_forever(char **names, int nfiles)
853{
854 int last;
855
856 last = -1;
857
858 while (1) {
859 int i;
860 int changed;
861
862 changed = 0;
863 for (i = 0; i < nfiles; i++) {
864 struct stat stats;
865
866 if (file_descs[i] < 0)
867 continue;
868 if (fstat(file_descs[i], &stats) < 0) {
869 detailed_error(0, errno, "%s", names[i]);
870 file_descs[i] = -1;
871 continue;
872 }
873 if (stats.st_size == file_sizes[i])
874 continue;
875
876 /* This file has changed size. Print out what we can, and
877 then keep looping. */
878
879 changed = 1;
880
881 if (stats.st_size < file_sizes[i]) {
882 write_header(names[i], "file truncated");
883 last = i;
884 lseek(file_descs[i], stats.st_size, SEEK_SET);
885 file_sizes[i] = stats.st_size;
886 continue;
887 }
888
889 if (i != last) {
890 if (print_headers)
891 write_header(names[i], NULL);
892 last = i;
893 } 172 }
894 file_sizes[i] += dump_remainder(names[i], file_descs[i]); 173 sleep(sleep_int);
895 } 174 }
896 175 usleep(sleep_int * 1000);
897 /* If none of the files changed size, sleep. */
898 if (!changed)
899 sleep(1);
900 }
901}
902
903/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
904 Return 0 if successful, 1 if an error occurred. */
905
906static int tail_bytes(const char *filename, int fd, off_t n_bytes)
907{
908 struct stat stats;
909
910 /* FIXME: resolve this like in dd.c. */
911 /* Use fstat instead of checking for errno == ESPIPE because
912 lseek doesn't work on some special files but doesn't return an
913 error, either. */
914 if (fstat(fd, &stats)) {
915 detailed_error(0, errno, "%s", filename);
916 return 1;
917 }
918
919 if (from_start) {
920 if (S_ISREG(stats.st_mode))
921 lseek(fd, n_bytes, SEEK_CUR);
922 else if (start_bytes(filename, fd, n_bytes))
923 return 1;
924 dump_remainder(filename, fd);
925 } else {
926 if (S_ISREG(stats.st_mode)) {
927 off_t current_pos, end_pos;
928 size_t bytes_remaining;
929
930 if ((current_pos = lseek(fd, (off_t) 0, SEEK_CUR)) != -1
931 && (end_pos = lseek(fd, (off_t) 0, SEEK_END)) != -1) {
932 off_t diff;
933
934 /* Be careful here. The current position may actually be
935 beyond the end of the file. */
936 bytes_remaining = (diff =
937 end_pos - current_pos) < 0 ? 0 : diff;
938 } else {
939 detailed_error(0, errno, "%s", filename);
940 return 1;
941 }
942
943 if (bytes_remaining <= n_bytes) {
944 /* From the current position to end of file, there are no
945 more bytes than have been requested. So reposition the
946 file pointer to the incoming current position and print
947 everything after that. */
948 lseek(fd, current_pos, SEEK_SET);
949 } else {
950 /* There are more bytes remaining than were requested.
951 Back up. */
952 lseek(fd, -n_bytes, SEEK_END);
953 }
954 dump_remainder(filename, fd);
955 } else
956 return pipe_bytes(filename, fd, n_bytes);
957 } 176 }
177 if (buffer)
178 free(buffer);
958 return 0; 179 return 0;
959} 180}
960 181
961/* Output the last N_LINES lines of file FILENAME open for reading in FD. 182void add_file(char *name)
962 Return 0 if successful, 1 if an error occurred. */
963
964static int tail_lines(const char *filename, int fd, long int n_lines)
965{ 183{
966 struct stat stats; 184 ++n_files;
967 off_t length; 185 files = realloc(files, n_files);
968 186 files[n_files - 1] = (char *) malloc(strlen(name) + 1);
969 if (fstat(fd, &stats)) { 187 strcpy(files[n_files - 1], name);
970 detailed_error(0, errno, "%s", filename);
971 return 1;
972 }
973
974 if (from_start) {
975 if (start_lines(filename, fd, n_lines))
976 return 1;
977 dump_remainder(filename, fd);
978 } else {
979 /* Use file_lines only if FD refers to a regular file with
980 its file pointer positioned at beginning of file. */
981 /* FIXME: adding the lseek conjunct is a kludge.
982 Once there's a reasonable test suite, fix the true culprit:
983 file_lines. file_lines shouldn't presume that the input
984 file pointer is initially positioned to beginning of file. */
985 if (S_ISREG(stats.st_mode)
986 && lseek(fd, (off_t) 0, SEEK_CUR) == (off_t) 0) {
987 length = lseek(fd, (off_t) 0, SEEK_END);
988 if (length != 0 && file_lines(filename, fd, n_lines, length))
989 return 1;
990 dump_remainder(filename, fd);
991 } else
992 return pipe_lines(filename, fd, n_lines);
993 }
994 return 0;
995} 188}
996 189
997/* Display the last N_UNITS units of file FILENAME, open for reading
998 in FD.
999 Return 0 if successful, 1 if an error occurred. */
1000 190
1001static int tail(const char *filename, int fd, off_t n_units) 191int tail_main(int argc, char **argv)
1002{ 192{
1003 if (count_lines) 193 int show_headers = 1;
1004 return tail_lines(filename, fd, (long) n_units); 194 int test;
1005 else 195 int c;
1006 return tail_bytes(filename, fd, n_units); 196 int nargs=0;
1007} 197 char **argn=NULL;
1008 198
1009/* Display the last N_UNITS units of file FILENAME. 199 opterr = 0;
1010 "-" for FILENAME means the standard input. 200
1011 FILENUM is this file's index in the list of files the user gave. 201 for(c=0;c<argc;c++){
1012 Return 0 if successful, 1 if an error occurred. */ 202 test=atoi(argv[c]);
1013 203 if(test){
1014static int tail_file(const char *filename, off_t n_units, int filenum) 204 units=test;
1015{ 205 if(units<0)
1016 int fd, errors; 206 units=units-1;
1017 struct stat stats; 207 }else{
1018 208 nargs++;
1019 if (!strcmp(filename, "-")) { 209 argn = realloc(argn, nargs);
1020 have_read_stdin = 1; 210 argn[nargs - 1] = (char *) malloc(strlen(argv[c]) + 1);
1021 filename = "standard input"; 211 strcpy(argn[nargs - 1], argv[c]);
1022 if (print_headers)
1023 write_header(filename, NULL);
1024 errors = tail(filename, 0, n_units);
1025 if (forever_multiple) {
1026 if (fstat(0, &stats) < 0) {
1027 detailed_error(0, errno, "standard input");
1028 errors = 1;
1029 } else if (!S_ISREG(stats.st_mode)) {
1030 detailed_error(0, 0,
1031 "standard input: cannot follow end of non-regular file");
1032 errors = 1;
1033 }
1034 if (errors)
1035 file_descs[filenum] = -1;
1036 else {
1037 file_descs[filenum] = 0;
1038 file_sizes[filenum] = stats.st_size;
1039 }
1040 }
1041 } else {
1042 /* Not standard input. */
1043 fd = open(filename, O_RDONLY);
1044 if (fd == -1) {
1045 if (forever_multiple)
1046 file_descs[filenum] = -1;
1047 detailed_error(0, errno, "%s", filename);
1048 errors = 1;
1049 } else {
1050 if (print_headers)
1051 write_header(filename, NULL);
1052 errors = tail(filename, fd, n_units);
1053 if (forever_multiple) {
1054 if (fstat(fd, &stats) < 0) {
1055 detailed_error(0, errno, "%s", filename);
1056 errors = 1;
1057 } else if (!S_ISREG(stats.st_mode)) {
1058 detailed_error(0, 0,
1059 "%s: cannot follow end of non-regular file",
1060 filename);
1061 errors = 1;
1062 }
1063 if (errors) {
1064 close(fd);
1065 file_descs[filenum] = -1;
1066 } else {
1067 file_descs[filenum] = fd;
1068 file_sizes[filenum] = stats.st_size;
1069 }
1070 } else {
1071 if (close(fd)) {
1072 detailed_error(0, errno, "%s", filename);
1073 errors = 1;
1074 }
1075 }
1076 } 212 }
1077 } 213 }
214 while (1) {
215 int opt_index = 0;
1078 216
1079 return errors; 217 c = getopt_long_only(nargs, argn,
1080} 218 "c:fhn:s:qv", NULL, &opt_index);
1081 219 if (c == -1)
1082extern int tail_main(int argc, char **argv) 220 break;
1083{ 221 switch (c) {
1084 int stopit = 0;
1085 enum header_mode header_mode = multiple_files;
1086 int exit_status = 0;
1087
1088 /* If from_start, the number of items to skip before printing; otherwise,
1089 the number of items at the end of the file to print. Initially, -1
1090 means the value has not been set. */
1091 off_t n_units = -1;
1092 int n_files;
1093 char **file;
1094
1095 program_name = argv[0];
1096 have_read_stdin = 0;
1097 count_lines = 1;
1098 forever = forever_multiple = from_start = print_headers = 0;
1099
1100 /* Parse any options */
1101 //fprintf(stderr, "argc=%d, argv=%s\n", argc, *argv);
1102 while (--argc > 0 && (**(++argv) == '-' || **argv == '+')) {
1103 if (**argv == '+') {
1104 from_start = 1;
1105 }
1106 stopit = 0;
1107 while (stopit == 0 && *(++(*argv))) {
1108 switch (**argv) {
1109 case 'c':
1110 count_lines = 0;
1111
1112 if (--argc < 1) {
1113 usage(tail_usage);
1114 }
1115 n_units = getNum(*(++argv));
1116 stopit = 1;
1117 break;
1118
1119 case 'f':
1120 forever = 1;
1121 break;
1122 222
1123 case 'n': 223#ifndef BB_FEATURE_SIMPLE_TAIL
1124 count_lines = 1;
1125 224
1126 if (--argc < 1) { 225 case 'c':
226 unit_type = BYTES;
227 test = atoi(optarg);
228 if(test==0)
229 usage(tail_usage);
230 if(optarg[strlen(optarg)-1]>'9') {
231 switch (optarg[strlen(optarg)-1]) {
232 case 'b':
233 test *= 512;
234 break;
235 case 'k':
236 test *= 1024;
237 break;
238 case 'm':
239 test *= (1024 * 1024);
240 break;
241 default:
242 fprintf(stderr,"Size must be b,k, or m.");
1127 usage(tail_usage); 243 usage(tail_usage);
1128 } 244 }
1129 n_units = atol(*(++argv));
1130 stopit = 1;
1131 break;
1132
1133 case 'q':
1134 header_mode = never;
1135 break;
1136
1137 case 'v':
1138 header_mode = always;
1139 break;
1140
1141 default:
1142 usage(tail_usage);
1143 } 245 }
246 if(optarg[0]=='+')
247 units=test+1;
248 else
249 units=-(test+1);
250 break;
251 case 'q':
252 show_headers = 0;
253 break;
254 case 's':
255 sleep_int = atoi(optarg);
256 if(sleep_int<1)
257 sleep_int=1;
258 break;
259 case 'v':
260 verbose = 1;
261 break;
262#endif
263 case 'f':
264 follow = 1;
265 break;
266 case 'h':
267 usage(tail_usage);
268 break;
269 case 'n':
270 test = atoi(optarg);
271 if (test) {
272 if (optarg[0] == '+')
273 units = test;
274 else
275 units = -(test+1);
276 } else
277 usage(tail_usage);
278 break;
279 default:
280 errorMsg("\nUnknown arg: %c.\n\n",c);
281 usage(tail_usage);
1144 } 282 }
1145 } 283 }
1146 284 while (optind < nargs) {
1147 285 if (!strcmp(argn[optind], "-"))
1148 if (n_units == -1) 286 add_file(STDIN);
1149 n_units = DEFAULT_N_LINES; 287 else
1150 288 add_file(argn[optind]);
1151 /* To start printing with item N_UNITS from the start of the file, skip 289 optind++;
1152 N_UNITS - 1 items. `tail +0' is actually meaningless, but for Unix 290 }
1153 compatibility it's treated the same as `tail +1'. */ 291 if(units==0)
1154 if (from_start) { 292 units=-11;
1155 if (n_units) 293 if(units>0)
1156 --n_units; 294 units--;
1157 } 295 if (n_files == 0)
1158 296 add_file(STDIN);
1159 n_files = argc; 297 if (n_files == 1)
1160 file = argv; 298#ifndef BB_FEATURE_SIMPLE_TAIL
1161 299 if (!verbose)
1162 if (n_files > 1 && forever) { 300#endif
1163 forever_multiple = 1; 301 show_headers = 0;
1164 forever = 0; 302 for (test = 0; test < n_files; test++) {
1165 file_descs = (int *) xmalloc(n_files * sizeof(int)); 303 if (show_headers)
1166 304 printf("==> %s <==\n", files[test]);
1167 file_sizes = (off_t *) xmalloc(n_files * sizeof(off_t)); 305 tail_stream(test);
1168 } 306 }
1169 307 if(files)
1170 if (header_mode == always 308 free(files);
1171 || (header_mode == multiple_files && n_files > 1)) 309 if(argn)
1172 print_headers = 1; 310 free(argn);
1173 311 return 0;
1174 if (n_files == 0) {
1175 exit_status |= tail_file("-", n_units, 0);
1176 } else {
1177 int i;
1178
1179 for (i = 0; i < n_files; i++)
1180 exit_status |= tail_file(file[i], n_units, i);
1181
1182 if (forever_multiple)
1183 tail_forever(file, n_files);
1184 }
1185
1186 if (have_read_stdin && close(0) < 0)
1187 detailed_error(EXIT_FAILURE, errno, "-");
1188 if (fclose(stdout) == EOF)
1189 detailed_error(EXIT_FAILURE, errno, "write error");
1190 exit(exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
1191} 312}
1192 313
1193 314/*
1194#endif 315Local Variables:
316c-file-style: "linux"
317c-basic-offset: 4
318tab-width: 4
319End:
320*/