627373bfd5112f9f4ca04a76faf18410f9bce59b
[oweals/busybox.git] / tail.c
1 /* vi: set sw=4 ts=4: */
2 /* tail -- output the last part of file(s)
3    Copyright (C) 89, 90, 91, 95, 1996 Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19    Original version by Paul Rubin <phr@ocf.berkeley.edu>.
20    Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
21    tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.  
22
23    Rewrote the option parser, removed locales support,
24    and generally busyboxed, Erik Andersen <andersen@lineo.com>
25
26    Removed superfluous options and associated code ("-c", "-n", "-q").
27    Removed "tail -f" support for multiple files.
28    Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>.
29
30    Compleate Rewrite to correctly support "-NUM", "+NUM", and "-s" by
31    E.Allen Soard (esp@espsw.net).
32
33  */
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <getopt.h>
42 #include "internal.h"
43
44 #define STDIN "standard input"
45 #define LINES 0
46 #define BYTES 1
47
48 static int n_files = 0;
49 static char **files = NULL;
50 static char * buffer;
51 static ssize_t bytes_read=0;
52 static ssize_t bs;
53 static ssize_t filelocation=0;
54 static char pip;
55
56 #ifdef BB_FEATURE_SIMPLE_TAIL
57 static const char unit_type=LINES;
58 #else
59 static char unit_type=LINES;
60 static char verbose = 0;
61 #endif
62
63 static off_t units=0;
64
65 int tail_stream(int fd)
66 {
67         ssize_t startpoint;
68         ssize_t endpoint=0;
69         ssize_t count=0;
70         ssize_t filesize=0;
71         char direction=1;
72
73         filelocation=0;
74         startpoint=bs=BUFSIZ;
75
76         filesize=lseek(fd, -1, SEEK_END)+1;
77         pip=(filesize<=0);
78
79         if(units>=0)
80                 lseek(fd,0,SEEK_SET);
81         else {
82                 direction=-1;
83                 count=1;
84         }
85         while(units != 0) {
86                 if (pip) {
87                         char * line;
88                         ssize_t f_size=0;
89
90                         bs=BUFSIZ;
91                         line=malloc(bs);
92                         while(1) {
93                                 bytes_read=read(fd,line,bs);
94                                 if(bytes_read<=0)
95                                         break;
96                                 buffer=realloc(buffer,f_size+bytes_read);
97                                 memcpy(&buffer[f_size],line,bytes_read);
98                                 filelocation=f_size+=bytes_read;
99                         }
100                         bs=f_size;
101                         if(direction<0)
102                                 bs--;
103                         if (line)
104                                 free(line);
105                 } else {
106                         filelocation = lseek(fd, 0, SEEK_CUR);
107                         if(direction<0) {
108                                 if(filelocation<bs)
109                                         bs=filelocation;
110                                 filelocation = lseek(fd, -bs, SEEK_CUR);
111                         }
112                         bytes_read = read(fd, buffer, bs);
113                         if (bytes_read <= 0)
114                                 break;
115                         bs=bytes_read;
116                 }
117                 startpoint=bs;
118                 if(direction>0) {
119                         endpoint=startpoint;
120                         startpoint=0;
121                 }
122                 for(;startpoint!=endpoint;startpoint+=direction) {
123 #ifndef BB_FEATURE_SIMPLE_TAIL
124                         if(unit_type==BYTES)
125                                 count++;
126                         else
127 #endif
128                                 if(buffer[startpoint-1]=='\n')
129                                         count++;
130                         if (!pip)
131                                 filelocation=lseek(fd,0,SEEK_CUR);
132                         if(count==units*direction)
133                                 break;
134                 }
135                 if((count==units*direction) | pip)
136                         break;
137                 if(direction<0){
138                         filelocation = lseek(fd, -bytes_read, SEEK_CUR);
139                         if(filelocation==0)
140                                 break;
141                 }
142         }
143         if(pip && (direction<0))
144                 bs++;
145         bytes_read=bs-startpoint;
146         memcpy(&buffer[0],&buffer[startpoint],bytes_read);
147
148         return 0;
149 }
150
151 void add_file(char *name)
152 {
153         ++n_files;
154         files = realloc(files, n_files);
155         files[n_files - 1] = (char *) malloc(strlen(name) + 1);
156         strcpy(files[n_files - 1], name);
157 }
158
159 void checknumbers(const char* name)
160 {
161         int test=atoi(name);
162         if(test){
163                 units=test;
164                 if(units<0)
165                         units=units-1;
166         } else {
167                 fatalError("Unrecognised number '%s'\n", name);
168         }
169 }
170
171 int tail_main(int argc, char **argv)
172 {
173         int show_headers = 1;
174         int test;
175         int opt;
176         char follow=0;
177         int sleep_int=1;
178         int *fd;
179
180         opterr = 0;
181         
182         while ((opt=getopt(argc,argv,"c:fhn:s:q:v123456789+")) >0) {
183
184                 switch (opt) {
185                         case '1':case '2':case '3':case '4':case '5':
186                         case '6':case '7':case '8':case '9':case '0':
187                                 checknumbers(argv[optind-1]);
188                                 break;
189
190 #ifndef BB_FEATURE_SIMPLE_TAIL
191                 case 'c':
192                         unit_type = BYTES;
193                         test = atoi(optarg);
194                         if(test==0)
195                                 usage(tail_usage);
196                         if(optarg[strlen(optarg)-1]>'9') {
197                                 switch (optarg[strlen(optarg)-1]) {
198                                 case 'b':
199                                         test *= 512;
200                                         break;
201                                 case 'k':
202                                         test *= 1024;
203                                         break;
204                                 case 'm':
205                                         test *= (1024 * 1024);
206                                         break;
207                                 default:
208                                         fprintf(stderr,"Size must be b,k, or m.");
209                                         usage(tail_usage);
210                                 }
211                         }
212                         if(optarg[0]=='+')
213                                 units=test+1;
214                         else
215                                 units=-(test+1);
216                         break;
217                 case 'q':
218                         show_headers = 0;
219                         break;
220                 case 's':
221                         sleep_int = atoi(optarg);
222                         if(sleep_int<1)
223                                 sleep_int=1;
224                         break;
225                 case 'v':
226                         verbose = 1;
227                         break;
228 #endif
229                 case 'f':
230                         follow = 1;
231                         break;
232                 case 'h':
233                         usage(tail_usage);
234                         break;
235                 case 'n':
236                         test = atoi(optarg);
237                         if (test) {
238                                 if (optarg[0] == '+')
239                                         units = test;
240                                 else
241                                         units = -(test+1);
242                         } else
243                                 usage(tail_usage);
244                         break;
245                 default:
246                         errorMsg("\nUnknown arg: %c.\n\n",optopt);
247                         usage(tail_usage);
248                 }
249         }
250         while (optind <= argc) {
251                 if(optind==argc) {
252                         if (n_files==0)
253                                 add_file(STDIN);
254                         else
255                                 break;
256                 }else {
257                         if (*argv[optind] == '+') {
258                                 checknumbers(argv[optind]);
259                         }
260                         else if (!strcmp(argv[optind], "-")) {
261                                 add_file(STDIN);
262                         } else {
263                                 add_file(argv[optind]);
264                         }
265                         optind++;
266                 }
267         }
268         if(units==0)
269                 units=-11;
270         if(units>0)
271                 units--;
272         fd=malloc(sizeof(int)*n_files);
273         if (n_files == 1)
274 #ifndef BB_FEATURE_SIMPLE_TAIL
275                 if (!verbose)
276 #endif
277                         show_headers = 0;
278         buffer=malloc(BUFSIZ);
279         for (test = 0; test < n_files; test++) {
280                 if (show_headers)
281                         printf("==> %s <==\n", files[test]);
282                 if (!strcmp(files[test], STDIN))
283                         fd[test] = 0;
284                 else
285                         fd[test] = open(files[test], O_RDONLY);
286                 if (fd[test] == -1)
287                         fatalError("Unable to open file %s.\n", files[test]);
288                 tail_stream(fd[test]);
289
290                 bs=BUFSIZ;
291                 while (1) {
292                         if((filelocation>0 || pip)){
293                                 write(1,buffer,bytes_read);
294                         }
295                         bytes_read = read(fd[test], buffer, bs);
296                         filelocation+=bytes_read;
297                         if (bytes_read <= 0) {
298                                 break;
299                         }
300                         usleep(sleep_int * 1000);
301                 }
302                 if(n_files>1)
303                         printf("\n");
304         }
305         while(1){
306                 for (test = 0; test < n_files; test++) {
307                         if(!follow){
308                                 close(fd[test]);
309                                 continue;
310                         } else {
311                                 sleep(sleep_int);
312                                 bytes_read = read(fd[test], buffer, bs);
313                                 if(bytes_read>0) {
314                                         if (show_headers)
315                                                 printf("==> %s <==\n", files[test]);
316                                         write(1,buffer,bytes_read);
317                                         if(n_files>1)
318                                                 printf("\n");
319                                 }
320                         }
321                 }
322                 if(!follow)
323                         break;
324         }
325         if (fd)
326                 free(fd);
327         if (buffer)
328                 free(buffer);
329         if(files)
330                 free(files);
331         return 0;
332 }
333
334 /*
335 Local Variables:
336 c-file-style: "linux"
337 c-basic-offset: 4
338 tab-width: 4
339 End:
340 */