1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini tail implementation for busybox 4 * 5 * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu> 6 * 7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 8 */ 9 10/* BB_AUDIT SUSv3 compliant (need fancy for -c) */ 11/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */ 12/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */ 13 14/* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org) 15 * 16 * Pretty much rewritten to fix numerous bugs and reduce realloc() calls. 17 * Bugs fixed (although I may have forgotten one or two... it was pretty bad) 18 * 1) mixing printf/write without fflush()ing stdout 19 * 2) no check that any open files are present 20 * 3) optstring had -q taking an arg 21 * 4) no error checking on write in some cases, and a warning even then 22 * 5) q and s interaction bug 23 * 6) no check for lseek error 24 * 7) lseek attempted when count==0 even if arg was +0 (from top) 25 */ 26 27#include "libbb.h" 28 29static const struct suffix_mult tail_suffixes[] = { 30 { "b", 512 }, 31 { "k", 1024 }, 32 { "m", 1024*1024 }, 33 { } 34}; 35 36struct globals { 37 bool status; 38}; 39#define G (*(struct globals*)&bb_common_bufsiz1) 40 41static void tail_xprint_header(const char *fmt, const char *filename) 42{ 43 if (fdprintf(STDOUT_FILENO, fmt, filename) < 0) 44 bb_perror_nomsg_and_die(); 45} 46 47static ssize_t tail_read(int fd, char *buf, size_t count) 48{ 49 ssize_t r; 50 off_t current, end; 51 struct stat sbuf; 52 53 end = current = lseek(fd, 0, SEEK_CUR); 54 if (!fstat(fd, &sbuf)) 55 end = sbuf.st_size; 56 lseek(fd, end < current ? 0 : current, SEEK_SET); 57 r = safe_read(fd, buf, count); 58 if (r < 0) { 59 bb_perror_msg(bb_msg_read_error); 60 G.status = EXIT_FAILURE; 61 } 62 63 return r; 64} 65 66static const char header_fmt[] ALIGN1 = "\n==> %s <==\n"; 67 68static unsigned eat_num(const char *p) 69{ 70 if (*p == '-') p++; 71 else if (*p == '+') { p++; G.status = EXIT_FAILURE; } 72 return xatou_sfx(p, tail_suffixes); 73} 74 75int tail_main(int argc, char **argv); 76int tail_main(int argc, char **argv) 77{ 78 unsigned count = 10; 79 unsigned sleep_period = 1; 80 bool from_top; 81 int header_threshhold = 1; 82 const char *str_c, *str_n; 83 USE_FEATURE_FANCY_TAIL(const char *str_s;) 84 85 char *tailbuf; 86 size_t tailbufsize; 87 int taillen = 0; 88 int newline = 0; 89 int nfiles, nread, nwrite, seen, i, opt; 90 91 int *fds; 92 char *s, *buf; 93 const char *fmt; 94 95#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL 96 /* Allow legacy syntax of an initial numeric option without -n. */ 97 if (argc >= 2 && (argv[1][0] == '+' || argv[1][0] == '-') 98 && isdigit(argv[1][1]) 99 ) { 100 /* replacing arg[0] with "-n" can segfault, so... */ 101 argv[1] = xasprintf("-n%s", argv[1]); 102 } 103#endif 104 105 opt = getopt32(argv, "fc:n:" USE_FEATURE_FANCY_TAIL("qs:v"), 106 &str_c, &str_n USE_FEATURE_FANCY_TAIL(,&str_s)); 107#define FOLLOW (opt & 0x1) 108#define COUNT_BYTES (opt & 0x2) 109 //if (opt & 0x1) // -f 110 if (opt & 0x2) count = eat_num(str_c); // -c 111 if (opt & 0x4) count = eat_num(str_n); // -n 112#if ENABLE_FEATURE_FANCY_TAIL 113 if (opt & 0x8) header_threshhold = INT_MAX; // -q 114 if (opt & 0x10) sleep_period = xatou(str_s); // -s 115 if (opt & 0x20) header_threshhold = 0; // -v 116#endif 117 argc -= optind; 118 argv += optind; 119 from_top = G.status; 120 121 /* open all the files */ 122 fds = xmalloc(sizeof(int) * (argc + 1)); 123 nfiles = i = 0; 124 G.status = EXIT_SUCCESS; 125 if (argc == 0) { 126 struct stat statbuf; 127 128 if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) { 129 opt &= ~1; /* clear FOLLOW */ 130 } 131 *argv = (char *) bb_msg_standard_input; 132 } 133 do { 134 FILE* fil = fopen_or_warn_stdin(argv[i]); 135 if (!fil) { 136 G.status = EXIT_FAILURE; 137 continue; 138 } 139 fds[nfiles] = fileno(fil); 140 argv[nfiles++] = argv[i]; 141 } while (++i < argc); 142 143 if (!nfiles) 144 bb_error_msg_and_die("no files"); 145 146 tailbufsize = BUFSIZ; 147 148 /* tail the files */ 149 if (!from_top && COUNT_BYTES) { 150 if (tailbufsize < count) { 151 tailbufsize = count + BUFSIZ; 152 } 153 } 154 155 buf = tailbuf = xmalloc(tailbufsize); 156 157 fmt = header_fmt + 1; /* Skip header leading newline on first output. */ 158 i = 0; 159 do { 160 /* Be careful. It would be possible to optimize the count-bytes 161 * case if the file is seekable. If you do though, remember that 162 * starting file position may not be the beginning of the file. 163 * Beware of backing up too far. See example in wc.c. 164 */ 165 if (!(count | from_top) && lseek(fds[i], 0, SEEK_END) >= 0) { 166 continue; 167 } 168 169 if (nfiles > header_threshhold) { 170 tail_xprint_header(fmt, argv[i]); 171 fmt = header_fmt; 172 } 173 174 buf = tailbuf; 175 taillen = 0; 176 seen = 1; 177 newline = 0; 178 179 while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) { 180 if (from_top) { 181 nwrite = nread; 182 if (seen < count) { 183 if (COUNT_BYTES) { 184 nwrite -= (count - seen); 185 seen = count; 186 } else { 187 s = buf; 188 do { 189 --nwrite; 190 if (*s++ == '\n' && ++seen == count) { 191 break; 192 } 193 } while (nwrite); 194 } 195 } 196 xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite); 197 } else if (count) { 198 if (COUNT_BYTES) { 199 taillen += nread; 200 if (taillen > count) { 201 memmove(tailbuf, tailbuf + taillen - count, count); 202 taillen = count; 203 } 204 } else { 205 int k = nread; 206 int nbuf = 0; 207 208 while (k) { 209 --k; 210 if (buf[k] == '\n') { 211 ++nbuf; 212 } 213 } 214 215 if (newline + nbuf < count) { 216 newline += nbuf; 217 taillen += nread; 218 } else { 219 int extra = 0; 220 221 if (buf[nread-1] != '\n') 222 extra = 1; 223 k = newline + nbuf + extra - count; 224 s = tailbuf; 225 while (k) { 226 if (*s == '\n') { 227 --k; 228 } 229 ++s; 230 } 231 taillen += nread - (s - tailbuf); 232 memmove(tailbuf, s, taillen); 233 newline = count - extra; 234 } 235 if (tailbufsize < taillen + BUFSIZ) { 236 tailbufsize = taillen + BUFSIZ; 237 tailbuf = xrealloc(tailbuf, tailbufsize); 238 } 239 } 240 buf = tailbuf + taillen; 241 } 242 } 243 244 if (!from_top) { 245 xwrite(STDOUT_FILENO, tailbuf, taillen); 246 } 247 248 taillen = 0; 249 } while (++i < nfiles); 250 251 buf = xrealloc(tailbuf, BUFSIZ); 252 253 fmt = NULL; 254 255 if (FOLLOW) while (1) { 256 sleep(sleep_period); 257 i = 0; 258 do { 259 if (nfiles > header_threshhold) { 260 fmt = header_fmt; 261 } 262 while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) { 263 if (fmt) { 264 tail_xprint_header(fmt, argv[i]); 265 fmt = NULL; 266 } 267 xwrite(STDOUT_FILENO, buf, nread); 268 } 269 } while (++i < nfiles); 270 } 271 if (ENABLE_FEATURE_CLEAN_UP) { 272 free(fds); 273 } 274 return G.status; 275} 276