diff --git a/readme.md b/readme.md index 3dfe6e5..3440299 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ A terrible busybox/gnu coreutils clone. Currently the only supported commands are: -`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls` +`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail` ## How to diff --git a/src/command.h b/src/command.h index 4a2c50b..939886d 100644 --- a/src/command.h +++ b/src/command.h @@ -17,3 +17,4 @@ COMMAND(print); COMMAND_EMPTY(groups); COMMAND_EMPTY(user_id); COMMAND(ls); +COMMAND(tail); diff --git a/src/commands/cat.c b/src/commands/cat.c index f5622e8..8e22706 100644 --- a/src/commands/cat.c +++ b/src/commands/cat.c @@ -1,7 +1,9 @@ #include "../command.h" #include +#include #include +#include struct Flags { bool number_lines; @@ -138,6 +140,15 @@ COMMAND(cat) { continue; } else { files = true; + struct stat s; + if (stat(argv[i], &s) < 0) { + printf("error: failed to read %s: %s\n", argv[i], strerror(errno)); + continue; + } + if (!S_ISREG(s.st_mode)) { + printf("error: %s is not a file\n", argv[i]); + continue; + } FILE* in = get_file(argv[i], "r"); cat_file(in, flags); fclose(in); diff --git a/src/commands/tail.c b/src/commands/tail.c new file mode 100644 index 0000000..04f7f42 --- /dev/null +++ b/src/commands/tail.c @@ -0,0 +1,80 @@ +#include "../command.h" + +#include +#include +#include + +static void tail_file(FILE* file) { + char* ring[10]; + memset(ring, 0, sizeof(char*) * 10); + + int ring_len[10]; + + int index = 0; + int size = 0; + + int read; + size_t len = 0; + 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++; + index %= 10; + if (size < 10) size++; + + line = NULL; + } + + index += 10 - size; + index %= 10; + + for (int i = 0; i < size; i++) { + fwrite(ring[index], ring_len[index], 1, stdout); + free(ring[index]); + index += 1; + index %= 10; + } + + free(line); + fclose(file); +} + +static void help() { + printf("Usage: tail [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"); + exit(EXIT_SUCCESS); +} + +COMMAND(tail) { + if (argc < 1) { + tail_file(stdin); + return EXIT_SUCCESS; + } + + if (streql(argv[0], "--help")) help(); + + if (argc == 1) { + FILE* file = get_file(argv[0], "r"); + tail_file(file); + 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); + } + + return EXIT_SUCCESS; +} diff --git a/src/main.c b/src/main.c index 63aa775..ce89210 100644 --- a/src/main.c +++ b/src/main.c @@ -20,7 +20,7 @@ int main (ARGUMENTS) { if (argc < 2) { printf("usage: lazysphere [function [arguments]...]\n\n"); printf("currently defined functions:\n"); - printf("\tdd, cat, yes, echo, printf, id, groups, ls\n"); + printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail\n"); return EXIT_SUCCESS; } argc--; @@ -50,6 +50,8 @@ int main (ARGUMENTS) { return user_id(); } else if (streql(cmd, "ls")) { return ls(NEXT_ARGS); + } else if (streql(cmd, "tail")) { + return tail(NEXT_ARGS); } else { error("error: invalid command %s", cmd); } diff --git a/src/shared.c b/src/shared.c index b167e6f..8d0dcca 100644 --- a/src/shared.c +++ b/src/shared.c @@ -5,6 +5,7 @@ #include #include #include +#include void error(const char* format, ...) { va_list list; @@ -15,14 +16,29 @@ void error(const char* format, ...) { exit(EXIT_FAILURE); } -FILE* get_file(const char* path, const char* type) { +FILE* get_file_s(const char* path, const char* type) { + struct stat s; + if (stat(path, &s) < 0) { + fprintf(stderr, "error: failed to read %s: %s\n", path, strerror(errno)); + return NULL; + } + if (!S_ISREG(s.st_mode)) { + fprintf(stderr, "error: %s is not a file\n", path); + return NULL; + } FILE* file = fopen(path, type); if (file == NULL) { - error("error: failed to open file %s: %s", path, strerror(errno)); + fprintf(stderr, "error: failed to open file %s: %s\n", path, strerror(errno)); } return file; } +FILE* get_file(const char* path, const char* type) { + FILE* file = get_file_s(path, type); + if (file == NULL) exit(EXIT_FAILURE); + return file; +} + long int get_number(const char* text) { char* end; long int n = strtol(text, &end, 10); diff --git a/src/shared.h b/src/shared.h index 4556d5d..251a993 100644 --- a/src/shared.h +++ b/src/shared.h @@ -7,6 +7,7 @@ __attribute__ ((__format__(printf, 1, 2))) void error(const char* format, ...); +FILE* get_file_s(const char* path, const char* type); FILE* get_file(const char* path, const char* type); long int get_number(const char* text);