tail.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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_xprint_header(const char *fmt, const char *filename)
  40. {
  41. /* If we get an output error, there is really no sense in continuing. */
  42. if (dprintf(STDOUT_FILENO, fmt, filename) < 0) {
  43. bb_perror_nomsg_and_die();
  44. }
  45. }
  46. /* len should probably be size_t */
  47. static void tail_xbb_full_write(const char *buf, size_t len)
  48. {
  49. /* If we get a write error, there is really no sense in continuing. */
  50. if (bb_full_write(STDOUT_FILENO, buf, len) < 0) {
  51. bb_perror_nomsg_and_die();
  52. }
  53. }
  54. static ssize_t tail_read(int fd, char *buf, size_t count)
  55. {
  56. ssize_t r;
  57. off_t current,end;
  58. struct stat sbuf;
  59. end = current = lseek(fd, 0, SEEK_CUR);
  60. if (!fstat(fd, &sbuf))
  61. end = sbuf.st_size;
  62. lseek(fd, end < current ? 0 : current, SEEK_SET);
  63. if ((r = safe_read(fd, buf, count)) < 0) {
  64. bb_perror_msg("read");
  65. status = EXIT_FAILURE;
  66. }
  67. return r;
  68. }
  69. static const char tail_opts[] =
  70. "fn:c:"
  71. #if ENABLE_FEATURE_FANCY_TAIL
  72. "qs:v"
  73. #endif
  74. ;
  75. static const char header_fmt[] = "\n==> %s <==\n";
  76. int tail_main(int argc, char **argv)
  77. {
  78. long count = 10;
  79. unsigned int sleep_period = 1;
  80. int from_top = 0;
  81. int follow = 0;
  82. int header_threshhold = 1;
  83. int count_bytes = 0;
  84. char *tailbuf;
  85. size_t tailbufsize;
  86. int taillen = 0;
  87. int newline = 0;
  88. int *fds, nfiles, nread, nwrite, seen, i, opt;
  89. char *s, *buf;
  90. const char *fmt;
  91. #if !ENABLE_DEBUG_YANK_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
  92. /* Allow legacy syntax of an initial numeric option without -n. */
  93. if (argc >=2 && ((argv[1][0] == '+') || ((argv[1][0] == '-')
  94. /* && (isdigit)(argv[1][1]) */
  95. && (((unsigned int)(argv[1][1] - '0')) <= 9))))
  96. {
  97. optind = 2;
  98. optarg = argv[1];
  99. goto GET_COUNT;
  100. }
  101. #endif
  102. while ((opt = getopt(argc, argv, tail_opts)) > 0) {
  103. switch (opt) {
  104. case 'f':
  105. follow = 1;
  106. break;
  107. case 'c':
  108. count_bytes = 1;
  109. /* FALLS THROUGH */
  110. case 'n':
  111. #if !ENABLE_DEBUG_YANK_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
  112. GET_COUNT:
  113. #endif
  114. count = bb_xgetlarg10_sfx(optarg, tail_suffixes);
  115. /* Note: Leading whitespace is an error trapped above. */
  116. if (*optarg == '+') {
  117. from_top = 1;
  118. } else {
  119. from_top = 0;
  120. }
  121. if (count < 0) {
  122. count = -count;
  123. }
  124. break;
  125. #if ENABLE_FEATURE_FANCY_TAIL
  126. case 'q':
  127. header_threshhold = INT_MAX;
  128. break;
  129. case 's':
  130. sleep_period =bb_xgetularg10_bnd(optarg, 0, UINT_MAX);
  131. break;
  132. case 'v':
  133. header_threshhold = 0;
  134. break;
  135. #endif
  136. default:
  137. bb_show_usage();
  138. }
  139. }
  140. /* open all the files */
  141. fds = (int *)xmalloc(sizeof(int) * (argc - optind + 1));
  142. argv += optind;
  143. nfiles = i = 0;
  144. if ((argc -= optind) == 0) {
  145. struct stat statbuf;
  146. if (!fstat(STDIN_FILENO, &statbuf) && S_ISFIFO(statbuf.st_mode)) {
  147. follow = 0;
  148. }
  149. /* --argv; */
  150. *argv = (char *) bb_msg_standard_input;
  151. goto DO_STDIN;
  152. }
  153. do {
  154. if ((argv[i][0] == '-') && !argv[i][1]) {
  155. DO_STDIN:
  156. fds[nfiles] = STDIN_FILENO;
  157. } else if ((fds[nfiles] = open(argv[i], O_RDONLY)) < 0) {
  158. bb_perror_msg("%s", argv[i]);
  159. status = EXIT_FAILURE;
  160. continue;
  161. }
  162. argv[nfiles] = argv[i];
  163. ++nfiles;
  164. } while (++i < argc);
  165. if (!nfiles) {
  166. bb_error_msg_and_die("no files");
  167. }
  168. tailbufsize = BUFSIZ;
  169. /* tail the files */
  170. if (from_top < count_bytes) { /* Each is 0 or 1, so true iff 0 < 1. */
  171. /* Hence, !from_top && count_bytes */
  172. if (tailbufsize < count) {
  173. tailbufsize = count + BUFSIZ;
  174. }
  175. }
  176. buf = tailbuf = xmalloc(tailbufsize);
  177. fmt = header_fmt + 1; /* Skip header leading newline on first output. */
  178. i = 0;
  179. do {
  180. /* Be careful. It would be possible to optimize the count-bytes
  181. * case if the file is seekable. If you do though, remember that
  182. * starting file position may not be the beginning of the file.
  183. * Beware of backing up too far. See example in wc.c.
  184. */
  185. if ((!(count|from_top)) && (lseek(fds[i], 0, SEEK_END) >= 0)) {
  186. continue;
  187. }
  188. if (nfiles > header_threshhold) {
  189. tail_xprint_header(fmt, argv[i]);
  190. fmt = header_fmt;
  191. }
  192. buf = tailbuf;
  193. taillen = 0;
  194. seen = 1;
  195. newline = 0;
  196. while ((nread = tail_read(fds[i], buf, tailbufsize-taillen)) > 0) {
  197. if (from_top) {
  198. nwrite = nread;
  199. if (seen < count) {
  200. if (count_bytes) {
  201. nwrite -= (count - seen);
  202. seen = count;
  203. } else {
  204. s = buf;
  205. do {
  206. --nwrite;
  207. if ((*s++ == '\n') && (++seen == count)) {
  208. break;
  209. }
  210. } while (nwrite);
  211. }
  212. }
  213. tail_xbb_full_write(buf + nread - nwrite, nwrite);
  214. } else if (count) {
  215. if (count_bytes) {
  216. taillen += nread;
  217. if (taillen > count) {
  218. memmove(tailbuf, tailbuf + taillen - count, count);
  219. taillen = count;
  220. }
  221. } else {
  222. int k = nread;
  223. int nbuf = 0;
  224. while (k) {
  225. --k;
  226. if (buf[k] == '\n') {
  227. ++nbuf;
  228. }
  229. }
  230. if (newline + nbuf < count) {
  231. newline += nbuf;
  232. taillen += nread;
  233. } else {
  234. int extra = 0;
  235. if (buf[nread-1] != '\n') {
  236. extra = 1;
  237. }
  238. k = newline + nbuf + extra - count;
  239. s = tailbuf;
  240. while (k) {
  241. if (*s == '\n') {
  242. --k;
  243. }
  244. ++s;
  245. }
  246. taillen += nread - (s - tailbuf);
  247. memmove(tailbuf, s, taillen);
  248. newline = count - extra;
  249. }
  250. if (tailbufsize < taillen + BUFSIZ) {
  251. tailbufsize = taillen + BUFSIZ;
  252. tailbuf = xrealloc(tailbuf, tailbufsize);
  253. }
  254. }
  255. buf = tailbuf + taillen;
  256. }
  257. }
  258. if (!from_top) {
  259. tail_xbb_full_write(tailbuf, taillen);
  260. }
  261. taillen = 0;
  262. } while (++i < nfiles);
  263. buf = xrealloc(tailbuf, BUFSIZ);
  264. fmt = NULL;
  265. while (follow) {
  266. sleep(sleep_period);
  267. i = 0;
  268. do {
  269. if (nfiles > header_threshhold) {
  270. fmt = header_fmt;
  271. }
  272. while ((nread = tail_read(fds[i], buf, sizeof(buf))) > 0) {
  273. if (fmt) {
  274. tail_xprint_header(fmt, argv[i]);
  275. fmt = NULL;
  276. }
  277. tail_xbb_full_write(buf, nread);
  278. }
  279. } while (++i < nfiles);
  280. }
  281. return status;
  282. }