1*cf5a6c84SAndroid Build Coastguard Worker /* tail.c - copy last lines from input to stdout.
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2012 Timothy Elliott <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker *
5*cf5a6c84SAndroid Build Coastguard Worker * See http://opengroup.org/onlinepubs/9699919799/utilities/tail.html
6*cf5a6c84SAndroid Build Coastguard Worker *
7*cf5a6c84SAndroid Build Coastguard Worker * Deviations from posix: -f waits for pipe/fifo on stdin (nonblock?).
8*cf5a6c84SAndroid Build Coastguard Worker
9*cf5a6c84SAndroid Build Coastguard Worker USE_TAIL(NEWTOY(tail, "?fFs:c(bytes)-n(lines)-[-cn][-fF]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF))
10*cf5a6c84SAndroid Build Coastguard Worker
11*cf5a6c84SAndroid Build Coastguard Worker config TAIL
12*cf5a6c84SAndroid Build Coastguard Worker bool "tail"
13*cf5a6c84SAndroid Build Coastguard Worker default y
14*cf5a6c84SAndroid Build Coastguard Worker help
15*cf5a6c84SAndroid Build Coastguard Worker usage: tail [-n|c NUMBER] [-f|F] [-s SECONDS] [FILE...]
16*cf5a6c84SAndroid Build Coastguard Worker
17*cf5a6c84SAndroid Build Coastguard Worker Copy last lines from files to stdout. If no files listed, copy from
18*cf5a6c84SAndroid Build Coastguard Worker stdin. Filename "-" is a synonym for stdin.
19*cf5a6c84SAndroid Build Coastguard Worker
20*cf5a6c84SAndroid Build Coastguard Worker -n Output the last NUMBER lines (default 10), +X counts from start
21*cf5a6c84SAndroid Build Coastguard Worker -c Output the last NUMBER bytes, +NUMBER counts from start
22*cf5a6c84SAndroid Build Coastguard Worker -f Follow FILE(s) by descriptor, waiting for more data to be appended
23*cf5a6c84SAndroid Build Coastguard Worker -F Follow FILE(s) by filename, waiting for more data, and retrying
24*cf5a6c84SAndroid Build Coastguard Worker -s Used with -F, sleep SECONDS between retries (default 1)
25*cf5a6c84SAndroid Build Coastguard Worker */
26*cf5a6c84SAndroid Build Coastguard Worker
27*cf5a6c84SAndroid Build Coastguard Worker #define FOR_tail
28*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
29*cf5a6c84SAndroid Build Coastguard Worker
30*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
31*cf5a6c84SAndroid Build Coastguard Worker long n, c;
32*cf5a6c84SAndroid Build Coastguard Worker char *s;
33*cf5a6c84SAndroid Build Coastguard Worker
34*cf5a6c84SAndroid Build Coastguard Worker int file_no, last_fd, ss;
35*cf5a6c84SAndroid Build Coastguard Worker struct xnotify *not;
36*cf5a6c84SAndroid Build Coastguard Worker struct {
37*cf5a6c84SAndroid Build Coastguard Worker char *path;
38*cf5a6c84SAndroid Build Coastguard Worker int fd;
39*cf5a6c84SAndroid Build Coastguard Worker struct dev_ino di;
40*cf5a6c84SAndroid Build Coastguard Worker } *F;
41*cf5a6c84SAndroid Build Coastguard Worker )
42*cf5a6c84SAndroid Build Coastguard Worker
43*cf5a6c84SAndroid Build Coastguard Worker struct line_list {
44*cf5a6c84SAndroid Build Coastguard Worker struct line_list *next, *prev;
45*cf5a6c84SAndroid Build Coastguard Worker char *data;
46*cf5a6c84SAndroid Build Coastguard Worker int len;
47*cf5a6c84SAndroid Build Coastguard Worker };
48*cf5a6c84SAndroid Build Coastguard Worker
read_chunk(int fd,int len)49*cf5a6c84SAndroid Build Coastguard Worker static struct line_list *read_chunk(int fd, int len)
50*cf5a6c84SAndroid Build Coastguard Worker {
51*cf5a6c84SAndroid Build Coastguard Worker struct line_list *line = xmalloc(sizeof(struct line_list)+len);
52*cf5a6c84SAndroid Build Coastguard Worker
53*cf5a6c84SAndroid Build Coastguard Worker memset(line, 0, sizeof(struct line_list));
54*cf5a6c84SAndroid Build Coastguard Worker line->data = ((char *)line) + sizeof(struct line_list);
55*cf5a6c84SAndroid Build Coastguard Worker line->len = readall(fd, line->data, len);
56*cf5a6c84SAndroid Build Coastguard Worker
57*cf5a6c84SAndroid Build Coastguard Worker if (line->len < 1) {
58*cf5a6c84SAndroid Build Coastguard Worker free(line);
59*cf5a6c84SAndroid Build Coastguard Worker return 0;
60*cf5a6c84SAndroid Build Coastguard Worker }
61*cf5a6c84SAndroid Build Coastguard Worker
62*cf5a6c84SAndroid Build Coastguard Worker return line;
63*cf5a6c84SAndroid Build Coastguard Worker }
64*cf5a6c84SAndroid Build Coastguard Worker
write_chunk(void * ptr)65*cf5a6c84SAndroid Build Coastguard Worker static void write_chunk(void *ptr)
66*cf5a6c84SAndroid Build Coastguard Worker {
67*cf5a6c84SAndroid Build Coastguard Worker struct line_list *list = ptr;
68*cf5a6c84SAndroid Build Coastguard Worker
69*cf5a6c84SAndroid Build Coastguard Worker xwrite(1, list->data, list->len);
70*cf5a6c84SAndroid Build Coastguard Worker free(list);
71*cf5a6c84SAndroid Build Coastguard Worker }
72*cf5a6c84SAndroid Build Coastguard Worker
73*cf5a6c84SAndroid Build Coastguard Worker // Reading through very large files is slow. Using lseek can speed things
74*cf5a6c84SAndroid Build Coastguard Worker // up a lot, but isn't applicable to all input (cat | tail).
75*cf5a6c84SAndroid Build Coastguard Worker // Note: bytes and lines are negative here.
try_lseek(int fd,long bytes,long lines)76*cf5a6c84SAndroid Build Coastguard Worker static int try_lseek(int fd, long bytes, long lines)
77*cf5a6c84SAndroid Build Coastguard Worker {
78*cf5a6c84SAndroid Build Coastguard Worker struct line_list *list = 0, *temp;
79*cf5a6c84SAndroid Build Coastguard Worker int flag = 0, chunk = sizeof(toybuf);
80*cf5a6c84SAndroid Build Coastguard Worker off_t pos = lseek(fd, 0, SEEK_END);
81*cf5a6c84SAndroid Build Coastguard Worker
82*cf5a6c84SAndroid Build Coastguard Worker // If lseek() doesn't work on this stream, return now.
83*cf5a6c84SAndroid Build Coastguard Worker if (pos<0) return 0;
84*cf5a6c84SAndroid Build Coastguard Worker
85*cf5a6c84SAndroid Build Coastguard Worker // Seek to the right spot, output data from there.
86*cf5a6c84SAndroid Build Coastguard Worker if (bytes) {
87*cf5a6c84SAndroid Build Coastguard Worker if (lseek(fd, bytes, SEEK_END)<0) lseek(fd, 0, SEEK_SET);
88*cf5a6c84SAndroid Build Coastguard Worker xsendfile(fd, 1);
89*cf5a6c84SAndroid Build Coastguard Worker return 1;
90*cf5a6c84SAndroid Build Coastguard Worker }
91*cf5a6c84SAndroid Build Coastguard Worker
92*cf5a6c84SAndroid Build Coastguard Worker // Read from end to find enough lines, then output them.
93*cf5a6c84SAndroid Build Coastguard Worker
94*cf5a6c84SAndroid Build Coastguard Worker bytes = pos;
95*cf5a6c84SAndroid Build Coastguard Worker while (lines && pos) {
96*cf5a6c84SAndroid Build Coastguard Worker int offset;
97*cf5a6c84SAndroid Build Coastguard Worker
98*cf5a6c84SAndroid Build Coastguard Worker // Read in next chunk from end of file
99*cf5a6c84SAndroid Build Coastguard Worker if (chunk>pos) chunk = pos;
100*cf5a6c84SAndroid Build Coastguard Worker pos -= chunk;
101*cf5a6c84SAndroid Build Coastguard Worker if (pos != lseek(fd, pos, SEEK_SET)) {
102*cf5a6c84SAndroid Build Coastguard Worker perror_msg("seek failed");
103*cf5a6c84SAndroid Build Coastguard Worker break;
104*cf5a6c84SAndroid Build Coastguard Worker }
105*cf5a6c84SAndroid Build Coastguard Worker if (!(temp = read_chunk(fd, chunk))) break;
106*cf5a6c84SAndroid Build Coastguard Worker temp->next = list;
107*cf5a6c84SAndroid Build Coastguard Worker list = temp;
108*cf5a6c84SAndroid Build Coastguard Worker
109*cf5a6c84SAndroid Build Coastguard Worker // Count newlines in this chunk.
110*cf5a6c84SAndroid Build Coastguard Worker offset = list->len;
111*cf5a6c84SAndroid Build Coastguard Worker while (offset--) {
112*cf5a6c84SAndroid Build Coastguard Worker // If the last line ends with a newline, that one doesn't count.
113*cf5a6c84SAndroid Build Coastguard Worker if (!flag) flag++;
114*cf5a6c84SAndroid Build Coastguard Worker
115*cf5a6c84SAndroid Build Coastguard Worker // Start outputting data right after newline
116*cf5a6c84SAndroid Build Coastguard Worker else if (list->data[offset] == '\n' && !++lines) {
117*cf5a6c84SAndroid Build Coastguard Worker offset++;
118*cf5a6c84SAndroid Build Coastguard Worker list->data += offset;
119*cf5a6c84SAndroid Build Coastguard Worker list->len -= offset;
120*cf5a6c84SAndroid Build Coastguard Worker
121*cf5a6c84SAndroid Build Coastguard Worker break;
122*cf5a6c84SAndroid Build Coastguard Worker }
123*cf5a6c84SAndroid Build Coastguard Worker }
124*cf5a6c84SAndroid Build Coastguard Worker }
125*cf5a6c84SAndroid Build Coastguard Worker
126*cf5a6c84SAndroid Build Coastguard Worker // Output stored data
127*cf5a6c84SAndroid Build Coastguard Worker llist_traverse(list, write_chunk);
128*cf5a6c84SAndroid Build Coastguard Worker
129*cf5a6c84SAndroid Build Coastguard Worker // In case of -f
130*cf5a6c84SAndroid Build Coastguard Worker lseek(fd, bytes, SEEK_SET);
131*cf5a6c84SAndroid Build Coastguard Worker return 1;
132*cf5a6c84SAndroid Build Coastguard Worker }
133*cf5a6c84SAndroid Build Coastguard Worker
134*cf5a6c84SAndroid Build Coastguard Worker // For -f and -F
tail_continue()135*cf5a6c84SAndroid Build Coastguard Worker static void tail_continue()
136*cf5a6c84SAndroid Build Coastguard Worker {
137*cf5a6c84SAndroid Build Coastguard Worker long long pos;
138*cf5a6c84SAndroid Build Coastguard Worker char *path;
139*cf5a6c84SAndroid Build Coastguard Worker struct stat sb;
140*cf5a6c84SAndroid Build Coastguard Worker int i = 0, fd, len;
141*cf5a6c84SAndroid Build Coastguard Worker
142*cf5a6c84SAndroid Build Coastguard Worker for (i = 0; ; i++) {
143*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(f)) fd = xnotify_wait(TT.not, &path);
144*cf5a6c84SAndroid Build Coastguard Worker else {
145*cf5a6c84SAndroid Build Coastguard Worker if (i == TT.file_no) {
146*cf5a6c84SAndroid Build Coastguard Worker i = 0;
147*cf5a6c84SAndroid Build Coastguard Worker msleep(TT.ss);
148*cf5a6c84SAndroid Build Coastguard Worker }
149*cf5a6c84SAndroid Build Coastguard Worker fd = TT.F[i].fd;
150*cf5a6c84SAndroid Build Coastguard Worker path = TT.F[i].path;
151*cf5a6c84SAndroid Build Coastguard Worker
152*cf5a6c84SAndroid Build Coastguard Worker if (stat(TT.F[i].path, &sb)) {
153*cf5a6c84SAndroid Build Coastguard Worker if (fd >= 0) {
154*cf5a6c84SAndroid Build Coastguard Worker close(fd);
155*cf5a6c84SAndroid Build Coastguard Worker TT.F[i].fd = -1;
156*cf5a6c84SAndroid Build Coastguard Worker error_msg("file inaccessible: %s\n", TT.F[i].path);
157*cf5a6c84SAndroid Build Coastguard Worker }
158*cf5a6c84SAndroid Build Coastguard Worker continue;
159*cf5a6c84SAndroid Build Coastguard Worker }
160*cf5a6c84SAndroid Build Coastguard Worker
161*cf5a6c84SAndroid Build Coastguard Worker if (fd<0 || !same_dev_ino(&sb, &TT.F[i].di)) {
162*cf5a6c84SAndroid Build Coastguard Worker if (fd>=0) close(fd);
163*cf5a6c84SAndroid Build Coastguard Worker if (-1 == (TT.F[i].fd = fd = open(path, O_RDONLY))) continue;
164*cf5a6c84SAndroid Build Coastguard Worker error_msg("following new file: %s\n", path);
165*cf5a6c84SAndroid Build Coastguard Worker TT.F[i].di.dev = sb.st_dev;
166*cf5a6c84SAndroid Build Coastguard Worker TT.F[i].di.ino = sb.st_ino;
167*cf5a6c84SAndroid Build Coastguard Worker } else if (sb.st_size <= (pos = lseek(fd, 0, SEEK_CUR))) {
168*cf5a6c84SAndroid Build Coastguard Worker if (pos == sb.st_size) continue;
169*cf5a6c84SAndroid Build Coastguard Worker error_msg("file truncated: %s\n", path);
170*cf5a6c84SAndroid Build Coastguard Worker lseek(fd, 0, SEEK_SET);
171*cf5a6c84SAndroid Build Coastguard Worker }
172*cf5a6c84SAndroid Build Coastguard Worker }
173*cf5a6c84SAndroid Build Coastguard Worker
174*cf5a6c84SAndroid Build Coastguard Worker while ((len = read(fd, toybuf, sizeof(toybuf)))>0) {
175*cf5a6c84SAndroid Build Coastguard Worker if (TT.file_no>1 && TT.last_fd != fd) {
176*cf5a6c84SAndroid Build Coastguard Worker TT.last_fd = fd;
177*cf5a6c84SAndroid Build Coastguard Worker xprintf("\n==> %s <==\n", path);
178*cf5a6c84SAndroid Build Coastguard Worker }
179*cf5a6c84SAndroid Build Coastguard Worker xwrite(1, toybuf, len);
180*cf5a6c84SAndroid Build Coastguard Worker }
181*cf5a6c84SAndroid Build Coastguard Worker }
182*cf5a6c84SAndroid Build Coastguard Worker }
183*cf5a6c84SAndroid Build Coastguard Worker
184*cf5a6c84SAndroid Build Coastguard Worker // Called for each file listed on command line, and/or stdin
do_tail(int fd,char * name)185*cf5a6c84SAndroid Build Coastguard Worker static void do_tail(int fd, char *name)
186*cf5a6c84SAndroid Build Coastguard Worker {
187*cf5a6c84SAndroid Build Coastguard Worker long bytes = TT.c, lines = TT.n;
188*cf5a6c84SAndroid Build Coastguard Worker int linepop = 1;
189*cf5a6c84SAndroid Build Coastguard Worker
190*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(F)) {
191*cf5a6c84SAndroid Build Coastguard Worker if (!fd) perror_exit("no -F with '-'");
192*cf5a6c84SAndroid Build Coastguard Worker } else if (fd == -1) return;
193*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(f) || FLAG(F)) {
194*cf5a6c84SAndroid Build Coastguard Worker char *s = name;
195*cf5a6c84SAndroid Build Coastguard Worker struct stat sb;
196*cf5a6c84SAndroid Build Coastguard Worker
197*cf5a6c84SAndroid Build Coastguard Worker if (!fd) sprintf(s = toybuf, "/proc/self/fd/%d", fd);
198*cf5a6c84SAndroid Build Coastguard Worker
199*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(f)) xnotify_add(TT.not, fd, s);
200*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(F)) {
201*cf5a6c84SAndroid Build Coastguard Worker if (fd != -1) {
202*cf5a6c84SAndroid Build Coastguard Worker if (fstat(fd, &sb)) perror_exit("%s", name);
203*cf5a6c84SAndroid Build Coastguard Worker TT.F[TT.file_no].di.dev = sb.st_dev;
204*cf5a6c84SAndroid Build Coastguard Worker TT.F[TT.file_no].di.ino = sb.st_ino;
205*cf5a6c84SAndroid Build Coastguard Worker }
206*cf5a6c84SAndroid Build Coastguard Worker TT.F[TT.file_no].fd = fd;
207*cf5a6c84SAndroid Build Coastguard Worker TT.F[TT.file_no].path = s;
208*cf5a6c84SAndroid Build Coastguard Worker }
209*cf5a6c84SAndroid Build Coastguard Worker }
210*cf5a6c84SAndroid Build Coastguard Worker
211*cf5a6c84SAndroid Build Coastguard Worker if (TT.file_no++) xputc('\n');
212*cf5a6c84SAndroid Build Coastguard Worker TT.last_fd = fd;
213*cf5a6c84SAndroid Build Coastguard Worker if (toys.optc > 1) xprintf("==> %s <==\n", name);
214*cf5a6c84SAndroid Build Coastguard Worker
215*cf5a6c84SAndroid Build Coastguard Worker // Are we measuring from the end of the file?
216*cf5a6c84SAndroid Build Coastguard Worker
217*cf5a6c84SAndroid Build Coastguard Worker if (bytes<0 || lines<0) {
218*cf5a6c84SAndroid Build Coastguard Worker struct line_list *list = 0, *new;
219*cf5a6c84SAndroid Build Coastguard Worker
220*cf5a6c84SAndroid Build Coastguard Worker // The slow codepath is always needed, and can handle all input,
221*cf5a6c84SAndroid Build Coastguard Worker // so make lseek support optional.
222*cf5a6c84SAndroid Build Coastguard Worker if (try_lseek(fd, bytes, lines)) return;
223*cf5a6c84SAndroid Build Coastguard Worker
224*cf5a6c84SAndroid Build Coastguard Worker // Read data until we run out, keep a trailing buffer
225*cf5a6c84SAndroid Build Coastguard Worker for (;;) {
226*cf5a6c84SAndroid Build Coastguard Worker // Read next page of data, appending to linked list in order
227*cf5a6c84SAndroid Build Coastguard Worker if (!(new = read_chunk(fd, sizeof(toybuf)))) break;
228*cf5a6c84SAndroid Build Coastguard Worker dlist_add_nomalloc((void *)&list, (void *)new);
229*cf5a6c84SAndroid Build Coastguard Worker
230*cf5a6c84SAndroid Build Coastguard Worker // If tracing bytes, add until we have enough, discarding overflow.
231*cf5a6c84SAndroid Build Coastguard Worker if (TT.c) {
232*cf5a6c84SAndroid Build Coastguard Worker bytes += new->len;
233*cf5a6c84SAndroid Build Coastguard Worker if (bytes > 0) {
234*cf5a6c84SAndroid Build Coastguard Worker while (list->len <= bytes) {
235*cf5a6c84SAndroid Build Coastguard Worker bytes -= list->len;
236*cf5a6c84SAndroid Build Coastguard Worker free(dlist_pop(&list));
237*cf5a6c84SAndroid Build Coastguard Worker }
238*cf5a6c84SAndroid Build Coastguard Worker list->data += bytes;
239*cf5a6c84SAndroid Build Coastguard Worker list->len -= bytes;
240*cf5a6c84SAndroid Build Coastguard Worker bytes = 0;
241*cf5a6c84SAndroid Build Coastguard Worker }
242*cf5a6c84SAndroid Build Coastguard Worker } else {
243*cf5a6c84SAndroid Build Coastguard Worker int len = new->len, count;
244*cf5a6c84SAndroid Build Coastguard Worker char *try = new->data;
245*cf5a6c84SAndroid Build Coastguard Worker
246*cf5a6c84SAndroid Build Coastguard Worker // First character _after_ a newline starts a new line, which
247*cf5a6c84SAndroid Build Coastguard Worker // works even if file doesn't end with a newline
248*cf5a6c84SAndroid Build Coastguard Worker for (count=0; count<len; count++) {
249*cf5a6c84SAndroid Build Coastguard Worker if (linepop) lines++;
250*cf5a6c84SAndroid Build Coastguard Worker linepop = try[count] == '\n';
251*cf5a6c84SAndroid Build Coastguard Worker
252*cf5a6c84SAndroid Build Coastguard Worker if (lines > 0) {
253*cf5a6c84SAndroid Build Coastguard Worker char c;
254*cf5a6c84SAndroid Build Coastguard Worker
255*cf5a6c84SAndroid Build Coastguard Worker do {
256*cf5a6c84SAndroid Build Coastguard Worker c = *list->data;
257*cf5a6c84SAndroid Build Coastguard Worker if (!--(list->len)) free(dlist_pop(&list));
258*cf5a6c84SAndroid Build Coastguard Worker else list->data++;
259*cf5a6c84SAndroid Build Coastguard Worker } while (c != '\n');
260*cf5a6c84SAndroid Build Coastguard Worker lines--;
261*cf5a6c84SAndroid Build Coastguard Worker }
262*cf5a6c84SAndroid Build Coastguard Worker }
263*cf5a6c84SAndroid Build Coastguard Worker }
264*cf5a6c84SAndroid Build Coastguard Worker }
265*cf5a6c84SAndroid Build Coastguard Worker
266*cf5a6c84SAndroid Build Coastguard Worker // Output/free the buffer.
267*cf5a6c84SAndroid Build Coastguard Worker llist_traverse(list, write_chunk);
268*cf5a6c84SAndroid Build Coastguard Worker
269*cf5a6c84SAndroid Build Coastguard Worker // Measuring from the beginning of the file.
270*cf5a6c84SAndroid Build Coastguard Worker } else for (;;) {
271*cf5a6c84SAndroid Build Coastguard Worker int len, offset = 0;
272*cf5a6c84SAndroid Build Coastguard Worker
273*cf5a6c84SAndroid Build Coastguard Worker // Error while reading does not exit. Error writing does.
274*cf5a6c84SAndroid Build Coastguard Worker len = read(fd, toybuf, sizeof(toybuf));
275*cf5a6c84SAndroid Build Coastguard Worker if (len<1) break;
276*cf5a6c84SAndroid Build Coastguard Worker while (bytes > 1 || lines > 1) {
277*cf5a6c84SAndroid Build Coastguard Worker bytes--;
278*cf5a6c84SAndroid Build Coastguard Worker if (toybuf[offset++] == '\n') lines--;
279*cf5a6c84SAndroid Build Coastguard Worker if (offset >= len) break;
280*cf5a6c84SAndroid Build Coastguard Worker }
281*cf5a6c84SAndroid Build Coastguard Worker if (offset<len) xwrite(1, toybuf+offset, len-offset);
282*cf5a6c84SAndroid Build Coastguard Worker }
283*cf5a6c84SAndroid Build Coastguard Worker }
284*cf5a6c84SAndroid Build Coastguard Worker
tail_main(void)285*cf5a6c84SAndroid Build Coastguard Worker void tail_main(void)
286*cf5a6c84SAndroid Build Coastguard Worker {
287*cf5a6c84SAndroid Build Coastguard Worker char **args = toys.optargs;
288*cf5a6c84SAndroid Build Coastguard Worker
289*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(n) && !FLAG(c)) {
290*cf5a6c84SAndroid Build Coastguard Worker char *arg = *args;
291*cf5a6c84SAndroid Build Coastguard Worker
292*cf5a6c84SAndroid Build Coastguard Worker // handle old "-42" / "+42" style arguments, else default to last 10 lines
293*cf5a6c84SAndroid Build Coastguard Worker if (arg && (*arg == '-' || *arg == '+') && arg[1]) {
294*cf5a6c84SAndroid Build Coastguard Worker TT.n = atolx(*(args++));
295*cf5a6c84SAndroid Build Coastguard Worker toys.optc--;
296*cf5a6c84SAndroid Build Coastguard Worker } else TT.n = -10;
297*cf5a6c84SAndroid Build Coastguard Worker }
298*cf5a6c84SAndroid Build Coastguard Worker
299*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(F)) TT.F = xzalloc(toys.optc*sizeof(*TT.F));
300*cf5a6c84SAndroid Build Coastguard Worker else if (FLAG(f)) TT.not = xnotify_init(toys.optc);
301*cf5a6c84SAndroid Build Coastguard Worker TT.ss = TT.s ? xparsemillitime(TT.s) : 1000;
302*cf5a6c84SAndroid Build Coastguard Worker
303*cf5a6c84SAndroid Build Coastguard Worker loopfiles_rw(args,
304*cf5a6c84SAndroid Build Coastguard Worker O_RDONLY|WARN_ONLY|LOOPFILES_ANYWAY|O_CLOEXEC*!(FLAG(f) || FLAG(F)),
305*cf5a6c84SAndroid Build Coastguard Worker 0, do_tail);
306*cf5a6c84SAndroid Build Coastguard Worker
307*cf5a6c84SAndroid Build Coastguard Worker // Wait for more data when following files
308*cf5a6c84SAndroid Build Coastguard Worker if (TT.file_no && (FLAG(F) || FLAG(f))) tail_continue();
309*cf5a6c84SAndroid Build Coastguard Worker }
310