aboutsummaryrefslogtreecommitdiff
path: root/coreutils/tail.c
diff options
context:
space:
mode:
Diffstat (limited to 'coreutils/tail.c')
-rw-r--r--coreutils/tail.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/coreutils/tail.c b/coreutils/tail.c
new file mode 100644
index 000000000..ed5ea1467
--- /dev/null
+++ b/coreutils/tail.c
@@ -0,0 +1,326 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini tail implementation for busybox
4 *
5 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9
10/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
11/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
12/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
13
14/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
15 *
16 * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
17 * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
18 * 1) mixing printf/write without fflush()ing stdout
19 * 2) no check that any open files are present
20 * 3) optstring had -q taking an arg
21 * 4) no error checking on write in some cases, and a warning even then
22 * 5) q and s interaction bug
23 * 6) no check for lseek error
24 * 7) lseek attempted when count==0 even if arg was +0 (from top)
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <ctype.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <sys/stat.h>
34#include "busybox.h"
35
36static const struct suffix_mult tail_suffixes[] = {
37 { "b", 512 },
38 { "k", 1024 },
39 { "m", 1048576 },
40 { NULL, 0 }
41};
42
43static int status;
44
45static void tail_xbb_full_write(const char *buf, size_t len)
46{
47 /* If we get a write error, there is really no sense in continuing. */
48 if (full_write(STDOUT_FILENO, buf, len) < 0)
49 bb_perror_nomsg_and_die();
50}
51
52static void tail_xprint_header(const char *fmt, const char *filename)
53{
54#if defined __GLIBC__
55 if (dprintf(STDOUT_FILENO, fmt, filename) < 0) {
56 bb_perror_nomsg_and_die();
57 }
58#else
59 int hdr_len = strlen(fmt) + strlen(filename);
60 char *hdr = xzalloc(hdr_len);
61 sprintf(hdr, filename, filename);
62 tail_xbb_full_write(hdr, hdr_len);
63#endif
64}
65
66static ssize_t tail_read(int fd, char *buf, size_t count)
67{
68 ssize_t r;
69 off_t current,end;
70 struct stat sbuf;
71
72 end = current = lseek(fd, 0, SEEK_CUR);
73 if (!fstat(fd, &sbuf))
74 end = sbuf.st_size;
75 lseek(fd, end < current ? 0 : current, SEEK_SET);
76 if ((r = safe_read(fd, buf, count)) < 0) {
77 bb_perror_msg(bb_msg_read_error);
78 status = EXIT_FAILURE;
79 }
80
81 return r;
82}
83
84static const char tail_opts[] =
85 "fn:c:"
86#if ENABLE_FEATURE_FANCY_TAIL
87 "qs:v"
88#endif
89 ;
90
91static const char header_fmt[] = "\n==> %s <==\n";
92
93int tail_main(int argc, char **argv)
94{
95 long count = 10;
96 unsigned sleep_period = 1;
97 int from_top = 0;
98 int follow = 0;
99 int header_threshhold = 1;
100 int count_bytes = 0;
101
102 char *tailbuf;
103 size_t tailbufsize;
104 int taillen = 0;
105 int newline = 0;
106
107 int *fds, nfiles, nread, nwrite, seen, i, opt;
108 char *s, *buf;
109 const char *fmt;
110
111#if !ENABLE_DEBUG_YANK_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
112 /* Allow legacy syntax of an initial numeric option without -n. */
113 if (argc >= 2 && (argv[1][0] == '+' || argv[1][0] == '-')
114 && isdigit(argv[1][1])
115 ) {
116 optind = 2;
117 optarg = argv[1];
118 goto GET_COUNT;
119 }
120#endif
121
122 while ((opt = getopt(argc, argv, tail_opts)) > 0) {
123 switch (opt) {
124 case 'f':
125 follow = 1;
126 break;
127 case 'c':
128 count_bytes = 1;
129 /* FALLS THROUGH */
130 case 'n':
131#if !ENABLE_DEBUG_YANK_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
132 GET_COUNT:
133#endif
134 count = xatol_sfx(optarg, tail_suffixes);
135 /* Note: Leading whitespace is an error trapped above. */
136 if (*optarg == '+') {
137 from_top = 1;
138 } else {
139 from_top = 0;
140 }
141 if (count < 0) {
142 count = -count;
143 }
144 break;
145#if ENABLE_FEATURE_FANCY_TAIL
146 case 'q':
147 header_threshhold = INT_MAX;
148 break;
149 case 's':
150 sleep_period = xatou(optarg);
151 break;
152 case 'v':
153 header_threshhold = 0;
154 break;
155#endif
156 default:
157 bb_show_usage();
158 }
159 }
160
161 /* open all the files */
162 fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1));
163
164 argv += optind;
165 nfiles = i = 0;
166
167 if ((argc -= optind) == 0) {
168 struct stat statbuf;
169
170 if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) {
171 follow = 0;
172 }
173 /* --argv; */
174 *argv = (char *) bb_msg_standard_input;
175 goto DO_STDIN;
176 }
177
178 do {
179 if ((argv[i][0] == '-') && !argv[i][1]) {
180 DO_STDIN:
181 fds[nfiles] = STDIN_FILENO;
182 } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) {
183 bb_perror_msg("%s", argv[i]);
184 status = EXIT_FAILURE;
185 continue;
186 }
187 argv[nfiles] = argv[i];
188 ++nfiles;
189 } while (++i < argc);
190
191 if (!nfiles) {
192 bb_error_msg_and_die("no files");
193 }
194
195 tailbufsize = BUFSIZ;
196
197 /* tail the files */
198 if (from_top < count_bytes) { /* Each is 0 or 1, so true iff 0 < 1. */
199 /* Hence, !from_top && count_bytes */
200 if (tailbufsize < count) {
201 tailbufsize = count + BUFSIZ;
202 }
203 }
204
205 buf = tailbuf = xmalloc(tailbufsize);
206
207 fmt = header_fmt + 1; /* Skip header leading newline on first output. */
208 i = 0;
209 do {
210 /* Be careful. It would be possible to optimize the count-bytes
211 * case if the file is seekable. If you do though, remember that
212 * starting file position may not be the beginning of the file.
213 * Beware of backing up too far. See example in wc.c.
214 */
215 if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) {
216 continue;
217 }
218
219 if (nfiles > header_threshhold) {
220 tail_xprint_header(fmt, argv[i]);
221 fmt = header_fmt;
222 }
223
224 buf = tailbuf;
225 taillen = 0;
226 seen = 1;
227 newline = 0;
228
229 while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) {
230 if (from_top) {
231 nwrite = nread;
232 if (seen < count) {
233 if (count_bytes) {
234 nwrite -= (count - seen);
235 seen = count;
236 } else {
237 s = buf;
238 do {
239 --nwrite;
240 if ((*s++ == '\n') && (++seen == count)) {
241 break;
242 }
243 } while (nwrite);
244 }
245 }
246 tail_xbb_full_write(buf + nread - nwrite, nwrite);
247 } else if (count) {
248 if (count_bytes) {
249 taillen += nread;
250 if (taillen > count) {
251 memmove(tailbuf, tailbuf + taillen - count, count);
252 taillen = count;
253 }
254 } else {
255 int k = nread;
256 int nbuf = 0;
257
258 while (k) {
259 --k;
260 if (buf[k] == '\n') {
261 ++nbuf;
262 }
263 }
264
265 if (newline + nbuf < count) {
266 newline += nbuf;
267 taillen += nread;
268
269 } else {
270 int extra = 0;
271 if (buf[nread-1] != '\n') {
272 extra = 1;
273 }
274
275 k = newline + nbuf + extra - count;
276 s = tailbuf;
277 while (k) {
278 if (*s == '\n') {
279 --k;
280 }
281 ++s;
282 }
283
284 taillen += nread - (s - tailbuf);
285 memmove(tailbuf, s, taillen);
286 newline = count - extra;
287 }
288 if (tailbufsize < taillen + BUFSIZ) {
289 tailbufsize = taillen + BUFSIZ;
290 tailbuf = xrealloc(tailbuf, tailbufsize);
291 }
292 }
293 buf = tailbuf + taillen;
294 }
295 }
296
297 if (!from_top) {
298 tail_xbb_full_write(tailbuf, taillen);
299 }
300
301 taillen = 0;
302 } while (++i < nfiles);
303
304 buf = xrealloc(tailbuf, BUFSIZ);
305
306 fmt = NULL;
307
308 while (follow) {
309 sleep(sleep_period);
310 i = 0;
311 do {
312 if (nfiles > header_threshhold) {
313 fmt = header_fmt;
314 }
315 while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) {
316 if (fmt) {
317 tail_xprint_header(fmt, argv[i]);
318 fmt = NULL;
319 }
320 tail_xbb_full_write(buf, nread);
321 }
322 } while (++i < nfiles);
323 }
324
325 return status;
326}