summaryrefslogtreecommitdiff
path: root/command/tail.c
diff options
context:
space:
mode:
Diffstat (limited to 'command/tail.c')
-rw-r--r--command/tail.c243
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;
+}