tail.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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. /* BB_AUDIT SUSv3 compliant (need fancy for -c) */
  10. /* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
  11. /* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
  12. /* Mar 16, 2003 Manuel Novoa III (mjn3@codepoet.org)
  13. *
  14. * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
  15. * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
  16. * 1) mixing printf/write without fflush()ing stdout
  17. * 2) no check that any open files are present
  18. * 3) optstring had -q taking an arg
  19. * 4) no error checking on write in some cases, and a warning even then
  20. * 5) q and s interaction bug
  21. * 6) no check for lseek error
  22. * 7) lseek attempted when count==0 even if arg was +0 (from top)
  23. */
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <ctype.h>
  28. #include <unistd.h>
  29. #include <fcntl.h>
  30. #include <sys/stat.h>
  31. #include "busybox.h"
  32. static const struct suffix_mult tail_suffixes[] = {
  33. { "b", 512 },
  34. { "k", 1024 },
  35. { "m", 1048576 },
  36. { NULL, 0 }
  37. };
  38. static int status;
  39. static void tail_xbb_full_write(const char *buf, size_t len)
  40. {
  41. /* If we get a write error, there is really no sense in continuing. */
  42. if (full_write(STDOUT_FILENO, buf, len) < 0)
  43. bb_perror_nomsg_and_die();
  44. }
  45. static void tail_xprint_header(const char *fmt, const char *filename)
  46. {
  47. #if defined __GLIBC__
  48. if (dprintf(STDOUT_FILENO, fmt, filename) < 0) {
  49. bb_perror_nomsg_and_die();
  50. }
  51. #else
  52. int hdr_len = strlen(fmt) + strlen(filename);
  53. char *hdr = xzalloc(hdr_len);
  54. sprintf(hdr, filename, filename);
  55. tail_xbb_full_write(hdr, hdr_len);
  56. #endif
  57. }
  58. static ssize_t tail_read(int fd, char *buf, size_t count)
  59. {
  60. ssize_t r;
  61. off_t current,end;
  62. struct stat sbuf;
  63. end = current = lseek(fd, 0, SEEK_CUR);
  64. if (!fstat(fd, &sbuf))
  65. end = sbuf.st_size;
  66. lseek(fd, end < current ? 0 : current, SEEK_SET);
  67. if ((r = safe_read(fd, buf, count)) < 0) {
  68. bb_perror_msg(bb_msg_read_error);
  69. status = EXIT_FAILURE;
  70. }
  71. return r;
  72. }
  73. static const char tail_opts[] =
  74. "fn:c:"
  75. #if ENABLE_FEATURE_FANCY_TAIL
  76. "qs:v"
  77. #endif
  78. ;
  79. static const char header_fmt[] = "\n==> %s <==\n";
  80. int tail_main(int argc, char **argv)
  81. {
  82. long count = 10;
  83. unsigned sleep_period = 1;
  84. int from_top = 0;
  85. int follow = 0;
  86. int header_threshhold = 1;
  87. int count_bytes = 0;
  88. char *tailbuf;
  89. size_t tailbufsize;
  90. int taillen = 0;
  91. int newline = 0;
  92. int *fds, nfiles, nread, nwrite, seen, i, opt;
  93. char *s, *buf;
  94. const char *fmt;
  95. #if !ENABLE_DEBUG_YANK_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. optind = 2;
  101. optarg = argv[1];
  102. goto GET_COUNT;
  103. }
  104. #endif
  105. while ((opt = getopt(argc, argv, tail_opts)) > 0) {
  106. switch (opt) {
  107. case 'f':
  108. follow = 1;
  109. break;
  110. case 'c':
  111. count_bytes = 1;
  112. /* FALLS THROUGH */
  113. case 'n':
  114. #if !ENABLE_DEBUG_YANK_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
  115. GET_COUNT:
  116. #endif
  117. count = xatol_sfx(optarg, tail_suffixes);
  118. /* Note: Leading whitespace is an error trapped above. */
  119. if (*optarg == '+') {
  120. from_top = 1;
  121. } else {
  122. from_top = 0;
  123. }
  124. if (count < 0) {
  125. count = -count;
  126. }
  127. break;
  128. #if ENABLE_FEATURE_FANCY_TAIL
  129. case 'q':
  130. header_threshhold = INT_MAX;
  131. break;
  132. case 's':
  133. sleep_period = xatou(optarg);
  134. break;
  135. case 'v':
  136. header_threshhold = 0;
  137. break;
  138. #endif
  139. default:
  140. bb_show_usage();
  141. }
  142. }
  143. /* open all the files */
  144. fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1));
  145. argv += optind;
  146. nfiles = i = 0;
  147. if ((argc -= optind) == 0) {
  148. struct stat statbuf;
  149. if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) {
  150. follow = 0;
  151. }
  152. /* --argv; */
  153. *argv = (char *) bb_msg_standard_input;
  154. goto DO_STDIN;
  155. }
  156. do {
  157. if ((argv[i][0] == '-') && !argv[i][1]) {
  158. DO_STDIN:
  159. fds[nfiles] = STDIN_FILENO;
  160. } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) {
  161. bb_perror_msg("%s", argv[i]);
  162. status = EXIT_FAILURE;
  163. continue;
  164. }
  165. argv[nfiles] = argv[i];
  166. ++nfiles;
  167. } while (++i < argc);
  168. if (!nfiles) {
  169. bb_error_msg_and_die("no files");
  170. }
  171. tailbufsize = BUFSIZ;
  172. /* tail the files */
  173. if (from_top < count_bytes) { /* Each is 0 or 1, so true iff 0 < 1. */
  174. /* Hence, !from_top && count_bytes */
  175. if (tailbufsize < count) {
  176. tailbufsize = count + BUFSIZ;
  177. }
  178. }
  179. buf = tailbuf = xmalloc(tailbufsize);
  180. fmt = header_fmt + 1; /* Skip header leading newline on first output. */
  181. i = 0;
  182. do {
  183. /* Be careful. It would be possible to optimize the count-bytes
  184. * case if the file is seekable. If you do though, remember that
  185. * starting file position may not be the beginning of the file.
  186. * Beware of backing up too far. See example in wc.c.
  187. */
  188. if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) {
  189. continue;
  190. }
  191. if (nfiles > header_threshhold) {
  192. tail_xprint_header(fmt, argv[i]);
  193. fmt = header_fmt;
  194. }
  195. buf = tailbuf;
  196. taillen = 0;
  197. seen = 1;
  198. newline = 0;
  199. while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) {
  200. if (from_top) {
  201. nwrite = nread;
  202. if (seen < count) {
  203. if (count_bytes) {
  204. nwrite -= (count - seen);
  205. seen = count;
  206. } else {
  207. s = buf;
  208. do {
  209. --nwrite;
  210. if ((*s++ == '\n') && (++seen == count)) {
  211. break;
  212. }
  213. } while (nwrite);
  214. }
  215. }
  216. tail_xbb_full_write(buf + nread - nwrite, nwrite);
  217. } else if (count) {
  218. if (count_bytes) {
  219. taillen += nread;
  220. if (taillen > count) {
  221. memmove(tailbuf, tailbuf + taillen - count, count);
  222. taillen = count;
  223. }
  224. } else {
  225. int k = nread;
  226. int nbuf = 0;
  227. while (k) {
  228. --k;
  229. if (buf[k] == '\n') {
  230. ++nbuf;
  231. }
  232. }
  233. if (newline + nbuf < count) {
  234. newline += nbuf;
  235. taillen += nread;
  236. } else {
  237. int extra = 0;
  238. if (buf[nread-1] != '\n') {
  239. extra = 1;
  240. }
  241. k = newline + nbuf + extra - count;
  242. s = tailbuf;
  243. while (k) {
  244. if (*s == '\n') {
  245. --k;
  246. }
  247. ++s;
  248. }
  249. taillen += nread - (s - tailbuf);
  250. memmove(tailbuf, s, taillen);
  251. newline = count - extra;
  252. }
  253. if (tailbufsize < taillen + BUFSIZ) {
  254. tailbufsize = taillen + BUFSIZ;
  255. tailbuf = xrealloc(tailbuf, tailbufsize);
  256. }
  257. }
  258. buf = tailbuf + taillen;
  259. }
  260. }
  261. if (!from_top) {
  262. tail_xbb_full_write(tailbuf, taillen);
  263. }
  264. taillen = 0;
  265. } while (++i < nfiles);
  266. buf = xrealloc(tailbuf, BUFSIZ);
  267. fmt = NULL;
  268. while (follow) {
  269. sleep(sleep_period);
  270. i = 0;
  271. do {
  272. if (nfiles > header_threshhold) {
  273. fmt = header_fmt;
  274. }
  275. while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) {
  276. if (fmt) {
  277. tail_xprint_header(fmt, argv[i]);
  278. fmt = NULL;
  279. }
  280. tail_xbb_full_write(buf, nread);
  281. }
  282. } while (++i < nfiles);
  283. }
  284. return status;
  285. }