Fix calls to {m,c,re}alloc so that they use x{m,c,re}alloc instead of
[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 <fcntl.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <getopt.h>
41 #include "internal.h"
42
43 #define STDIN "standard input"
44 #define LINES 0
45 #define BYTES 1
46
47 static int n_files = 0;
48 static char **files = NULL;
49 static char * buffer;
50 static ssize_t bytes_read=0;
51 static ssize_t bs;
52 static ssize_t filelocation=0;
53 static char pip;
54
55 #ifdef BB_FEATURE_SIMPLE_TAIL
56 static const char unit_type=LINES;
57 #else
58 static char unit_type=LINES;
59 static char verbose = 0;
60 #endif
61
62 static off_t units=0;
63
64 int tail_stream(int fd)
65 {
66         ssize_t startpoint;
67         ssize_t endpoint=0;
68         ssize_t count=0;
69         ssize_t filesize=0;
70         int direction=1;
71
72         filelocation=0;
73         startpoint=bs=BUFSIZ;
74
75         filesize=lseek(fd, -1, SEEK_END)+1;
76         pip=(filesize<=0);
77
78         if(units>=0)
79                 lseek(fd,0,SEEK_SET);
80         else {
81                 direction=-1;
82                 count=1;
83         }
84         while(units != 0) {
85                 if (pip) {
86                         char * line;
87                         ssize_t f_size=0;
88
89                         bs=BUFSIZ;
90                         line=xmalloc(bs);
91                         while(1) {
92                                 bytes_read=read(fd,line,bs);
93                                 if(bytes_read<=0)
94                                         break;
95                                 buffer=xrealloc(buffer,f_size+bytes_read);
96                                 memcpy(&buffer[f_size],line,bytes_read);
97                                 filelocation=f_size+=bytes_read;
98                         }
99                         bs=f_size;
100                         if(direction<0)
101                                 bs--;
102                         if (line)
103                                 free(line);
104                 } else {
105                         filelocation = lseek(fd, 0, SEEK_CUR);
106                         if(direction<0) {
107                                 if(filelocation<bs)
108                                         bs=filelocation;
109                                 filelocation = lseek(fd, -bs, SEEK_CUR);
110                         }
111                         bytes_read = read(fd, buffer, bs);
112                         if (bytes_read <= 0)
113                                 break;
114                         bs=bytes_read;
115                 }
116                 startpoint=bs;
117                 if(direction>0) {
118                         endpoint=startpoint;
119                         startpoint=0;
120                 }
121                 for(;startpoint!=endpoint;startpoint+=direction) {
122 #ifndef BB_FEATURE_SIMPLE_TAIL
123                         if(unit_type==BYTES)
124                                 count++;
125                         else
126 #endif
127                                 if(buffer[startpoint-1]=='\n')
128                                         count++;
129                         if (!pip)
130                                 filelocation=lseek(fd,0,SEEK_CUR);
131                         if(count==units*direction)
132                                 break;
133                 }
134                 if((count==units*direction) | pip)
135                         break;
136                 if(direction<0){
137                         filelocation = lseek(fd, -bytes_read, SEEK_CUR);
138                         if(filelocation==0)
139                                 break;
140                 }
141         }
142         if(pip && (direction<0))
143                 bs++;
144         bytes_read=bs-startpoint;
145         memcpy(&buffer[0],&buffer[startpoint],bytes_read);
146
147         return 0;
148 }
149
150 void add_file(char *name)
151 {
152         ++n_files;
153         files = xrealloc(files, n_files);
154         files[n_files - 1] = (char *) xmalloc(strlen(name) + 1);
155         strcpy(files[n_files - 1], name);
156 }
157
158 void checknumbers(const char* name)
159 {
160         int test=atoi(name);
161         if(test){
162                 units=test;
163                 if(units<0)
164                         units=units-1;
165         } else {
166                 fatalError("Unrecognised number '%s'\n", name);
167         }
168 }
169
170 int tail_main(int argc, char **argv)
171 {
172         int show_headers = 1;
173         int test;
174         int opt;
175         char follow=0;
176         int sleep_int=1;
177         int *fd;
178
179         opterr = 0;
180         
181         while ((opt=getopt(argc,argv,"c:fhn:s:q:v123456789+")) >0) {
182
183                 switch (opt) {
184                         case '1':case '2':case '3':case '4':case '5':
185                         case '6':case '7':case '8':case '9':case '0':
186                                 checknumbers(argv[optind-1]);
187                                 break;
188
189 #ifndef BB_FEATURE_SIMPLE_TAIL
190                 case 'c':
191                         unit_type = BYTES;
192                         test = atoi(optarg);
193                         if(test==0)
194                                 usage(tail_usage);
195                         if(optarg[strlen(optarg)-1]>'9') {
196                                 switch (optarg[strlen(optarg)-1]) {
197                                 case 'b':
198                                         test *= 512;
199                                         break;
200                                 case 'k':
201                                         test *= 1024;
202                                         break;
203                                 case 'm':
204                                         test *= (1024 * 1024);
205                                         break;
206                                 default:
207                                         fprintf(stderr,"Size must be b,k, or m.");
208                                         usage(tail_usage);
209                                 }
210                         }
211                         if(optarg[0]=='+')
212                                 units=test+1;
213                         else
214                                 units=-(test+1);
215                         break;
216                 case 'q':
217                         show_headers = 0;
218                         break;
219                 case 's':
220                         sleep_int = atoi(optarg);
221                         if(sleep_int<1)
222                                 sleep_int=1;
223                         break;
224                 case 'v':
225                         verbose = 1;
226                         break;
227 #endif
228                 case 'f':
229                         follow = 1;
230                         break;
231                 case 'h':
232                         usage(tail_usage);
233                         break;
234                 case 'n':
235                         test = atoi(optarg);
236                         if (test) {
237                                 if (optarg[0] == '+')
238                                         units = test;
239                                 else
240                                         units = -(test+1);
241                         } else
242                                 usage(tail_usage);
243                         break;
244                 default:
245                         errorMsg("\nUnknown arg: %c.\n\n",optopt);
246                         usage(tail_usage);
247                 }
248         }
249         while (optind <= argc) {
250                 if(optind==argc) {
251                         if (n_files==0)
252                                 add_file(STDIN);
253                         else
254                                 break;
255                 }else {
256                         if (*argv[optind] == '+') {
257                                 checknumbers(argv[optind]);
258                         }
259                         else if (!strcmp(argv[optind], "-")) {
260                                 add_file(STDIN);
261                         } else {
262                                 add_file(argv[optind]);
263                         }
264                         optind++;
265                 }
266         }
267         if(units==0)
268                 units=-11;
269         if(units>0)
270                 units--;
271         fd=xmalloc(sizeof(int)*n_files);
272         if (n_files == 1)
273 #ifndef BB_FEATURE_SIMPLE_TAIL
274                 if (!verbose)
275 #endif
276                         show_headers = 0;
277         buffer=xmalloc(BUFSIZ);
278         for (test = 0; test < n_files; test++) {
279                 if (show_headers)
280                         printf("==> %s <==\n", files[test]);
281                 if (!strcmp(files[test], STDIN))
282                         fd[test] = 0;
283                 else
284                         fd[test] = open(files[test], O_RDONLY);
285                 if (fd[test] == -1)
286                         fatalError("Unable to open file %s.\n", files[test]);
287                 tail_stream(fd[test]);
288
289                 bs=BUFSIZ;
290                 while (1) {
291                         if((filelocation>0 || pip)){
292                                 write(1,buffer,bytes_read);
293                         }
294                         bytes_read = read(fd[test], buffer, bs);
295                         filelocation+=bytes_read;
296                         if (bytes_read <= 0) {
297                                 break;
298                         }
299                         usleep(sleep_int * 1000);
300                 }
301                 if(n_files>1)
302                         printf("\n");
303         }
304         while(1){
305                 for (test = 0; test < n_files; test++) {
306                         if(!follow){
307                                 close(fd[test]);
308                                 continue;
309                         } else {
310                                 sleep(sleep_int);
311                                 bytes_read = read(fd[test], buffer, bs);
312                                 if(bytes_read>0) {
313                                         if (show_headers)
314                                                 printf("==> %s <==\n", files[test]);
315                                         write(1,buffer,bytes_read);
316                                         if(n_files>1)
317                                                 printf("\n");
318                                 }
319                         }
320                 }
321                 if(!follow)
322                         break;
323         }
324         if (fd)
325                 free(fd);
326         if (buffer)
327                 free(buffer);
328         if(files)
329                 free(files);
330         return 0;
331 }
332
333 /*
334 Local Variables:
335 c-file-style: "linux"
336 c-basic-offset: 4
337 tab-width: 4
338 End:
339 */