diff options
Diffstat (limited to 'command/tail.c')
-rw-r--r-- | command/tail.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/command/tail.c b/command/tail.c new file mode 100644 index 0000000..8137eca --- /dev/null +++ b/command/tail.c @@ -0,0 +1,243 @@ +#include "command.h" +#include "lslib.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static struct { + bool lines; + int count; + bool print_headers; + bool dont_print_headers; + bool print_as_grow; + int grow_wait; +} flags; + +static size_t tail_file_lines(FILE* file, unsigned int count, size_t skip) { + + char** ring; + int* ring_len; + int index, read; + unsigned int size, i; + size_t len; + char* line; + + ring = malloc(sizeof(char*) * count); + memset(ring, 0, sizeof(char*) * count); + + ring_len = malloc(sizeof(int) * count); + + index = 0; + size = 0; + + fseek(file, skip, SEEK_SET); + + len = skip; + line = NULL; + + while ((read = getline(&line, &len, file)) != -1) { + + if (ring[index] != NULL) free(ring[index]); + ring[index] = line; + ring_len[index] = read; + + index++; + index %= count; + if (size < count) size++; + + line = NULL; + } + + index += count - size; + index %= count; + + for (i = 0; i < size; i++) { + fwrite(ring[index], ring_len[index], 1, stdout); + free(ring[index]); + index += 1; + index %= count; + } + + free(line); + fclose(file); + free(ring); + free(ring_len); + + return len; +} + +static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) { + + char* ring; + int index; + unsigned int size, i; + int read, c; + + ring = malloc(sizeof(char) * count); + memset(ring, 0, count); + + index = 0; + size = 0; + + fseek(file, skip, SEEK_SET); + read = skip; + + while((c = getc(file)) != EOF) { + ring[index] = c; + index++; + read++; + index %= count; + if (size < count) size++; + } + + index += count - size; + index %= count; + + for (i = 0; i < size; i++) { + putchar(ring[index]); + index += 1; + index %= count; + } + + fclose(file); + + return read; +} + +static void help(void) { + printf("Usage: tail [OPTIONS] [FILE]...\n\n"); + printf("Print last 10 lines of FILEs (or stdin) to.\n"); + printf("With more than one FILE, precede each with a filename header.\n\n"); + printf("\t-c [+]N[bkm]\tPrint last N bytes\n"); + printf("\t-n N[bkm]\tPrint last N lines\n"); + printf("\t\t\t(b:*512 k:*1024 m:*1024^2)\n"); + printf("\t-q\t\tNever print headers\n"); + printf("\t-v\t\tAlways print headers\n"); + printf("\t-f\t\tPrint data as file grows\n"); + printf("\t-s SECONDS\tWait SECONDS between reads with -f\n"); + exit(EXIT_SUCCESS); +} + +static void print_header(char* path, bool many) { + if (flags.dont_print_headers) return; + if (!many && !flags.print_headers) return; + if (streql("-", path)) { + printf("\n==> standard input <==\n"); + } else { + printf("\n=>> %s <==\n", path); + } +} + +static void tail_file(char* path, bool many) { + + FILE* file; + size_t skip; + + file = get_file(path, "r"); + print_header(path, many); + + skip = 0; + while (true) { + if (flags.lines) { + skip = tail_file_lines(file, flags.count, skip); + } else { + skip = tail_file_chars(file, flags.count, skip); + } + if (!flags.print_as_grow) break; + sleep(flags.grow_wait); + get_file(path, "r"); + }; +} + +static int short_arg(char c, char* next) { + switch (c) { + case 'c': { + long int bkm; + + flags.lines = false; + + check_arg(next); + bkm = get_blkm(next); + + if (bkm < 1) { + error("bkm cannot be less than 1"); + } + + flags.count = bkm; + return ARG_USED; + } + case 'n': { + long int bkm; + + flags.lines = true; + + check_arg(next); + bkm = get_blkm(next); + + if (bkm < 1) { + error("bkm cannot be less than 1"); + } + + flags.count = bkm; + return ARG_USED; + } + case 'q': + flags.dont_print_headers = true; + break; + case 'v': + flags.print_headers = true; + break; + case 'f': + flags.print_as_grow = true; + break; + case 's': { + long int sec; + + check_arg(next); + sec = get_number(next); + + if (sec < 1) { + error("wait seconds cannot be less than 1"); + } + + flags.grow_wait = sec; + return ARG_USED; + } + default: + return ARG_INVALID; + } + return ARG_UNUSED; +} + +COMMAND(tail) { + + int start, count, i; + + flags.count = 10; + flags.dont_print_headers = false; + flags.print_headers = false; + flags.lines = true; + flags.print_as_grow = false; + flags.grow_wait = 10; + + start = parse_args(argc, argv, help, short_arg, NULL); + + count = argc - start; + + if (count < 1) { + tail_file_lines(stdin, 10, 0); + return EXIT_SUCCESS; + } + + if (count == 1) { + tail_file(argv[start], false); + return EXIT_SUCCESS; + } + + for (i = 0; i < count; i++) { + tail_file(argv[start + i], true); + } + + return EXIT_SUCCESS; +} |