17aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley/* tail.c - copy last lines from input to stdout.
250591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott *
350591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott * Copyright 2012 Timothy Elliott <tle@holymonkey.com>
450591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott *
5f91b7c89bc852868692b9518185421ebb52d67b3Rob Landley * See http://opengroup.org/onlinepubs/9699919799/utilities/tail.html
6ada3c0876dc99b6b1ef457e06d271d3b933f9dd9Rob Landley *
7ada3c0876dc99b6b1ef457e06d271d3b933f9dd9Rob Landley * Deviations from posix: -f waits for pipe/fifo on stdin (nonblock?).
850591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
91dd3704c5ffea926f61a96bb7de7d9dbee52fa44Paul BarkerUSE_TAIL(NEWTOY(tail, "?fc-n-[-cn]", TOYFLAG_USR|TOYFLAG_BIN))
1050591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
1150591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliottconfig TAIL
127aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  bool "tail"
137aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  default y
147aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  help
155f57bccc41c8893914121b00e16a96dd16282486Rob Landley    usage: tail [-n|c NUMBER] [-f] [FILE...]
1650591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
177aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    Copy last lines from files to stdout. If no files listed, copy from
187aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    stdin. Filename "-" is a synonym for stdin.
1950591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
20eb7e847adcecf11cd8fd99077ba3a582abe60146Elliott Hughes    -n	output the last NUMBER lines (default 10), +X counts from start
215f57bccc41c8893914121b00e16a96dd16282486Rob Landley    -c	output the last NUMBER bytes, +NUMBER counts from start
22908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao    -f	follow FILE(s), waiting for more data to be appended
232c48247a01a19c709f693d649d8158bccb5fbf70Rob Landley
242c48247a01a19c709f693d649d8158bccb5fbf70Rob Landleyconfig TAIL_SEEK
257aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  bool "tail seek support"
267aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  default y
277aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  depends on TAIL
287aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  help
297aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    This version uses lseek, which is faster on large files.
3050591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott*/
3150591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
32c0e56edaf256adb6c60c5a052525a1ffbb927901Rob Landley#define FOR_tail
3350591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott#include "toys.h"
34908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao#include <sys/inotify.h>
35908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao
36c0e56edaf256adb6c60c5a052525a1ffbb927901Rob LandleyGLOBALS(
377aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  long lines;
387aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  long bytes;
39b73d8e3c2d3b606295453940e602d173924cd380Rob Landley
4055a44676fa586b749718f7d24d6c579a02e2a398Rob Landley  int file_no, ffd, *files;
4150591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott)
4250591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
4350591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliottstruct line_list {
445f57bccc41c8893914121b00e16a96dd16282486Rob Landley  struct line_list *next, *prev;
457aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  char *data;
465f57bccc41c8893914121b00e16a96dd16282486Rob Landley  int len;
4750591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott};
4850591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
495f57bccc41c8893914121b00e16a96dd16282486Rob Landleystatic struct line_list *get_chunk(int fd, int len)
5050591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott{
517aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  struct line_list *line = xmalloc(sizeof(struct line_list)+len);
5250591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
535f57bccc41c8893914121b00e16a96dd16282486Rob Landley  memset(line, 0, sizeof(struct line_list));
545f57bccc41c8893914121b00e16a96dd16282486Rob Landley  line->data = ((char *)line) + sizeof(struct line_list);
557aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  line->len = readall(fd, line->data, len);
5650591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
575f57bccc41c8893914121b00e16a96dd16282486Rob Landley  if (line->len < 1) {
587aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    free(line);
597aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    return 0;
607aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  }
6150591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
627aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  return line;
6350591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott}
6450591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
652c48247a01a19c709f693d649d8158bccb5fbf70Rob Landleystatic void dump_chunk(void *ptr)
6650591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott{
677aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  struct line_list *list = ptr;
684b9c20351f5facff4b84c37757c335709af51133Felix Janda
695f57bccc41c8893914121b00e16a96dd16282486Rob Landley  xwrite(1, list->data, list->len);
707aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  free(list);
7150591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott}
7250591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
732c48247a01a19c709f693d649d8158bccb5fbf70Rob Landley// Reading through very large files is slow.  Using lseek can speed things
742c48247a01a19c709f693d649d8158bccb5fbf70Rob Landley// up a lot, but isn't applicable to all input (cat | tail).
752c48247a01a19c709f693d649d8158bccb5fbf70Rob Landley// Note: bytes and lines are negative here.
762c48247a01a19c709f693d649d8158bccb5fbf70Rob Landleystatic int try_lseek(int fd, long bytes, long lines)
7750591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott{
787aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  struct line_list *list = 0, *temp;
795f57bccc41c8893914121b00e16a96dd16282486Rob Landley  int flag = 0, chunk = sizeof(toybuf);
805cb65054067391af7602bc303d77349c76648fafJosh Gao  off_t pos = lseek(fd, 0, SEEK_END);
817aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
827aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // If lseek() doesn't work on this stream, return now.
835f57bccc41c8893914121b00e16a96dd16282486Rob Landley  if (pos<0) return 0;
847aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
857aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // Seek to the right spot, output data from there.
867aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  if (bytes) {
877aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    if (lseek(fd, bytes, SEEK_END)<0) lseek(fd, 0, SEEK_SET);
887aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    xsendfile(fd, 1);
897aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    return 1;
907aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  }
917aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
927aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // Read from end to find enough lines, then output them.
937aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
947aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  bytes = pos;
957aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  while (lines && pos) {
965f57bccc41c8893914121b00e16a96dd16282486Rob Landley    int offset;
977aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
987aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Read in next chunk from end of file
995f57bccc41c8893914121b00e16a96dd16282486Rob Landley    if (chunk>pos) chunk = pos;
1007aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    pos -= chunk;
1017aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    if (pos != lseek(fd, pos, SEEK_SET)) {
1027aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      perror_msg("seek failed");
1037aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      break;
1047aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    }
1057aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    if (!(temp = get_chunk(fd, chunk))) break;
1065f57bccc41c8893914121b00e16a96dd16282486Rob Landley    temp->next = list;
1077aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    list = temp;
1087aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1097aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Count newlines in this chunk.
1107aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    offset = list->len;
1117aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    while (offset--) {
1127aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      // If the last line ends with a newline, that one doesn't count.
1134b9c20351f5facff4b84c37757c335709af51133Felix Janda      if (!flag) flag++;
1147aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1157aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      // Start outputting data right after newline
1164b9c20351f5facff4b84c37757c335709af51133Felix Janda      else if (list->data[offset] == '\n' && !++lines) {
1177aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        offset++;
1187aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        list->data += offset;
1195f57bccc41c8893914121b00e16a96dd16282486Rob Landley        list->len -= offset;
1207aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1215f57bccc41c8893914121b00e16a96dd16282486Rob Landley        break;
1227aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      }
1237aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    }
1247aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  }
1257aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1267aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // Output stored data
1277aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  llist_traverse(list, dump_chunk);
1287aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1297aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // In case of -f
1307aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  lseek(fd, bytes, SEEK_SET);
1317aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  return 1;
13250591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott}
13350591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
1342c48247a01a19c709f693d649d8158bccb5fbf70Rob Landley// Called for each file listed on command line, and/or stdin
13550591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliottstatic void do_tail(int fd, char *name)
13650591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott{
1377aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  long bytes = TT.bytes, lines = TT.lines;
1385f57bccc41c8893914121b00e16a96dd16282486Rob Landley  int linepop = 1;
1397aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
14055a44676fa586b749718f7d24d6c579a02e2a398Rob Landley  if (toys.optflags & FLAG_f) {
141ada3c0876dc99b6b1ef457e06d271d3b933f9dd9Rob Landley    int f = TT.file_no*2;
14255a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    char *s = name;
14355a44676fa586b749718f7d24d6c579a02e2a398Rob Landley
14455a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    if (!fd) sprintf(s = toybuf, "/proc/self/fd/%d", fd);
14555a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    TT.files[f++] = fd;
14655a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    if (0 > (TT.files[f] = inotify_add_watch(TT.ffd, s, IN_MODIFY)))
14755a44676fa586b749718f7d24d6c579a02e2a398Rob Landley      perror_msg("bad -f on '%s'", name);
14855a44676fa586b749718f7d24d6c579a02e2a398Rob Landley  }
14955a44676fa586b749718f7d24d6c579a02e2a398Rob Landley
150ada3c0876dc99b6b1ef457e06d271d3b933f9dd9Rob Landley  if (TT.file_no++) xputc('\n');
151ada3c0876dc99b6b1ef457e06d271d3b933f9dd9Rob Landley  if (toys.optc > 1) xprintf("==> %s <==\n", name);
1527aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1537aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // Are we measuring from the end of the file?
1547aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1557aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  if (bytes<0 || lines<0) {
1565f57bccc41c8893914121b00e16a96dd16282486Rob Landley    struct line_list *list = 0, *new;
1577aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1587aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // The slow codepath is always needed, and can handle all input,
1597aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // so make lseek support optional.
1604b9c20351f5facff4b84c37757c335709af51133Felix Janda    if (CFG_TAIL_SEEK && try_lseek(fd, bytes, lines)) return;
1617aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
1627aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Read data until we run out, keep a trailing buffer
1634b9c20351f5facff4b84c37757c335709af51133Felix Janda    for (;;) {
1645f57bccc41c8893914121b00e16a96dd16282486Rob Landley      // Read next page of data, appending to linked list in order
1657aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      if (!(new = get_chunk(fd, sizeof(toybuf)))) break;
1665f57bccc41c8893914121b00e16a96dd16282486Rob Landley      dlist_add_nomalloc((void *)&list, (void *)new);
1675f57bccc41c8893914121b00e16a96dd16282486Rob Landley
1685f57bccc41c8893914121b00e16a96dd16282486Rob Landley      // If tracing bytes, add until we have enough, discarding overflow.
1695f57bccc41c8893914121b00e16a96dd16282486Rob Landley      if (TT.bytes) {
1705f57bccc41c8893914121b00e16a96dd16282486Rob Landley        bytes += new->len;
1715f57bccc41c8893914121b00e16a96dd16282486Rob Landley        if (bytes > 0) {
1725f57bccc41c8893914121b00e16a96dd16282486Rob Landley          while (list->len <= bytes) {
1735f57bccc41c8893914121b00e16a96dd16282486Rob Landley            bytes -= list->len;
1745f57bccc41c8893914121b00e16a96dd16282486Rob Landley            free(dlist_pop(&list));
1755f57bccc41c8893914121b00e16a96dd16282486Rob Landley          }
1765f57bccc41c8893914121b00e16a96dd16282486Rob Landley          list->data += bytes;
1775f57bccc41c8893914121b00e16a96dd16282486Rob Landley          list->len -= bytes;
178159a7f1621eecf6cf3c2824ffb762a19bf5f7667Rob Landley          bytes = 0;
1797aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        }
1805f57bccc41c8893914121b00e16a96dd16282486Rob Landley      } else {
1815f57bccc41c8893914121b00e16a96dd16282486Rob Landley        int len = new->len, count;
1825f57bccc41c8893914121b00e16a96dd16282486Rob Landley        char *try = new->data;
1835f57bccc41c8893914121b00e16a96dd16282486Rob Landley
1845f57bccc41c8893914121b00e16a96dd16282486Rob Landley        // First character _after_ a newline starts a new line, which
1855f57bccc41c8893914121b00e16a96dd16282486Rob Landley        // works even if file doesn't end with a newline
1865f57bccc41c8893914121b00e16a96dd16282486Rob Landley        for (count=0; count<len; count++) {
1875f57bccc41c8893914121b00e16a96dd16282486Rob Landley          if (linepop) lines++;
1885f57bccc41c8893914121b00e16a96dd16282486Rob Landley          linepop = try[count] == '\n';
1895f57bccc41c8893914121b00e16a96dd16282486Rob Landley
1905f57bccc41c8893914121b00e16a96dd16282486Rob Landley          if (lines > 0) {
191de36079ccdceffee0ed750ab8780b0c68925ba2fRob Landley            char c;
192de36079ccdceffee0ed750ab8780b0c68925ba2fRob Landley
1935f57bccc41c8893914121b00e16a96dd16282486Rob Landley            do {
194de36079ccdceffee0ed750ab8780b0c68925ba2fRob Landley              c = *list->data;
1955f57bccc41c8893914121b00e16a96dd16282486Rob Landley              if (!--(list->len)) free(dlist_pop(&list));
196de36079ccdceffee0ed750ab8780b0c68925ba2fRob Landley              else list->data++;
197de36079ccdceffee0ed750ab8780b0c68925ba2fRob Landley            } while (c != '\n');
1985f57bccc41c8893914121b00e16a96dd16282486Rob Landley            lines--;
1995f57bccc41c8893914121b00e16a96dd16282486Rob Landley          }
2007aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley        }
2017aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      }
2027aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    }
2037aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
2047aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Output/free the buffer.
2055f57bccc41c8893914121b00e16a96dd16282486Rob Landley    llist_traverse(list, dump_chunk);
2067aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
2077aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  // Measuring from the beginning of the file.
2087aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  } else for (;;) {
2097aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    int len, offset = 0;
2107aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley
2117aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    // Error while reading does not exit.  Error writing does.
2127aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    len = read(fd, toybuf, sizeof(toybuf));
2137aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    if (len<1) break;
2147aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    while (bytes > 1 || lines > 1) {
2157aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      bytes--;
2167aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      if (toybuf[offset++] == '\n') lines--;
2177aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley      if (offset >= len) break;
2187aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    }
2197aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley    if (offset<len) xwrite(1, toybuf+offset, len-offset);
2207aa651a6a4496d848f86de9b1e6b3a003256a01fRob Landley  }
22150591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott}
22250591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
22350591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliottvoid tail_main(void)
22450591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott{
2258d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley  char **args = toys.optargs;
22650591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott
2278d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley  if (!(toys.optflags&(FLAG_n|FLAG_c))) {
2288d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley    char *arg = *args;
2298d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley
2308d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley    // handle old "-42" style arguments
2318d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley    if (arg && *arg == '-' && arg[1]) {
2328d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley      TT.lines = atolx(*(args++));
2338d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley      toys.optc--;
234e57cb179608077b7459b95bb3219035dd0045912Elliott Hughes    } else {
235e57cb179608077b7459b95bb3219035dd0045912Elliott Hughes      // if nothing specified, default -n to -10
236e57cb179608077b7459b95bb3219035dd0045912Elliott Hughes      TT.lines = -10;
2378d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley    }
2388d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley  }
2398d3b3987b8861ea09e2f7f5bd8fcd7b1195e6ae1Rob Landley
24055a44676fa586b749718f7d24d6c579a02e2a398Rob Landley  // Allocate 2 ints per optarg for -f
24155a44676fa586b749718f7d24d6c579a02e2a398Rob Landley  if (toys.optflags&FLAG_f) {
24255a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    if ((TT.ffd = inotify_init()) < 0) perror_exit("inotify_init");
24355a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    TT.files = xmalloc(toys.optc*8);
24455a44676fa586b749718f7d24d6c579a02e2a398Rob Landley  }
245eed9ed41aa73023af8f79cd5353b96b80585490fRob Landley  loopfiles_rw(args, O_RDONLY|WARN_ONLY|(O_CLOEXEC*!(toys.optflags&FLAG_f)),
246eed9ed41aa73023af8f79cd5353b96b80585490fRob Landley    0, do_tail);
2472c48247a01a19c709f693d649d8158bccb5fbf70Rob Landley
2485f58880c15ce3434c0eac8959ea1e07a91b0dc03Elliott Hughes  if ((toys.optflags & FLAG_f) && TT.file_no) {
24955a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    int len, last_fd = TT.files[(TT.file_no-1)*2], i, fd;
25055a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    struct inotify_event ev;
251908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao
25255a44676fa586b749718f7d24d6c579a02e2a398Rob Landley    for (;;) {
25355a44676fa586b749718f7d24d6c579a02e2a398Rob Landley      if (sizeof(ev)!=read(TT.ffd, &ev, sizeof(ev))) perror_exit("inotify");
254908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao
25555a44676fa586b749718f7d24d6c579a02e2a398Rob Landley      for (i = 0; i<TT.file_no && ev.wd!=TT.files[(i*2)+1]; i++);
25655a44676fa586b749718f7d24d6c579a02e2a398Rob Landley      if (i==TT.file_no) continue;
25755a44676fa586b749718f7d24d6c579a02e2a398Rob Landley      fd = TT.files[i*2];
258908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao
25955a44676fa586b749718f7d24d6c579a02e2a398Rob Landley      // Read new data.
26055a44676fa586b749718f7d24d6c579a02e2a398Rob Landley      while ((len = read(fd, toybuf, sizeof(toybuf)))>0) {
26155a44676fa586b749718f7d24d6c579a02e2a398Rob Landley        if (last_fd != fd) {
26255a44676fa586b749718f7d24d6c579a02e2a398Rob Landley          last_fd = fd;
26355a44676fa586b749718f7d24d6c579a02e2a398Rob Landley          xprintf("\n==> %s <==\n", args[i]);
264908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao        }
265908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao
26655a44676fa586b749718f7d24d6c579a02e2a398Rob Landley        xwrite(1, toybuf, len);
267908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao      }
268908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao    }
269908d9edb66e736f4004c91e148922cbb1d7b3090Josh Gao  }
27050591238eb4e70bf2bca59f536fd4f2253cb57f9Timothy Elliott}
271