Rewrote tail.
[platform/upstream/busybox.git] / coreutils / tail.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini tail implementation for busybox
4  *
5  *
6  * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #include "busybox.h"
25
26 #include <sys/types.h>
27 #include <fcntl.h>
28
29 static struct suffix_mult tail_suffixes[] = {
30         { "b", 512 },
31         { "k", 1024 },
32         { "m", 1048576 },
33         { NULL, 0 }
34 };
35
36 #ifndef BB_FEATURE_SIMPLE_TAIL
37 static struct suffix_mult null_suffixes[] = {
38         { NULL, 0 }
39 };
40 #endif
41
42 #define BYTES 0
43 #define LINES 1
44
45 static char *tailbuf;
46 static int taillen;
47 static int newline;
48
49 void tailbuf_append(char *buf, int len)
50 {
51         tailbuf = xrealloc(tailbuf, taillen + len);
52         memcpy(tailbuf + taillen, buf, len);
53         taillen += len;
54 }
55
56 void tailbuf_trunc()
57 {
58         char *s;
59         s = memchr(tailbuf, '\n', taillen);
60         memmove(tailbuf, s + 1, taillen - ((s + 1) - tailbuf));
61         taillen -= (s + 1) - tailbuf;
62         newline = 0;
63 }
64
65 int tail_main(int argc, char **argv)
66 {
67         int from_top = 0, units = LINES, count = 10, sleep_period = 1;
68         int show_headers = 0, hide_headers = 0, follow = 0;
69         int *fds, nfiles = 0, status = EXIT_SUCCESS, nread, nwrite, seen = 0;
70         char *s, *start, *end, buf[BUFSIZ];
71         int i, opt;
72
73         while ((opt = getopt(argc, argv, "c:fhn:q:s:v")) > 0) {
74                 switch (opt) {
75                         case 'f':
76                                 follow = 1;
77                                 break;
78 #ifndef BB_FEATURE_SIMPLE_TAIL
79                         case 'c':
80                                 units = BYTES;
81                                 /* FALLS THROUGH */
82 #endif
83                         case 'n':
84                                 count = parse_number(optarg, tail_suffixes);
85                                 if (count < 0)
86                                         count = -count;
87                                 if (optarg[0] == '+')
88                                         from_top = 1;
89                                 break;
90 #ifndef BB_FEATURE_SIMPLE_TAIL
91                         case 'q':
92                                 hide_headers = 1;
93                                 break;
94                         case 's':
95                                 sleep_period = parse_number(optarg, null_suffixes);
96                                 break;
97                         case 'v':
98                                 show_headers = 1;
99                                 break;
100 #endif
101                         default:
102                                 usage(tail_usage);
103                 }
104         }
105
106         /* open all the files */
107         fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1));
108         if (argc == optind) {
109                 fds[nfiles++] = STDIN_FILENO;
110                 argv[optind] = "standard input";
111         } else {
112                 for (i = optind; i < argc; i++) {
113                         if (strcmp(argv[i], "-") == 0) {
114                                 fds[nfiles++] = STDIN_FILENO;
115                                 argv[i] = "standard input";
116                         } else if ((fds[nfiles++] = open(argv[i], O_RDONLY)) < 0) {
117                                 perror_msg("%s", argv[i]);
118                                 status = EXIT_FAILURE;
119                         }
120                 }
121         }
122         
123 #ifndef BB_FEATURE_SIMPLE_TAIL
124         /* tail the files */
125         if (!from_top && units == BYTES)
126                 tailbuf = xmalloc(count);
127 #endif
128
129         for (i = 0; i < nfiles; i++) {
130                 if (fds[i] == -1)
131                         continue;
132                 seen = 0;
133                 if (show_headers || (!hide_headers && nfiles > 1))
134                         printf("%s==> %s <==\n", i == 0 ? "" : "\n", argv[optind + i]);
135                 while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) {
136                         if (from_top) {
137 #ifndef BB_FEATURE_SIMPLE_TAIL
138                                 if (units == BYTES) {
139                                         if (count - 1 <= seen)
140                                                 nwrite = nread;
141                                         else if (count - 1 <= seen + nread)
142                                                 nwrite = nread + seen - (count - 1);
143                                         else
144                                                 nwrite = 0;
145                                         seen += nread;
146                                 } else {
147 #else
148                                 {
149 #endif
150                                         if (count - 1 <= seen)
151                                                 nwrite = nread;
152                                         else {
153                                                 nwrite = 0;
154                                                 for (s = memchr(buf, '\n', nread); s != NULL;
155                                                                 s = memchr(s+1, '\n', nread - (s + 1 - buf))) {
156                                                         if (count - 1 <= ++seen) {
157                                                                 nwrite = nread - (s + 1 - buf);
158                                                                 break;
159                                                         }
160                                                 }
161                                         }
162                                 }
163                                 if (full_write(STDOUT_FILENO, buf + nread - nwrite,
164                                                         nwrite) < 0) {
165                                         perror_msg("write");
166                                         status = EXIT_FAILURE;
167                                         break;
168                                 }
169                         } else {
170 #ifndef BB_FEATURE_SIMPLE_TAIL
171                                 if (units == BYTES) {
172                                         if (nread < count) {
173                                                 memmove(tailbuf, tailbuf + nread, count - nread);
174                                                 memcpy(tailbuf + count - nread, buf, nread);
175                                         } else {
176                                                 memcpy(tailbuf, buf + nread - count, count);
177                                         }
178                                         seen += nread;
179                                 } else {
180 #else
181                                 {
182 #endif
183                                         for (start = buf, end = memchr(buf, '\n', nread);
184                                                         end != NULL; start = end+1,
185                                                         end = memchr(start, '\n', nread - (start - buf))) {
186                                                 if (newline && count <= seen)
187                                                         tailbuf_trunc();
188                                                 tailbuf_append(start, end - start + 1);
189                                                 seen++;
190                                                 newline = 1;
191                                         }
192                                         if (newline && count <= seen && nread - (start - buf) > 0)
193                                                 tailbuf_trunc();
194                                         tailbuf_append(start, nread - (start - buf));
195                                 }
196                         }
197                 }
198
199                 if (nread < 0) {
200                         perror_msg("read");
201                         status = EXIT_FAILURE;
202                 }
203
204 #ifndef BB_FEATURE_SIMPLE_TAIL
205                 if (!from_top && units == BYTES) {
206                         if (count < seen)
207                                 seen = count;
208                         if (full_write(STDOUT_FILENO, tailbuf + count - seen, seen) < 0) {
209                                 perror_msg("write");
210                                 status = EXIT_FAILURE;
211                         }
212                 }
213 #endif
214
215                 if (!from_top && units == LINES) {
216                         if (full_write(STDOUT_FILENO, tailbuf, taillen) < 0) {
217                                 perror_msg("write");
218                                 status = EXIT_FAILURE;
219                         }
220                 }
221
222                 taillen = 0;
223         }
224
225         while (follow) {
226                 sleep(sleep_period);
227
228                 for (i = 0; i < nfiles; i++) {
229                         if (fds[i] == -1)
230                                 continue;
231
232                         if ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0) {
233                                 if (show_headers || (!hide_headers && nfiles > 1))
234                                         printf("\n==> %s <==\n", argv[optind + i]);
235
236                                 do {
237                                         full_write(STDOUT_FILENO, buf, nread);
238                                 } while ((nread = safe_read(fds[i], buf, sizeof(buf))) > 0);
239                         }
240
241                         if (nread < 0) {
242                                 perror_msg("read");
243                                 status = EXIT_FAILURE;
244                         }
245                 }
246         }
247
248         return status;
249 }