c88d6dcb4f
Adds useful descriptions to ones that were lacking them, updates ones that were innacurate, etc.
418 lines
8.3 KiB
C
418 lines
8.3 KiB
C
/*
|
|
* ls
|
|
*
|
|
* Lists files in a directory, with nice color
|
|
* output like any modern ls should have.
|
|
*/
|
|
|
|
|
|
#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;"
|
|
|
|
#define DEFAULT_TERM_WIDTH 80
|
|
|
|
|
|
/* 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 */
|
|
|
|
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 = DEFAULT_TERM_WIDTH; // 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
|
|
*/
|