|
|
@@ -0,0 +1,412 @@ |
|
|
/* |
|
|
* ls |
|
|
*/ |
|
|
|
|
|
|
|
|
#include <sys/stat.h> |
|
|
#include <fcntl.h> |
|
|
#include <stdint.h> |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <syscall.h> |
|
|
#include <unistd.h> |
|
|
|
|
|
#define MIN_COL_SPACING 2 |
|
|
|
|
|
#define EXE_COLOR "1;32" |
|
|
#define DIR_COLOR "1;34" |
|
|
#define REG_COLOR "0" |
|
|
#define MEDIA_COLOR "" |
|
|
#define SYM_COLOR "" |
|
|
#define BROKEN_COLOR "1;" |
|
|
|
|
|
|
|
|
/* Shit that belongs as a separate data structure */ |
|
|
|
|
|
typedef struct node { |
|
|
struct node * next; |
|
|
struct node * prev; |
|
|
void * value; |
|
|
} __attribute__((packed)) node_t; |
|
|
|
|
|
typedef struct { |
|
|
node_t * head; |
|
|
node_t * tail; |
|
|
size_t length; |
|
|
} __attribute__((packed)) list_t; |
|
|
|
|
|
void list_destroy(list_t * list); |
|
|
void list_free(list_t * list); |
|
|
void list_append(list_t * list, node_t * item); |
|
|
void list_insert(list_t * list, void * item); |
|
|
list_t * list_create(); |
|
|
node_t * list_find(list_t * list, void * value); |
|
|
void list_remove(list_t * list, size_t index); |
|
|
void list_delete(list_t * list, node_t * node); |
|
|
node_t * list_pop(list_t * list); |
|
|
node_t * list_dequeue(list_t * list); |
|
|
list_t * list_copy(list_t * original); |
|
|
void list_merge(list_t * target, list_t * source); |
|
|
|
|
|
#define foreach(i, list) for (node_t * i = list->head; i != NULL; i = i->next) |
|
|
|
|
|
void list_destroy(list_t * list) { |
|
|
/* Free all of the contents of a list */ |
|
|
node_t * n = list->head; |
|
|
while (n) { |
|
|
free(n->value); |
|
|
n = n->next; |
|
|
} |
|
|
} |
|
|
|
|
|
void list_free(list_t * list) { |
|
|
/* Free the actual structure of a list */ |
|
|
node_t * n = list->head; |
|
|
while (n) { |
|
|
node_t * s = n->next; |
|
|
free(n); |
|
|
n = s; |
|
|
} |
|
|
} |
|
|
|
|
|
void list_append(list_t * list, node_t * node) { |
|
|
/* Insert a node onto the end of a list */ |
|
|
if (!list->tail) { |
|
|
list->head = node; |
|
|
} else { |
|
|
list->tail->next = node; |
|
|
node->prev = list->tail; |
|
|
} |
|
|
list->tail = node; |
|
|
list->length++; |
|
|
} |
|
|
|
|
|
void list_insert(list_t * list, void * item) { |
|
|
/* Insert an item into a list */ |
|
|
node_t * node = malloc(sizeof(node_t)); |
|
|
node->value = item; |
|
|
node->next = NULL; |
|
|
node->prev = NULL; |
|
|
list_append(list, node); |
|
|
} |
|
|
|
|
|
list_t * list_create() { |
|
|
/* Create a fresh list */ |
|
|
list_t * out = malloc(sizeof(list_t)); |
|
|
out->head = NULL; |
|
|
out->tail = NULL; |
|
|
out->length = 0; |
|
|
return out; |
|
|
} |
|
|
|
|
|
node_t * list_find(list_t * list, void * value) { |
|
|
foreach(item, list) { |
|
|
if (item->value == value) { |
|
|
return item; |
|
|
} |
|
|
} |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
void list_remove(list_t * list, size_t index) { |
|
|
/* remove index from the list */ |
|
|
if (index > list->length) return; |
|
|
size_t i = 0; |
|
|
node_t * n = list->head; |
|
|
while (i < index) { |
|
|
n = n->next; |
|
|
i++; |
|
|
} |
|
|
list_delete(list, n); |
|
|
} |
|
|
|
|
|
void list_delete(list_t * list, node_t * node) { |
|
|
/* remove node from the list */ |
|
|
if (node == list->head) { |
|
|
list->head = node->next; |
|
|
} |
|
|
if (node == list->tail) { |
|
|
list->tail = node->prev; |
|
|
} |
|
|
if (node->prev) { |
|
|
node->prev->next = node->next; |
|
|
} |
|
|
if (node->next) { |
|
|
node->next->prev = node->prev; |
|
|
} |
|
|
list->length--; |
|
|
} |
|
|
|
|
|
node_t * list_pop(list_t * list) { |
|
|
/* Remove and return the last value in the list |
|
|
* If you don't need it, you still probably want to free it! |
|
|
* Try free(list_pop(list)); ! |
|
|
* */ |
|
|
if (!list->tail) return NULL; |
|
|
node_t * out = list->tail; |
|
|
list_delete(list, list->tail); |
|
|
return out; |
|
|
} |
|
|
|
|
|
node_t * list_dequeue(list_t * list) { |
|
|
if (!list->head) return NULL; |
|
|
node_t * out = list->head; |
|
|
list_delete(list, list->head); |
|
|
return out; |
|
|
} |
|
|
|
|
|
list_t * list_copy(list_t * original) { |
|
|
/* Create a new copy of original */ |
|
|
list_t * out = list_create(); |
|
|
node_t * node = original->head; |
|
|
while (node) { |
|
|
list_insert(out, node->value); |
|
|
} |
|
|
return out; |
|
|
} |
|
|
|
|
|
void list_merge(list_t * target, list_t * source) { |
|
|
/* Destructively merges source into target */ |
|
|
if (target->tail) { |
|
|
target->tail->next = source->head; |
|
|
} else { |
|
|
target->head = source->head; |
|
|
} |
|
|
if (source->tail) { |
|
|
target->tail = source->tail; |
|
|
} |
|
|
target->length += source->length; |
|
|
free(source); |
|
|
} |
|
|
|
|
|
|
|
|
/* Shit that belongs in the clib */ |
|
|
|
|
|
inline int max (int a, int b) { |
|
|
if (a > b) { |
|
|
return a; |
|
|
} else { |
|
|
return b; |
|
|
} |
|
|
} |
|
|
|
|
|
DEFN_SYSCALL3(readdir, 27, int, int, void *) |
|
|
|
|
|
/* Should be kept in sync with 'struct dirent' in kernel/include/fs.h */ |
|
|
struct dirent { |
|
|
uint32_t inode; |
|
|
char name[256]; |
|
|
}; |
|
|
|
|
|
|
|
|
typedef struct DIR { |
|
|
int fd; |
|
|
int cur_entry; |
|
|
} DIR; |
|
|
|
|
|
DIR * opendir (const char * dirname) { |
|
|
int fd = open(dirname, O_RDONLY); |
|
|
if (fd == -1) { |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
DIR * dir = malloc(sizeof(DIR)); |
|
|
dir->fd = fd; |
|
|
dir->cur_entry = -1; |
|
|
return dir; |
|
|
} |
|
|
|
|
|
int closedir (DIR * dir) { |
|
|
if (dir && (dir->fd != -1)) { |
|
|
return close(dir->fd); |
|
|
} else { |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
struct dirent * readdir (DIR * dirp) { |
|
|
static struct dirent ent; |
|
|
|
|
|
int ret = syscall_readdir(dirp->fd, ++dirp->cur_entry, &ent); |
|
|
if (ret != 0) { |
|
|
memset(&ent, 0, sizeof(struct dirent)); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
return &ent; |
|
|
} |
|
|
|
|
|
// TODO: This thing is broken! Oh fuck! |
|
|
/* |
|
|
int readdir_r (DIR *restrict dirp, |
|
|
struct dirent *restrict entry, |
|
|
struct dirent **restrict result) { |
|
|
|
|
|
if ((dirp == NULL) || (dirp->fd == -1)) { |
|
|
*result = NULL; |
|
|
return -1; |
|
|
} |
|
|
|
|
|
struct dirent * ent = malloc(sizeof(struct dirent)); |
|
|
|
|
|
// Find the guy |
|
|
int index = 0; |
|
|
while (1) { |
|
|
int ret = syscall_readdir(dirp->fd, index++, ent); |
|
|
if (ret == -1) { |
|
|
*result = NULL; |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
return 0; |
|
|
} |
|
|
*/ |
|
|
|
|
|
/* The program */ |
|
|
|
|
|
int entcmp (const void * c1, const void * c2) { |
|
|
struct dirent * d1 = *(struct dirent **)c1; |
|
|
struct dirent * d2 = *(struct dirent **)c2; |
|
|
return strcmp(d1->name, d2->name); |
|
|
} |
|
|
|
|
|
void print_entry (const char * filename, const char * srcpath, int colwidth) { |
|
|
/* Figure out full relpath */ |
|
|
char * relpath = malloc(strlen(srcpath) + strlen(filename) + 2); |
|
|
sprintf(relpath, "%s/%s", srcpath, filename); |
|
|
|
|
|
/* Classify file */ |
|
|
struct stat statbuf; |
|
|
stat(relpath, &statbuf); |
|
|
free(relpath); |
|
|
|
|
|
const char * ansi_color_str; |
|
|
if (S_ISDIR(statbuf.st_mode)) { |
|
|
// Directory |
|
|
ansi_color_str = DIR_COLOR; |
|
|
} else if (statbuf.st_mode & 0111) { |
|
|
// Executable |
|
|
ansi_color_str = EXE_COLOR; |
|
|
} else { |
|
|
// Something else |
|
|
ansi_color_str = REG_COLOR; |
|
|
} |
|
|
|
|
|
|
|
|
/* Print the file name */ |
|
|
printf("\033[%sm%s\033[0m", ansi_color_str, filename); |
|
|
|
|
|
/* Pad the rest of the column */ |
|
|
for (int rem = colwidth - strlen(filename); rem > 0; rem--) { |
|
|
printf(" "); |
|
|
} |
|
|
} |
|
|
|
|
|
int main (int argc, char * argv[]) { |
|
|
|
|
|
/* Parse arguments */ |
|
|
char * p = "."; |
|
|
int explicit_path_set = 0; |
|
|
int show_hidden = 0; |
|
|
|
|
|
#if 0 |
|
|
if (argc > 1) { |
|
|
int index, c; |
|
|
while ((c = getopt(argc, argv, "a")) != -1) { |
|
|
switch (c) { |
|
|
case 'a': |
|
|
show_hidden = 1; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
if (optind < argc) { |
|
|
p = argv[optind]; |
|
|
} |
|
|
} |
|
|
#else |
|
|
for (int i = 1; i < argc; i++) { |
|
|
if (strcmp(argv[i], "-a") == 0) { |
|
|
show_hidden = 1; |
|
|
} else if (!explicit_path_set) { |
|
|
p = argv[i]; |
|
|
explicit_path_set = 1; |
|
|
} |
|
|
} |
|
|
#endif |
|
|
|
|
|
/* Open the directory */ |
|
|
DIR * dirp = opendir(p); |
|
|
if (dirp == NULL) { |
|
|
printf("no such directory\n"); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
/* Read the entries in the directory */ |
|
|
list_t * ents_list = list_create(); |
|
|
|
|
|
struct dirent * ent = readdir(dirp); |
|
|
while (ent != NULL) { |
|
|
if (show_hidden || (ent->name[0] != '.')) { |
|
|
struct dirent * entcpy = malloc(sizeof(struct dirent)); |
|
|
memcpy(entcpy, ent, sizeof(struct dirent)); |
|
|
list_insert(ents_list, (void *)entcpy); |
|
|
} |
|
|
|
|
|
ent = readdir(dirp); |
|
|
} |
|
|
closedir(dirp); |
|
|
|
|
|
/* Now, copy those entries into an array (for sorting) */ |
|
|
struct dirent ** ents_array = malloc(sizeof(struct dirent *) * ents_list->length); |
|
|
int index = 0; |
|
|
node_t * node; |
|
|
foreach(node, ents_list) { |
|
|
ents_array[index++] = (struct dirent *)node->value; |
|
|
} |
|
|
list_free(ents_list); |
|
|
int numents = index; |
|
|
|
|
|
qsort(ents_array, numents, sizeof(struct dirent *), entcmp); |
|
|
|
|
|
/* Determine the gridding dimensions */ |
|
|
int ent_max_len = 0; |
|
|
for (int i = 0; i < numents; i++) { |
|
|
ent_max_len = max(ent_max_len, strlen(ents_array[i]->name)); |
|
|
} |
|
|
|
|
|
const int term_width = 128; // For now, we assume 128 |
|
|
|
|
|
int col_ext = ent_max_len + MIN_COL_SPACING; |
|
|
int cols = ((term_width - ent_max_len) / col_ext) + 1; |
|
|
#if 0 |
|
|
fprintf(stderr, "Printing %dx%d grid (max width: %d)\n", cols, (numents/cols), ent_max_len); |
|
|
#endif |
|
|
|
|
|
/* Print the entries */ |
|
|
|
|
|
// Print rows |
|
|
for (int i = 0; i < numents;) { |
|
|
|
|
|
// Print columns on this row |
|
|
print_entry(ents_array[i++]->name, p, ent_max_len); |
|
|
|
|
|
for (int j = 0; (i < numents) && (j < (cols-1)); j++) { |
|
|
printf(" "); |
|
|
print_entry(ents_array[i++]->name, p, ent_max_len); |
|
|
} |
|
|
|
|
|
printf("\n"); |
|
|
} |
|
|
free(ents_array); |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* |
|
|
* vim: tabstop=4 |
|
|
* vim: shiftwidth=4 |
|
|
* vim: noexpandtab |
|
|
*/ |