/* vi: set sw=4 ts=4: * * tail.c - copy last lines from input to stdout. * * Copyright 2012 Timothy Elliott * * See http://www.opengroup.org/onlinepubs/009695399/utilities/tail.html USE_TAIL(NEWTOY(tail, "c-|fn-|", TOYFLAG_BIN)) config TAIL bool "tail" default n help usage: tail [-n|c number] [-f] [file...] Copy last lines from files to stdout. If no files listed, copy from stdin. Filename "-" is a synonym for stdin. -n output the last X lines (default 10), +X counts from start. -c output the last X bytes, +X counts from start -f follow file, waiting for more data to be appended */ #include "toys.h" DEFINE_GLOBALS( long lines; long bytes; int file_no; ) #define TT this.tail #define FLAG_n 1 #define FLAG_f 2 #define FLAG_c 4 struct line_list { struct line_list *next; char *data; ssize_t len; long lines; }; static void print_after_offset(int fd, long bytes, long lines) { ssize_t read_len; long size=sizeof(toybuf); char c; while (bytes > 0 || lines > 0) { if (1>read(fd, &c, 1)) break; bytes--; if (c == '\n') lines--; } for (;;) { read_len = xread(fd, toybuf, size); if (read_len<1) break; xwrite(1, toybuf, read_len); } } static void print_last_bytes(int fd, long bytes) { char *buf1, *buf2, *temp; ssize_t read_len; buf1 = xmalloc(bytes); buf2 = xmalloc(bytes); for(;;) { // swap buf1 and buf2 temp = buf1; buf1 = buf2; buf2 = temp; read_len = readall(fd, buf2, bytes); if (read_lendata); free(list); } static void llist_add(struct line_list **head, struct line_list *new) { struct line_list *cur = *head; new->next = NULL; if (cur) { while (cur->next) cur = cur->next; cur->next = new; } else *head = new; } static void print_last_lines(int fd, long lines) { ssize_t i, total=0; long size=sizeof(toybuf); struct line_list *buf=NULL, *cur; for (;;) { // read from input and append to buffer list cur = xzalloc(sizeof(struct line_list)); cur->data = xmalloc(size); cur->len = readall(fd, cur->data, size); llist_add(&buf, cur); // count newlines in latest input for (i=0; ilen; i++) if (cur->data[i] == '\n') cur->lines++; total += cur->lines; // release first buffers if it leaves us enough newlines while (total - buf->lines > lines) { total -= buf->lines; free_line(llist_pop(&buf)); } if (cur->len < size) break; } // if last buffer doesn't end in a newline, pretend like it did if (cur->data[cur->len - 1]!='\n') total++; // print out part of the first buffer i = 0; while (total>lines) if (buf->data[i++]=='\n') total--; xwrite(1, buf->data + i, buf->len - i); // print remaining buffers in their entirety for (cur=buf->next; cur; cur=cur->next) xwrite(1, cur->data, cur->len); if (CFG_TOYBOX_FREE) llist_free(buf, free_line); } static void do_tail(int fd, char *name) { long lines=TT.lines, bytes=TT.bytes; if (toys.optc > 1) { // print an extra newline for all but the first file if (TT.file_no++) xputc('\n'); xprintf("==> %s <==\n", name); } if (lines > 0 || bytes > 0) print_after_offset(fd, bytes, lines); else if (bytes < 0) print_last_bytes(fd, bytes * -1); else print_last_lines(fd, lines * -1); } void tail_main(void) { // if nothing specified, default -n to -10 if (!(toys.optflags&(FLAG_n|FLAG_c))) TT.lines = -10; loopfiles(toys.optargs, do_tail); }