lazysphere/src/commands/tail.c

219 lines
5.6 KiB
C
Raw Normal View History

2023-04-28 19:13:10 +00:00
#include "../command.h"
2023-04-29 00:32:18 +00:00
struct Flags {
bool lines;
int count;
bool print_headers;
bool dont_print_headers;
bool print_as_grow;
int grow_wait;
};
2023-04-28 19:13:10 +00:00
2023-04-29 00:32:18 +00:00
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];
2023-04-28 19:13:10 +00:00
int index = 0;
2023-04-29 00:32:18 +00:00
unsigned int size = 0;
2023-04-28 19:13:10 +00:00
int read;
2023-04-29 00:32:18 +00:00
fseek(file, skip, SEEK_SET);
size_t len = skip;
2023-04-28 19:13:10 +00:00
char* line = NULL;
while ((read = getline(&line, &len, file)) != -1) {
if (ring[index] != NULL) free(ring[index]);
ring[index] = line;
ring_len[index] = read;
index++;
2023-04-29 00:32:18 +00:00
index %= count;
if (size < count) size++;
2023-04-28 19:13:10 +00:00
line = NULL;
}
2023-04-29 00:32:18 +00:00
index += count - size;
index %= count;
2023-04-28 19:13:10 +00:00
2023-04-29 00:32:18 +00:00
for (unsigned int i = 0; i < size; i++) {
2023-04-28 19:13:10 +00:00
fwrite(ring[index], ring_len[index], 1, stdout);
free(ring[index]);
index += 1;
2023-04-29 00:32:18 +00:00
index %= count;
2023-04-28 19:13:10 +00:00
}
free(line);
fclose(file);
2023-04-29 00:32:18 +00:00
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;
2023-04-28 19:13:10 +00:00
}
static void help() {
2023-04-29 00:32:18 +00:00
printf("Usage: tail [OPTIONS] [FILE]...\n\n");
2023-04-28 19:13:10 +00:00
printf("Print last 10 lines of FILEs (or stdin) to.\n");
2023-04-29 00:32:18 +00:00
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");
2023-04-28 19:13:10 +00:00
exit(EXIT_SUCCESS);
}
2023-04-29 00:32:18 +00:00
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");
};
}
2023-04-28 19:13:10 +00:00
COMMAND(tail) {
2023-04-29 00:32:18 +00:00
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);
}
}
}
2023-04-28 19:13:10 +00:00
}
2023-04-29 00:32:18 +00:00
int count = argc - start;
2023-04-28 19:13:10 +00:00
2023-04-29 00:32:18 +00:00
if (count < 1) {
tail_file_lines(stdin, 10, 0);
2023-04-28 19:13:10 +00:00
return EXIT_SUCCESS;
}
2023-04-29 00:32:18 +00:00
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);
2023-04-28 19:13:10 +00:00
}
return EXIT_SUCCESS;
}