view toys/tail.c @ 526:f435e8e3a0ba

Add mkdir.
author Georgi Chorbadzhiyski <georgi@unixsol.org>
date Mon, 05 Mar 2012 22:19:36 -0600
parents a497beb97eee
children c2f39708a4c4
line wrap: on
line source

/* vi: set sw=4 ts=4:
 *
 * tail.c - copy last lines from input to stdout.
 *
 * Copyright 2012 Timothy Elliott <tle@holymonkey.com>
 *
 * 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_len<bytes) break;
	}

	// output part of buf1 and all of buf2
	xwrite(1, buf1 + read_len, bytes - read_len);
	xwrite(1, buf2, read_len);

	if (CFG_TOYBOX_FREE) {
		free(buf1);
		free(buf2);
	}
}

static void free_line(void *data)
{
	struct line_list *list = (struct line_list *)data;
	free(list->data);
	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; i<cur->len; 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);
}