summaryrefslogtreecommitdiff
path: root/src/commands/tail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands/tail.c')
-rw-r--r--src/commands/tail.c206
1 files changed, 175 insertions, 31 deletions
diff --git a/src/commands/tail.c b/src/commands/tail.c
index 04f7f42..24d7b1f 100644
--- a/src/commands/tail.c
+++ b/src/commands/tail.c
@@ -2,19 +2,31 @@
#include <stdio.h>
#include <string.h>
+#include <sys/select.h>
#include <sys/stat.h>
+#include <unistd.h>
-static void tail_file(FILE* file) {
- char* ring[10];
- memset(ring, 0, sizeof(char*) * 10);
+struct Flags {
+ bool lines;
+ int count;
+ bool print_headers;
+ bool dont_print_headers;
+ bool print_as_grow;
+ int grow_wait;
+};
- int ring_len[10];
+static size_t tail_file_lines(FILE* file, unsigned int count, size_t skip) {
+ char* ring[count];
+ memset(ring, 0, sizeof(char*) * count);
+
+ int ring_len[count];
int index = 0;
- int size = 0;
+ unsigned int size = 0;
int read;
- size_t len = 0;
+ fseek(file, skip, SEEK_SET);
+ size_t len = skip;
char* line = NULL;
while ((read = getline(&line, &len, file)) != -1) {
@@ -23,57 +35,189 @@ static void tail_file(FILE* file) {
ring_len[index] = read;
index++;
- index %= 10;
- if (size < 10) size++;
+ index %= count;
+ if (size < count) size++;
line = NULL;
}
- index += 10 - size;
- index %= 10;
+ index += count - size;
+ index %= count;
- for (int i = 0; i < size; i++) {
+ for (unsigned int i = 0; i < size; i++) {
fwrite(ring[index], ring_len[index], 1, stdout);
free(ring[index]);
index += 1;
- index %= 10;
+ index %= count;
}
free(line);
fclose(file);
+
+ return len;
+}
+
+static size_t tail_file_chars(FILE* file, unsigned int count, size_t skip) {
+ char ring[count];
+ memset(ring, 0, count);
+
+ int index = 0;
+ unsigned int size = 0;
+
+ fseek(file, skip, SEEK_SET);
+ int read = skip;
+ int c;
+ while((c = getc(file)) != EOF) {
+ ring[index] = c;
+ index++;
+ read++;
+ index %= count;
+ if (size < count) size++;
+ }
+
+ index += count - size;
+ index %= count;
+
+ for (unsigned int i = 0; i < size; i++) {
+ putchar(ring[index]);
+ index += 1;
+ index %= count;
+ }
+
+ fclose(file);
+
+ return read;
}
static void help() {
- printf("Usage: tail [FILE]...\n\n");
+ 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");
+ 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 char* next_arg(int argc, char** argv, int index) {
+ if (index >= argc) {
+ error("error: expected another argument after option");
+ }
+ return argv[index];
+}
+
+static void print_header(char* path, struct Flags* flags, 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, struct Flags* flags, bool many) {
+ FILE* file = get_file(path, "r");
+ print_header(path, flags, many);
+
+ size_t 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");
+ };
+}
+
COMMAND(tail) {
- if (argc < 1) {
- tail_file(stdin);
- return EXIT_SUCCESS;
+
+ struct Flags flags;
+ flags.count = 10;
+ flags.dont_print_headers = false;
+ flags.print_headers = false;
+ flags.lines = true;
+ flags.print_as_grow = false;
+ flags.grow_wait = 10;
+
+ int start = 0;
+ for (int i = 0; i < argc; i++) {
+ if (!prefix("-", argv[i])) break;
+ if (streql(argv[0], "--help")) help();
+ start++;
+ int i_s = i;
+ for (size_t j = 1; j < strlen(argv[i_s]); j++) {
+ char c = argv[i_s][j];
+ switch(c) {
+ case 'c': {
+ start++;
+ flags.lines = false;
+ char* arg = next_arg(argc, argv, ++i);
+ long int bkm = get_blkm(arg);
+ if (bkm < 1) {
+ error("error: bkm cannot be less than 1");
+ }
+ flags.count = bkm;
+ break;
+ }
+ case 'n': {
+ start++;
+ flags.lines = true;
+ char* arg = next_arg(argc, argv, ++i);
+ long int bkm = get_blkm(arg);
+ if (bkm < 1) {
+ error("error: bkm cannot be less than 1");
+ }
+ flags.count = bkm;
+ break;
+ }
+ 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': {
+ start++;
+ char* arg = next_arg(argc, argv, ++i);
+ long int sec = get_number(arg);
+ if (sec < 1) {
+ error("error: wait seconds cannot be less than 1");
+ }
+ flags.grow_wait = sec;
+ break;
+ }
+ default: {
+ error("error: unknown option -%c", c);
+ }
+ }
+ }
}
- if (streql(argv[0], "--help")) help();
+ int count = argc - start;
- if (argc == 1) {
- FILE* file = get_file(argv[0], "r");
- tail_file(file);
+ if (count < 1) {
+ tail_file_lines(stdin, 10, 0);
return EXIT_SUCCESS;
}
- for (int i = 0; i < argc; i++) {
- FILE* file;
- if (streql("-", argv[i])) {
- file = stdin;
- } else {
- file = get_file_s(argv[i], "r");
- if (file == NULL) continue;
- }
- printf("\n==> %s <==\n", argv[i]);
- tail_file(file);
+ if (count == 1) {
+ tail_file(argv[start], &flags, false);
+ return EXIT_SUCCESS;
+ }
+
+ for (int i = 0; i < count; i++) {
+ tail_file(argv[start + i], &flags, true);
}
return EXIT_SUCCESS;