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 | 24ac017 | 2000-12-18 21:38:57 +0000 | [diff] [blame] | 64 | static struct suffix_mult tail_suffixes[] = { |
| 65 | { "b", 512 }, |
| 66 | { "k", 1024 }, |
| 67 | { "m", 1048576 }, |
| 68 | { NULL, 0 } |
| 69 | }; |
| 70 | |
Matt Kraai | 69229a6 | 2000-10-19 21:28:32 +0000 | [diff] [blame] | 71 | static int tail_stream(int fd) |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 72 | { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 73 | ssize_t startpoint; |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 74 | ssize_t endpoint=0; |
| 75 | ssize_t count=0; |
| 76 | ssize_t filesize=0; |
Pavel Roskin | 8237add | 2000-08-22 15:38:16 +0000 | [diff] [blame] | 77 | int direction=1; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 78 | |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 79 | filelocation=0; |
| 80 | startpoint=bs=BUFSIZ; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 81 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 82 | filesize=lseek(fd, -1, SEEK_END)+1; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 83 | pip=(filesize<=0); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 84 | |
| 85 | if(units>=0) |
| 86 | lseek(fd,0,SEEK_SET); |
| 87 | else { |
| 88 | direction=-1; |
| 89 | count=1; |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 90 | } |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 91 | while(units != 0) { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 92 | if (pip) { |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 93 | char * line; |
| 94 | ssize_t f_size=0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 95 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 96 | bs=BUFSIZ; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 97 | line=xmalloc(bs); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 98 | while(1) { |
| 99 | bytes_read=read(fd,line,bs); |
| 100 | if(bytes_read<=0) |
| 101 | break; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 102 | buffer=xrealloc(buffer,f_size+bytes_read); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 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) |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 120 | break; |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 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++; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 136 | if (!pip) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 137 | filelocation=lseek(fd,0,SEEK_CUR); |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 138 | if(count==units*direction) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 139 | break; |
| 140 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 141 | if((count==units*direction) | pip) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 142 | break; |
| 143 | if(direction<0){ |
| 144 | filelocation = lseek(fd, -bytes_read, SEEK_CUR); |
| 145 | if(filelocation==0) |
| 146 | break; |
| 147 | } |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 148 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 149 | if(pip && (direction<0)) |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 150 | bs++; |
| 151 | bytes_read=bs-startpoint; |
| 152 | memcpy(&buffer[0],&buffer[startpoint],bytes_read); |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 153 | |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 154 | return 0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 155 | } |
| 156 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 157 | void add_file(char *name) |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 158 | { |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 159 | ++n_files; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 160 | files = xrealloc(files, n_files); |
| 161 | files[n_files - 1] = (char *) xmalloc(strlen(name) + 1); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 162 | strcpy(files[n_files - 1], name); |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 163 | } |
| 164 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 165 | int tail_main(int argc, char **argv) |
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 | int show_headers = 1; |
| 168 | int test; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 169 | int opt; |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 170 | char follow=0; |
| 171 | int sleep_int=1; |
| 172 | int *fd; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 173 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 174 | opterr = 0; |
| 175 | |
Matt Kraai | 69229a6 | 2000-10-19 21:28:32 +0000 | [diff] [blame] | 176 | while ((opt=getopt(argc,argv,"c:fhn:s:q:v")) >0) { |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 177 | |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 178 | switch (opt) { |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 179 | #ifndef BB_FEATURE_SIMPLE_TAIL |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 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; |
Matt Kraai | 24ac017 | 2000-12-18 21:38:57 +0000 | [diff] [blame] | 191 | case 'c': |
| 192 | unit_type = BYTES; |
| 193 | /* FALLS THROUGH */ |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 194 | #endif |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 195 | case 'n': |
Matt Kraai | 24ac017 | 2000-12-18 21:38:57 +0000 | [diff] [blame] | 196 | test = parse_number(optarg, tail_suffixes); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 197 | if (test) { |
| 198 | if (optarg[0] == '+') |
| 199 | units = test; |
| 200 | else |
| 201 | units = -(test+1); |
| 202 | } else |
| 203 | usage(tail_usage); |
| 204 | break; |
Matt Kraai | 24ac017 | 2000-12-18 21:38:57 +0000 | [diff] [blame] | 205 | case 'f': |
| 206 | follow = 1; |
| 207 | break; |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 208 | default: |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 209 | usage(tail_usage); |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 210 | } |
Eric Andersen | 1792f8c | 1999-12-09 06:11:36 +0000 | [diff] [blame] | 211 | } |
Eric Andersen | 46ade97 | 2000-08-02 19:57:18 +0000 | [diff] [blame] | 212 | while (optind <= argc) { |
| 213 | if(optind==argc) { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 214 | if (n_files==0) |
| 215 | add_file(STDIN); |
| 216 | else |
| 217 | break; |
| 218 | }else { |
Matt Kraai | 69229a6 | 2000-10-19 21:28:32 +0000 | [diff] [blame] | 219 | if (!strcmp(argv[optind], "-")) { |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 220 | add_file(STDIN); |
Eric Andersen | 46ade97 | 2000-08-02 19:57:18 +0000 | [diff] [blame] | 221 | } else { |
| 222 | add_file(argv[optind]); |
| 223 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 224 | optind++; |
| 225 | } |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 226 | } |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 227 | if(units==0) |
| 228 | units=-11; |
| 229 | if(units>0) |
| 230 | units--; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 231 | fd=xmalloc(sizeof(int)*n_files); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 232 | if (n_files == 1) |
| 233 | #ifndef BB_FEATURE_SIMPLE_TAIL |
| 234 | if (!verbose) |
| 235 | #endif |
| 236 | show_headers = 0; |
Matt Kraai | 322ae93 | 2000-09-13 02:46:14 +0000 | [diff] [blame] | 237 | buffer=xmalloc(BUFSIZ); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 238 | for (test = 0; test < n_files; test++) { |
| 239 | if (show_headers) |
| 240 | printf("==> %s <==\n", files[test]); |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 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) |
Mark Whitley | f57c944 | 2000-12-07 19:56:48 +0000 | [diff] [blame] | 246 | error_msg_and_die("Unable to open file %s.\n", files[test]); |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 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"); |
Erik Andersen | e49d5ec | 2000-02-08 19:58:47 +0000 | [diff] [blame] | 263 | } |
Eric Andersen | d5fa3e3 | 2000-08-02 16:42:58 +0000 | [diff] [blame] | 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); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 288 | if(files) |
| 289 | free(files); |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 290 | return 0; |
Eric Andersen | abc0f4f | 1999-12-08 23:19:36 +0000 | [diff] [blame] | 291 | } |
Erik Andersen | 3fe39dc | 2000-01-25 18:13:53 +0000 | [diff] [blame] | 292 | |
Eric Andersen | 98bbd68 | 2000-07-31 17:05:58 +0000 | [diff] [blame] | 293 | /* |
| 294 | Local Variables: |
| 295 | c-file-style: "linux" |
| 296 | c-basic-offset: 4 |
| 297 | tab-width: 4 |
| 298 | End: |
| 299 | */ |