tac, ls fixes
This commit is contained in:
parent
1af425e15d
commit
ab7109065c
8 changed files with 196 additions and 15 deletions
|
@ -4,7 +4,7 @@
|
||||||
A terrible busybox/gnu coreutils clone.
|
A terrible busybox/gnu coreutils clone.
|
||||||
|
|
||||||
Currently the only supported commands are:
|
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
|
## How to
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "util/shared.h"
|
#include "util/shared.h"
|
||||||
|
#include "util/stack.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -31,3 +32,4 @@ COMMAND(tee);
|
||||||
COMMAND(whoami);
|
COMMAND(whoami);
|
||||||
COMMAND(wc);
|
COMMAND(wc);
|
||||||
COMMAND(xargs);
|
COMMAND(xargs);
|
||||||
|
COMMAND(tac);
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
#define FILE_COLOR ANSCII BLACK COLOR
|
#define FILE_COLOR ANSCII BLACK COLOR
|
||||||
#define DIR_COLOR ANSCII BOLD NEXT NORMAL BLUE COLOR
|
#define DIR_COLOR ANSCII BOLD NEXT NORMAL BLUE COLOR
|
||||||
#define DIR_COLOR_EXEC ANSCII BACKGROUND GREEN NEXT NORMAL BLACK 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_UID_COLOR ANSCII BACKGROUND RED NEXT NORMAL WHITE COLOR
|
||||||
#define SET_GID_COLOR ANSCII BACKGROUND YELLOW NEXT NORMAL BLACK COLOR
|
#define SET_GID_COLOR ANSCII BACKGROUND YELLOW NEXT NORMAL BLACK COLOR
|
||||||
#define EXEC_COLOR ANSCII BOLD NEXT NORMAL GREEN COLOR
|
#define EXEC_COLOR ANSCII BOLD NEXT NORMAL GREEN COLOR
|
||||||
#define BLK_COLOR ANSCII BOLD NEXT NORMAL YELLOW COLOR
|
#define BLK_COLOR ANSCII BOLD NEXT NORMAL YELLOW COLOR
|
||||||
|
#define SOCK_COLOR ANSCII BOLD NEXT NORMAL MAGENTA COLOR
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
bool hidden;
|
bool hidden;
|
||||||
|
@ -32,6 +33,7 @@ struct FileInfo {
|
||||||
char mode[11];
|
char mode[11];
|
||||||
char size[5];
|
char size[5];
|
||||||
int links;
|
int links;
|
||||||
|
int bytes;
|
||||||
bool set_uid;
|
bool set_uid;
|
||||||
bool set_gid;
|
bool set_gid;
|
||||||
bool exec;
|
bool exec;
|
||||||
|
@ -45,6 +47,7 @@ struct FileListInfo {
|
||||||
int max_size;
|
int max_size;
|
||||||
int max_name;
|
int max_name;
|
||||||
int total_len;
|
int total_len;
|
||||||
|
int total_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
static DIR* get_directory(char* path) {
|
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_file_size(s.st_size, info->size);
|
||||||
print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000000, info->date);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,10 +192,11 @@ static char* get_file_color(struct FileInfo* info) {
|
||||||
}
|
}
|
||||||
} else if (info->type == DT_LNK) {
|
} else if (info->type == DT_LNK) {
|
||||||
color = LINK_COLOR;
|
color = LINK_COLOR;
|
||||||
|
} else if (info->type == DT_SOCK) {
|
||||||
|
color = SOCK_COLOR;
|
||||||
} else if (
|
} else if (
|
||||||
info->type == DT_CHR ||
|
info->type == DT_CHR ||
|
||||||
info->type == DT_BLK ||
|
info->type == DT_BLK
|
||||||
info->type == DT_SOCK
|
|
||||||
) {
|
) {
|
||||||
color = BLK_COLOR;
|
color = BLK_COLOR;
|
||||||
} else {
|
} 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) {
|
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;
|
struct winsize w;
|
||||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &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;
|
if (link_len > info->max_link) info->max_link = link_len;
|
||||||
|
|
||||||
info->total_len += name_len + 2;
|
info->total_len += name_len + 2;
|
||||||
|
info->total_size += finfo.bytes;
|
||||||
|
|
||||||
(*files)[*size] = finfo;
|
(*files)[*size] = finfo;
|
||||||
(*size)++;
|
(*size)++;
|
||||||
|
@ -328,7 +343,6 @@ static bool recurse_directory(char* path) {
|
||||||
|
|
||||||
d = get_directory(path);
|
d = get_directory(path);
|
||||||
if (d == NULL) return false;
|
if (d == NULL) return false;
|
||||||
|
|
||||||
|
|
||||||
int capacity = 8;
|
int capacity = 8;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
@ -337,9 +351,9 @@ static bool recurse_directory(char* path) {
|
||||||
memset(&info, 0, sizeof(struct FileListInfo));
|
memset(&info, 0, sizeof(struct FileListInfo));
|
||||||
|
|
||||||
while((file = readdir(d)) != NULL) {
|
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 (!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 (first) {
|
||||||
if (flags.colored == NO)
|
if (flags.colored == NO)
|
||||||
printf("\n%s:\n", path);
|
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++) {
|
for (int i = start; i < argc; i++) {
|
||||||
if (is_dir(argv[i])) continue;
|
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, ".");
|
if (size > 0) list_files(files, size, info, ".");
|
||||||
|
@ -506,8 +520,7 @@ COMMAND(ls) {
|
||||||
else
|
else
|
||||||
printf("\n%s:\n", argv[i]);
|
printf("\n%s:\n", argv[i]);
|
||||||
}
|
}
|
||||||
if (list_directory(argv[i]) && i + 1 != argc)
|
list_directory(argv[i]);
|
||||||
if (titled && !flags.recurse) printf("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
105
src/commands/tac.c
Normal file
105
src/commands/tac.c
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#include "../command.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ int main (ARGUMENTS) {
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
printf("usage: lazysphere [function [arguments]...]\n\n");
|
printf("usage: lazysphere [function [arguments]...]\n\n");
|
||||||
printf("currently defined functions:\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;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
argc--;
|
argc--;
|
||||||
|
@ -70,6 +70,8 @@ int main (ARGUMENTS) {
|
||||||
return wc(NEXT_ARGS);
|
return wc(NEXT_ARGS);
|
||||||
} else if (streql(cmd, "xargs")) {
|
} else if (streql(cmd, "xargs")) {
|
||||||
return xargs(NEXT_ARGS);
|
return xargs(NEXT_ARGS);
|
||||||
|
} else if (streql(cmd, "tac")) {
|
||||||
|
return tac(NEXT_ARGS);
|
||||||
} else {
|
} else {
|
||||||
error("error: invalid command %s", cmd);
|
error("error: invalid command %s", cmd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,11 @@ void error(const char* format, ...) {
|
||||||
|
|
||||||
FILE* get_file_s(const char* path, const char* type) {
|
FILE* get_file_s(const char* path, const char* type) {
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
if (streql("-", path) && type[0] == 'r') {
|
||||||
|
clearerr(stdin);
|
||||||
|
fflush(stdin);
|
||||||
|
return stdin;
|
||||||
|
}
|
||||||
if (lstat(path, &s) < 0) {
|
if (lstat(path, &s) < 0) {
|
||||||
if (type[0] != 'r') goto read;
|
if (type[0] != 'r') goto read;
|
||||||
fprintf(stderr, "error: failed to read %s: %s\n", path, strerror(errno));
|
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) {
|
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);
|
FILE* file = get_file_s(path, type);
|
||||||
if (file == NULL) exit(EXIT_FAILURE);
|
if (file == NULL) exit(EXIT_FAILURE);
|
||||||
return file;
|
return file;
|
||||||
|
|
32
src/util/stack.c
Normal file
32
src/util/stack.c
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include "stack.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
26
src/util/stack.h
Normal file
26
src/util/stack.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
Reference in a new issue