Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 1 | /* vi: set sw=4 ts=4: */ |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 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, |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 24 | and generally busyboxed, Erik Andersen <andersen@lineo.com> |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 25 | |
| 26 | Removed superfluous options and associated code ("-c", "-n", "-q"). |
Erik Andersen | fac10d7 | 2000-02-07 05:29:42 +0000 | [diff] [blame] | 27 | Removed "tail -f" support for multiple files. |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 28 | Both changes by Friedrich Vedder <fwv@myrtle.lahn.de>. |
| 29 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 30 | Compleate Rewrite to correctly support "-NUM", "+NUM", and "-s" by |
| 31 | E.Allen Soard (esp@espsw.net). |
| 32 | |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 33 | */ |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 34 | #include <sys/types.h> |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 35 | #include <fcntl.h> |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 36 | #include <stdio.h> |
| 37 | #include <stdlib.h> |
| 38 | #include <unistd.h> |
| 39 | #include <string.h> |
| 40 | #include <getopt.h> |
Eric Andersen | 3570a34 | 2000-09-25 21:45:58 +0000 | [diff] [blame] | 41 | #include "busybox.h" |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 42 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 43 | #define STDIN "standard input" |
| 44 | #define LINES 0 |
| 45 | #define BYTES 1 |
Eric Andersen | 1792f8c | 1999-12-09 06:11:36 +0000 | [diff] [blame] | 46 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 47 | static int n_files = 0; |
| 48 | static char **files = NULL; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 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; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 54 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 55 | #ifdef BB_FEATURE_SIMPLE_TAIL |
| 56 | static const char unit_type=LINES; |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 57 | #else |
| 58 | static char unit_type=LINES; |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 59 | static char verbose = 0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 60 | #endif |
| 61 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 62 | static off_t units=0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 63 | |
Matt Kraai | 69229a6 | 2000-10-19 21:28:32 +0000 | [diff] [blame] | 64 | static int tail_stream(int fd) |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 65 | { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 66 | ssize_t startpoint; |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 67 | ssize_t endpoint=0; |
| 68 | ssize_t count=0; |
| 69 | ssize_t filesize=0; |
Pavel Roskin | 8237add | 2000-08-22 15:38:16 +0000 | [diff] [blame] | 70 | int direction=1; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 71 | |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 72 | filelocation=0; |
| 73 | startpoint=bs=BUFSIZ; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 74 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 75 | filesize=lseek(fd, -1, SEEK_END)+1; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 76 | pip=(filesize<=0); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 77 | |
| 78 | if(units>=0) |
| 79 | lseek(fd,0,SEEK_SET); |
| 80 | else { |
| 81 | direction=-1; |
| 82 | count=1; |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 83 | } |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 84 | while(units != 0) { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 85 | if (pip) { |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 86 | char * line; |
| 87 | ssize_t f_size=0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 88 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 89 | bs=BUFSIZ; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 90 | line=xmalloc(bs); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 91 | while(1) { |
| 92 | bytes_read=read(fd,line,bs); |
| 93 | if(bytes_read<=0) |
| 94 | break; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 95 | buffer=xrealloc(buffer,f_size+bytes_read); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 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) |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 113 | break; |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 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++; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 129 | if (!pip) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 130 | filelocation=lseek(fd,0,SEEK_CUR); |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 131 | if(count==units*direction) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 132 | break; |
| 133 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 134 | if((count==units*direction) | pip) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 135 | break; |
| 136 | if(direction<0){ |
| 137 | filelocation = lseek(fd, -bytes_read, SEEK_CUR); |
| 138 | if(filelocation==0) |
| 139 | break; |
| 140 | } |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 141 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 142 | if(pip && (direction<0)) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 143 | bs++; |
| 144 | bytes_read=bs-startpoint; |
| 145 | memcpy(&buffer[0],&buffer[startpoint],bytes_read); |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 146 | |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 147 | return 0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 148 | } |
| 149 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 150 | void add_file(char *name) |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 151 | { |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 152 | ++n_files; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 153 | files = xrealloc(files, n_files); |
| 154 | files[n_files - 1] = (char *) xmalloc(strlen(name) + 1); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 155 | strcpy(files[n_files - 1], name); |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 156 | } |
| 157 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 158 | int tail_main(int argc, char **argv) |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 159 | { |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 160 | int show_headers = 1; |
| 161 | int test; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 162 | int opt; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 163 | char follow=0; |
| 164 | int sleep_int=1; |
| 165 | int *fd; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 166 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 167 | opterr = 0; |
| 168 | |
Matt Kraai | 69229a6 | 2000-10-19 21:28:32 +0000 | [diff] [blame] | 169 | while ((opt=getopt(argc,argv,"c:fhn:s:q:v")) >0) { |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 170 | |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 171 | switch (opt) { |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 172 | #ifndef BB_FEATURE_SIMPLE_TAIL |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 173 | case 'c': |
| 174 | unit_type = BYTES; |
| 175 | test = atoi(optarg); |
| 176 | if(test==0) |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 177 | usage(tail_usage); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 178 | if(optarg[strlen(optarg)-1]>'9') { |
| 179 | switch (optarg[strlen(optarg)-1]) { |
| 180 | case 'b': |
| 181 | test *= 512; |
| 182 | break; |
| 183 | case 'k': |
| 184 | test *= 1024; |
| 185 | break; |
| 186 | case 'm': |
| 187 | test *= (1024 * 1024); |
| 188 | break; |
| 189 | default: |
| 190 | fprintf(stderr,"Size must be b,k, or m."); |
| 191 | usage(tail_usage); |
| 192 | } |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 193 | } |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 194 | if(optarg[0]=='+') |
| 195 | units=test+1; |
| 196 | else |
| 197 | units=-(test+1); |
| 198 | break; |
| 199 | case 'q': |
| 200 | show_headers = 0; |
| 201 | break; |
| 202 | case 's': |
| 203 | sleep_int = atoi(optarg); |
| 204 | if(sleep_int<1) |
| 205 | sleep_int=1; |
| 206 | break; |
| 207 | case 'v': |
| 208 | verbose = 1; |
| 209 | break; |
| 210 | #endif |
| 211 | case 'f': |
| 212 | follow = 1; |
| 213 | break; |
| 214 | case 'h': |
| 215 | usage(tail_usage); |
| 216 | break; |
| 217 | case 'n': |
| 218 | test = atoi(optarg); |
| 219 | if (test) { |
| 220 | if (optarg[0] == '+') |
| 221 | units = test; |
| 222 | else |
| 223 | units = -(test+1); |
| 224 | } else |
| 225 | usage(tail_usage); |
| 226 | break; |
| 227 | default: |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 228 | errorMsg("\nUnknown arg: %c.\n\n",optopt); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 229 | usage(tail_usage); |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 230 | } |
Eric Andersen | 1792f8c | 1999-12-09 06:11:36 +0000 | [diff] [blame] | 231 | } |
Eric Andersen | 46ade97 | 2000-08-02 19:57:18 +0000 | [diff] [blame] | 232 | while (optind <= argc) { |
| 233 | if(optind==argc) { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 234 | if (n_files==0) |
| 235 | add_file(STDIN); |
| 236 | else |
| 237 | break; |
| 238 | }else { |
Matt Kraai | 69229a6 | 2000-10-19 21:28:32 +0000 | [diff] [blame] | 239 | if (!strcmp(argv[optind], "-")) { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 240 | add_file(STDIN); |
Eric Andersen | 46ade97 | 2000-08-02 19:57:18 +0000 | [diff] [blame] | 241 | } else { |
| 242 | add_file(argv[optind]); |
| 243 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 244 | optind++; |
| 245 | } |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 246 | } |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 247 | if(units==0) |
| 248 | units=-11; |
| 249 | if(units>0) |
| 250 | units--; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 251 | fd=xmalloc(sizeof(int)*n_files); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 252 | if (n_files == 1) |
| 253 | #ifndef BB_FEATURE_SIMPLE_TAIL |
| 254 | if (!verbose) |
| 255 | #endif |
| 256 | show_headers = 0; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 257 | buffer=xmalloc(BUFSIZ); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 258 | for (test = 0; test < n_files; test++) { |
| 259 | if (show_headers) |
| 260 | printf("==> %s <==\n", files[test]); |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 261 | if (!strcmp(files[test], STDIN)) |
| 262 | fd[test] = 0; |
| 263 | else |
| 264 | fd[test] = open(files[test], O_RDONLY); |
| 265 | if (fd[test] == -1) |
| 266 | fatalError("Unable to open file %s.\n", files[test]); |
| 267 | tail_stream(fd[test]); |
| 268 | |
| 269 | bs=BUFSIZ; |
| 270 | while (1) { |
| 271 | if((filelocation>0 || pip)){ |
| 272 | write(1,buffer,bytes_read); |
| 273 | } |
| 274 | bytes_read = read(fd[test], buffer, bs); |
| 275 | filelocation+=bytes_read; |
| 276 | if (bytes_read <= 0) { |
| 277 | break; |
| 278 | } |
| 279 | usleep(sleep_int * 1000); |
| 280 | } |
| 281 | if(n_files>1) |
| 282 | printf("\n"); |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 283 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 284 | while(1){ |
| 285 | for (test = 0; test < n_files; test++) { |
| 286 | if(!follow){ |
| 287 | close(fd[test]); |
| 288 | continue; |
| 289 | } else { |
| 290 | sleep(sleep_int); |
| 291 | bytes_read = read(fd[test], buffer, bs); |
| 292 | if(bytes_read>0) { |
| 293 | if (show_headers) |
| 294 | printf("==> %s <==\n", files[test]); |
| 295 | write(1,buffer,bytes_read); |
| 296 | if(n_files>1) |
| 297 | printf("\n"); |
| 298 | } |
| 299 | } |
| 300 | } |
| 301 | if(!follow) |
| 302 | break; |
| 303 | } |
| 304 | if (fd) |
| 305 | free(fd); |
| 306 | if (buffer) |
| 307 | free(buffer); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 308 | if(files) |
| 309 | free(files); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 310 | return 0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 311 | } |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 312 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 313 | /* |
| 314 | Local Variables: |
| 315 | c-file-style: "linux" |
| 316 | c-basic-offset: 4 |
| 317 | tab-width: 4 |
| 318 | End: |
| 319 | */ |