diff --git a/readme.md b/readme.md index 9cb9dc4..a594b66 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`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc`, `xargs` +`dd`, `cat`, `yes`, `echo`, `printf`, `id`, `groups`, `ls`, `tail`, `head`, `ed`, `tee`, `true`, `false`, `tee`, `whoami`, `wc`, `xargs`, `tac` ## How to diff --git a/src/command.h b/src/command.h index e596847..a73e3c8 100644 --- a/src/command.h +++ b/src/command.h @@ -1,4 +1,5 @@ #include "util/shared.h" +#include "util/stack.h" #include #include @@ -31,3 +32,4 @@ COMMAND(tee); COMMAND(whoami); COMMAND(wc); COMMAND(xargs); +COMMAND(tac); diff --git a/src/commands/ls.c b/src/commands/ls.c index ce7206c..3aad1a1 100644 --- a/src/commands/ls.c +++ b/src/commands/ls.c @@ -9,11 +9,12 @@ #define FILE_COLOR ANSCII BLACK COLOR #define DIR_COLOR ANSCII BOLD NEXT NORMAL BLUE COLOR #define DIR_COLOR_EXEC ANSCII BACKGROUND GREEN NEXT NORMAL BLACK COLOR -#define LINK_COLOR ANSCII BOLD NEXT HIGHLIGHT TURQUOISE COLOR +#define LINK_COLOR ANSCII BOLD NEXT NORMAL TURQUOISE COLOR #define SET_UID_COLOR ANSCII BACKGROUND RED NEXT NORMAL WHITE COLOR #define SET_GID_COLOR ANSCII BACKGROUND YELLOW NEXT NORMAL BLACK COLOR #define EXEC_COLOR ANSCII BOLD NEXT NORMAL GREEN COLOR #define BLK_COLOR ANSCII BOLD NEXT NORMAL YELLOW COLOR +#define SOCK_COLOR ANSCII BOLD NEXT NORMAL MAGENTA COLOR static struct { bool hidden; @@ -32,6 +33,7 @@ struct FileInfo { char mode[11]; char size[5]; int links; + int bytes; bool set_uid; bool set_gid; bool exec; @@ -45,6 +47,7 @@ struct FileListInfo { int max_size; int max_name; int total_len; + int total_size; }; static DIR* get_directory(char* path) { @@ -173,6 +176,9 @@ static bool get_file_info(const char* file_path, const char* dir_path, struct Fi print_file_size(s.st_size, info->size); print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000000, info->date); + + info->bytes = (s.st_size + s.st_blksize - 1) / s.st_blksize * s.st_blksize; + return true; } @@ -186,10 +192,11 @@ static char* get_file_color(struct FileInfo* info) { } } else if (info->type == DT_LNK) { color = LINK_COLOR; + } else if (info->type == DT_SOCK) { + color = SOCK_COLOR; } else if ( info->type == DT_CHR || - info->type == DT_BLK || - info->type == DT_SOCK + info->type == DT_BLK ) { color = BLK_COLOR; } else { @@ -207,6 +214,13 @@ static char* get_file_color(struct FileInfo* info) { } static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info, const char* dir_path) { + + if (flags.more_info) { + char total[13]; + print_file_size(info.total_size, total); + printf("total %s\n", total); + } + struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); @@ -316,6 +330,7 @@ static void push_file( if (link_len > info->max_link) info->max_link = link_len; info->total_len += name_len + 2; + info->total_size += finfo.bytes; (*files)[*size] = finfo; (*size)++; @@ -328,7 +343,6 @@ static bool recurse_directory(char* path) { d = get_directory(path); if (d == NULL) return false; - int capacity = 8; int size = 0; @@ -337,9 +351,9 @@ static bool recurse_directory(char* path) { memset(&info, 0, sizeof(struct FileListInfo)); while((file = readdir(d)) != NULL) { - if (file->d_type == DT_DIR) continue; + if (file->d_type == DT_DIR && !is_dot_dir(file->d_name)) continue; if (!flags.hidden && prefix(".", file->d_name)) continue; - if (is_dot_dir(file->d_name)) continue; + if (flags.hide_dot && is_dot_dir(file->d_name)) continue; if (first) { if (flags.colored == NO) printf("\n%s:\n", path); @@ -417,7 +431,7 @@ static void list_file_args(int start, int argc, char** argv) { for (int i = start; i < argc; i++) { if (is_dir(argv[i])) continue; - push_file((struct FileInfo**) &files, &info, &size, &capacity, argv[i], "."); + push_file((struct FileInfo**) &files, &info, &size, &capacity, argv[i], argv[i][0] == '/' ? "" : "."); } if (size > 0) list_files(files, size, info, "."); @@ -506,8 +520,7 @@ COMMAND(ls) { else printf("\n%s:\n", argv[i]); } - if (list_directory(argv[i]) && i + 1 != argc) - if (titled && !flags.recurse) printf("\n"); + list_directory(argv[i]); } return EXIT_SUCCESS; diff --git a/src/commands/tac.c b/src/commands/tac.c new file mode 100644 index 0000000..fbd0f74 --- /dev/null +++ b/src/commands/tac.c @@ -0,0 +1,105 @@ +#include "../command.h" + +#include +#include +#include + +static void help(void) { + printf("Usage: tac [FILE]...\n\n"); + printf("Concatenate FILEs and print them in reverse\n"); +} + +static void print_range(FILE* file, int start, int end) { + int len = end - start; + fseek(file, start, SEEK_SET); + for (int i = 0; i < len; i++) { + putchar(getc(file)); + } + fflush(stdout); +} + +static char stdin_path[PATH_MAX]; + +static FILE* read_stdin() { + static bool read; + static FILE* file; + + if (read) goto finished; + read = true; + + srand(time(NULL)); + int r = rand() % 1000000; + sprintf(stdin_path, "/tmp/%d.tac", r); + file = get_file(stdin_path, "w"); + + char c; + while((c = getchar()) != EOF) putc(c, file); + fclose(file); + + file = get_file(stdin_path, "r"); + +finished: + return file; +} + +static void parse_file(FILE* file, struct Stack* stack) { + char buf[1024]; + int read; + int total = 1; + + stack_push_int(stack, 0); + rewind(file); + while ((read = fread(buf, 1, 1024, file)) > 0) { + for (int i = 0; i < read; i++) { + char c = buf[i]; + if (c != '\n') continue; + stack_push_int(stack, total + i); + } + total += read; + } +} + +static void tac_file(FILE* file) { + struct Stack stack; + stack_init(&stack, 80); + + parse_file(file, &stack); + + rewind(file); + int last, current; + if (!stack_pop_int(&stack, &last)) goto cleanup; + while(stack_pop_int(&stack, ¤t)) { + print_range(file, current, last); + last = current; + } + +cleanup: + stack_free(&stack); +} + +COMMAND(tac) { + + parse_help(argc, argv, help); + + FILE* in = read_stdin(); + + if (argc < 1) { + tac_file(in); + return EXIT_SUCCESS; + } + + for (int i = 0; i < argc; i++) { + FILE* file = get_file(argv[i], "r"); + if (file == stdin) { + tac_file(in); + } else { + tac_file(file); + fclose(file); + } + } + + fclose(in); + remove(stdin_path); + + return EXIT_SUCCESS; +} diff --git a/src/main.c b/src/main.c index 35899e2..7bd931e 100644 --- a/src/main.c +++ b/src/main.c @@ -21,7 +21,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, tail, head, ed, tee, true, false, tee, whoami, wc, xargs\n"); + printf("\tdd, cat, yes, echo, printf, id, groups, ls, tail, head, ed, tee, true, false, tee, whoami, wc, xargs, tac\n"); return EXIT_SUCCESS; } argc--; @@ -70,6 +70,8 @@ int main (ARGUMENTS) { return wc(NEXT_ARGS); } else if (streql(cmd, "xargs")) { return xargs(NEXT_ARGS); + } else if (streql(cmd, "tac")) { + return tac(NEXT_ARGS); } else { error("error: invalid command %s", cmd); } diff --git a/src/util/shared.c b/src/util/shared.c index e207c6d..3e6fca3 100644 --- a/src/util/shared.c +++ b/src/util/shared.c @@ -20,6 +20,11 @@ void error(const char* format, ...) { FILE* get_file_s(const char* path, const char* type) { struct stat s; + if (streql("-", path) && type[0] == 'r') { + clearerr(stdin); + fflush(stdin); + return stdin; + } if (lstat(path, &s) < 0) { if (type[0] != 'r') goto read; fprintf(stderr, "error: failed to read %s: %s\n", path, strerror(errno)); @@ -40,10 +45,6 @@ read: } FILE* get_file(const char* path, const char* type) { - if (streql("-", path) && type[0] == 'r') { - clearerr(stdin); - return stdin; - } FILE* file = get_file_s(path, type); if (file == NULL) exit(EXIT_FAILURE); return file; diff --git a/src/util/stack.c b/src/util/stack.c new file mode 100644 index 0000000..15d5a8e --- /dev/null +++ b/src/util/stack.c @@ -0,0 +1,32 @@ +#include "stack.h" + +#include +#include +#include +#include + +void stack_init(struct Stack* stack, size_t size) { + stack->size = 0; + stack->capacity = size; + stack->data = malloc(size); +} + +void stack_push(struct Stack* stack, void* data, size_t len) { + size_t new_size = stack->size + len; + if (new_size >= stack->capacity) { + stack->capacity = new_size * 2; + stack->data = realloc(stack->data, stack->capacity); + } + memcpy((uint8_t*)stack->data + stack->size, data, len); + stack->size += len; +} + +void* stack_pop(struct Stack* stack, size_t len) { + if (stack->size < len) return NULL; + stack->size -= len; + return (uint8_t*)stack->data + stack->size; +} + +void stack_free(struct Stack *stack) { + free(stack->data); +} diff --git a/src/util/stack.h b/src/util/stack.h new file mode 100644 index 0000000..01a48e5 --- /dev/null +++ b/src/util/stack.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +struct Stack { + size_t size; + size_t capacity; + void* data; +}; + +void stack_init(struct Stack* stack, size_t size); +void stack_push(struct Stack* stack, void* data, size_t len); +void* stack_pop(struct Stack* stack, size_t len); +void stack_free(struct Stack* stack); + +inline void stack_push_int(struct Stack* stack, int value) { + stack_push(stack, &value, sizeof(int)); +} + +inline bool stack_pop_int(struct Stack* stack, int* value) { + void* d = stack_pop(stack, sizeof(int)); + if (d == NULL) return false; + *value = *(int*)(d); + return true; +}