Some corrections, some additions, some embellishments.
[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 "busybox.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 static struct suffix_mult tail_suffixes[] = {
65         { "b", 512 },
66         { "k", 1024 },
67         { "m", 1048576 },
68         { NULL, 0 }
69 };
70
71 static int tail_stream(int fd)
72 {
73         ssize_t startpoint;
74         ssize_t endpoint=0;
75         ssize_t count=0;
76         ssize_t filesize=0;
77         int direction=1;
78
79         filelocation=0;
80         startpoint=bs=BUFSIZ;
81
82         filesize=lseek(fd, -1, SEEK_END)+1;
83         pip=(filesize<=0);
84
85         if(units>=0)
86                 lseek(fd,0,SEEK_SET);
87         else {
88                 direction=-1;
89                 count=1;
90         }
91         while(units != 0) {
92                 if (pip) {
93                         char * line;
94                         ssize_t f_size=0;
95
96                         bs=BUFSIZ;
97                         line=xmalloc(bs);
98                         while(1) {
99                                 bytes_read=read(fd,line,bs);
100                                 if(bytes_read<=0)
101                                         break;
102                                 buffer=xrealloc(buffer,f_size+bytes_read);
103                                 memcpy(&buffer[f_size],line,bytes_read);
104                                 filelocation=f_size+=bytes_read;
105                         }
106                         bs=f_size;
107                         if(direction<0)
108                                 bs--;
109                         if (line)
110                                 free(line);
111                 } else {
112                         filelocation = lseek(fd, 0, SEEK_CUR);
113                         if(direction<0) {
114                                 if(filelocation<bs)
115                                         bs=filelocation;
116                                 filelocation = lseek(fd, -bs, SEEK_CUR);
117                         }
118                         bytes_read = read(fd, buffer, bs);
119                         if (bytes_read <= 0)
120                                 break;
121                         bs=bytes_read;
122                 }
123                 startpoint=bs;
124                 if(direction>0) {
125                         endpoint=startpoint;
126                         startpoint=0;
127                 }
128                 for(;startpoint!=endpoint;startpoint+=direction) {
129 #ifndef BB_FEATURE_SIMPLE_TAIL
130                         if(unit_type==BYTES)
131                                 count++;
132                         else
133 #endif
134                                 if(buffer[startpoint-1]=='\n')
135                                         count++;
136                         if (!pip)
137                                 filelocation=lseek(fd,0,SEEK_CUR);
138                         if(count==units*direction)
139                                 break;
140                 }
141                 if((count==units*direction) | pip)
142                         break;
143                 if(direction<0){
144                         filelocation = lseek(fd, -bytes_read, SEEK_CUR);
145                         if(filelocation==0)
146                                 break;
147                 }
148         }
149         if(pip && (direction<0))
150                 bs++;
151         bytes_read=bs-startpoint;
152         memcpy(&buffer[0],&buffer[startpoint],bytes_read);
153
154         return 0;
155 }
156
157 void add_file(char *name)
158 {
159         ++n_files;
160         files = xrealloc(files, n_files);
161         files[n_files - 1] = (char *) xmalloc(strlen(name) + 1);
162         strcpy(files[n_files - 1], name);
163 }
164
165 int tail_main(int argc, char **argv)
166 {
167         int show_headers = 1;
168         int test;
169         int opt;
170         char follow=0;
171         int sleep_int=1;
172         int *fd;
173
174         opterr = 0;
175         
176         while ((opt=getopt(argc,argv,"c:fhn:s:q:v")) >0) {
177
178                 switch (opt) {
179 #ifndef BB_FEATURE_SIMPLE_TAIL
180                 case 'q':
181                         show_headers = 0;
182                         break;
183                 case 's':
184                         sleep_int = atoi(optarg);
185                         if(sleep_int<1)
186                                 sleep_int=1;
187                         break;
188                 case 'v':
189                         verbose = 1;
190                         break;
191                 case 'c':
192                         unit_type = BYTES;
193                         /* FALLS THROUGH */
194 #endif
195                 case 'n':
196                         test = parse_number(optarg, tail_suffixes);
197                         if (test) {
198                                 if (optarg[0] == '+')
199                                         units = test;
200                                 else
201                                         units = -(test+1);
202                         } else
203                                 usage(tail_usage);
204                         break;
205                 case 'f':
206                         follow = 1;
207                         break;
208                 default:
209                         usage(tail_usage);
210                 }
211         }
212         while (optind <= argc) {
213                 if(optind==argc) {
214                         if (n_files==0)
215                                 add_file(STDIN);
216                         else
217                                 break;
218                 }else {
219                         if (!strcmp(argv[optind], "-")) {
220                                 add_file(STDIN);
221                         } else {
222                                 add_file(argv[optind]);
223                         }
224                         optind++;
225                 }
226         }
227         if(units==0)
228                 units=-11;
229         if(units>0)
230                 units--;
231         fd=xmalloc(sizeof(int)*n_files);
232         if (n_files == 1)
233 #ifndef BB_FEATURE_SIMPLE_TAIL
234                 if (!verbose)
235 #endif
236                         show_headers = 0;
237         buffer=xmalloc(BUFSIZ);
238         for (test = 0; test < n_files; test++) {
239                 if (show_headers)
240                         printf("==> %s <==\n", files[test]);
241                 if (!strcmp(files[test], STDIN))
242                         fd[test] = 0;
243                 else
244                         fd[test] = open(files[test], O_RDONLY);
245                 if (fd[test] == -1)
246                         error_msg_and_die("Unable to open file %s.\n", files[test]);
247                 tail_stream(fd[test]);
248
249                 bs=BUFSIZ;
250                 while (1) {
251                         if((filelocation>0 || pip)){
252                                 write(1,buffer,bytes_read);
253                         }
254                         bytes_read = read(fd[test], buffer, bs);
255                         filelocation+=bytes_read;
256                         if (bytes_read <= 0) {
257                                 break;
258                         }
259                         usleep(sleep_int * 1000);
260                 }
261                 if(n_files>1)
262                         printf("\n");
263         }
264         while(1){
265                 for (test = 0; test < n_files; test++) {
266                         if(!follow){
267                                 close(fd[test]);
268                                 continue;
269                         } else {
270                                 sleep(sleep_int);
271                                 bytes_read = read(fd[test], buffer, bs);
272                                 if(bytes_read>0) {
273                                         if (show_headers)
274                                                 printf("==> %s <==\n", files[test]);
275                                         write(1,buffer,bytes_read);
276                                         if(n_files>1)
277                                                 printf("\n");
278                                 }
279                         }
280                 }
281                 if(!follow)
282                         break;
283         }
284         if (fd)
285                 free(fd);
286         if (buffer)
287                 free(buffer);
288         if(files)
289                 free(files);
290         return 0;
291 }
292
293 /*
294 Local Variables:
295 c-file-style: "linux"
296 c-basic-offset: 4
297 tab-width: 4
298 End:
299 */