improve ls command

This commit is contained in:
Murphy 2023-04-28 12:31:49 -04:00
parent 4e3e5f2990
commit 3b53b4c96d
3 changed files with 244 additions and 118 deletions

View file

@ -6,6 +6,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h> #include <dirent.h>
#include <ftw.h> #include <ftw.h>
@ -26,6 +27,28 @@ struct Flags {
bool recurse; bool recurse;
}; };
struct FileInfo {
char mode[11];
int links;
struct passwd* usr;
struct group* grp;
char size[5];
char date[13];
struct dirent* file;
bool set_uid;
bool set_gid;
bool exec;
};
struct FileListInfo {
int max_link;
int max_usr;
int max_grp;
int max_size;
int max_name;
int total_len;
};
static DIR* get_directory(char* path) { static DIR* get_directory(char* path) {
DIR* d = opendir(path); DIR* d = opendir(path);
if (d == NULL) { if (d == NULL) {
@ -44,19 +67,7 @@ static void append_path(char buf[PATH_MAX], const char* dir_path, const struct d
memcpy(buf + dir_len, file->d_name, strlen(file->d_name) + 1); memcpy(buf + dir_len, file->d_name, strlen(file->d_name) + 1);
} }
static void list_file(struct dirent* file, struct Flags* flags, const char* dir_path) { static bool get_file_info(struct dirent* file, const char* dir_path, struct FileInfo* info) {
char* color;
if (file->d_type == DT_DIR) {
color = DIR_COLOR;
} else if (file->d_type == DT_LNK) {
color = LINK_COLOR;
} else {
color = FILE_COLOR;
}
if (flags->more_info) {
uid_t uid = getuid(); uid_t uid = getuid();
gid_t gid = getgid(); gid_t gid = getgid();
@ -67,91 +78,141 @@ static void list_file(struct dirent* file, struct Flags* flags, const char* dir_
struct stat s; struct stat s;
if (stat(buf, &s) < 0) { if (stat(buf, &s) < 0) {
printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno)); printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno));
return; return false;
} }
if (file->d_type == DT_DIR) putchar('d'); info->set_uid = false;
else if (file->d_type == DT_LNK) putchar('l'); info->set_gid = false;
else putchar('-'); info->exec = false;
bool group_sticky = false; if (file->d_type == DT_DIR)
bool user_sticky = false; info->mode[0] = 'd';
bool exectuable = false; else if (file->d_type == DT_LNK)
info->mode[0] = 'l';
else
info->mode[0] = '-';
putchar((s.st_mode & S_IRUSR) ? 'r' : '-'); info->mode[1] = (s.st_mode & S_IRUSR) ? 'r' : '-';
putchar((s.st_mode & S_IWUSR) ? 'w' : '-'); info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-';
if (s.st_mode & S_IXUSR) { if (s.st_mode & S_IXUSR) {
if (putchar(s.st_mode & S_ISUID)) { if (s.st_mode & S_ISUID) {
putchar('s'); info->mode[3] = 's';
user_sticky = true; info->set_uid = true;
} else { } else {
putchar('x'); info->mode[3] = 'x';
} }
if (!exectuable) exectuable = s.st_uid == uid; if (!info->exec) info->exec = s.st_uid == uid;
} else { } else {
putchar('-'); info->mode[3] = '-';
} }
putchar((s.st_mode & S_IRGRP) ? 'r' : '-'); info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-';
putchar((s.st_mode & S_IWGRP) ? 'w' : '-'); info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-';
if (s.st_mode & S_IXGRP) { if (s.st_mode & S_IXGRP) {
if (putchar(s.st_mode & S_ISGID)) { if (s.st_mode & S_ISGID) {
putchar('s'); info->mode[6] = 's';
group_sticky = true; info->set_gid = true;
} else { } else {
putchar('x'); info->mode[6] = 'x';
} }
if (!exectuable) exectuable = s.st_gid == gid; if (!info->exec) info->exec = s.st_gid == gid;
} else { } else {
putchar('-'); info->mode[6] = '-';
} }
putchar((s.st_mode & S_IROTH) ? 'r' : '-'); info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-';
putchar((s.st_mode & S_IWOTH) ? 'w' : '-'); info->mode[8] = (s.st_mode & S_IWOTH) ? 'w' : '-';
if (s.st_mode & S_IXOTH) { if (s.st_mode & S_IXOTH) {
putchar('x'); info->mode[9] = 'x';
exectuable = true; info->exec = true;
} else { } else {
putchar('-'); info->mode[9] = '-';
} }
printf(" %ld\t", s.st_nlink); info->mode[10] = '\0';
struct passwd* passwd = getpwuid(s.st_uid); info->usr = getpwuid(s.st_uid);
struct group* group = getgrgid(s.st_gid); info->grp = getgrgid(s.st_gid);
info->file = file;
info->links = s.st_nlink;
printf("%s ", passwd->pw_name); print_file_size(s.st_size, info->size);
printf("%s ", group->gr_name); print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000, info->date);
return true;
}
print_file_size(s.st_size); static char* get_file_color(struct FileInfo* info) {
print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000); char* color;
if (info->file->d_type == DT_DIR) {
if (file->d_type == DT_REG) { color = DIR_COLOR;
if (user_sticky) { } else if (info->file->d_type == DT_LNK) {
color = LINK_COLOR;
} else {
if (info->set_uid) {
color = SET_UID_COLOR; color = SET_UID_COLOR;
} else if (group_sticky) { } else if (info->set_gid) {
color = SET_GID_COLOR; color = SET_GID_COLOR;
} else if (exectuable) { } else if (info->exec) {
color = EXEC_COLOR; color = EXEC_COLOR;
}
}
printf("%s%s\x1b[0m", color, file->d_name);
if (file->d_type == DT_LNK) {
char link[PATH_MAX];
if (readlink(buf, link, PATH_MAX) < 0) {
printf("\n");
} else { } else {
printf(" -> %s\n", link); color = FILE_COLOR;
} }
}
return color;
}
static void list_files(struct FileInfo* files, int file_len, struct FileListInfo info, struct Flags* flags, const char* dir_path) {
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
char* color;
const int column_width = info.max_name + 1;
const int row_count = w.ws_col / column_width;
for (int i = 0; i < file_len; i++) {
struct FileInfo finfo = files[i];
if (strlen(finfo.file->d_name) < 1) continue;
color = get_file_color(&finfo);
if (flags->more_info) {
printf("%s %*d %*s %*s %*s %s %s%s\x1b[0m",
finfo.mode,
info.max_link,
finfo.links,
info.max_usr,
finfo.usr->pw_name,
info.max_grp,
finfo.grp->gr_name,
info.max_size,
finfo.size,
finfo.date,
color,
finfo.file->d_name
);
if (finfo.file->d_type == DT_LNK) {
char path[PATH_MAX];
append_path(path, dir_path, finfo.file);
char lnk[PATH_MAX];
if (readlink(path, lnk, PATH_MAX) < 0) {
putchar('\n');
} else { } else {
printf("\n"); printf(" -> %s\n", lnk);
} }
} else { } else {
printf("%s%s", color, file->d_name); putchar('\n');
flags->one_column ? putchar('\n') : putchar(' '); }
} else if (flags->one_column) {
printf("%s%s\x1b[0m\n", color, finfo.file->d_name);
} else {
if (info.total_len > w.ws_col) {
if (i != 0 && i % row_count == 0) putchar('\n');
printf("%s%*s\x1b[0m", color, column_width, finfo.file->d_name);
} else {
printf("%s%s\x1b[0m ", color, finfo.file->d_name);
}
}
} }
} }
@ -159,6 +220,53 @@ static bool is_dot_dir(const char* path) {
return streql(path, ".") || streql(path, ".."); return streql(path, ".") || streql(path, "..");
} }
static int num_places (int n) {
int r = 1;
if (n < 0) n = (n == INT_MIN) ? INT_MAX: -n;
while (n > 9) {
n /= 10;
r++;
}
return r;
}
static void push_file(
struct FileInfo** files,
struct FileListInfo* info,
int* size, int* capacity,
struct dirent* file,
const char* dir_path
) {
struct FileInfo finfo;
if (!get_file_info(file, dir_path, &finfo)) return;
if (*size == *capacity) {
*capacity *= 2;
*files = realloc(*files, sizeof(struct FileInfo) * *capacity);
}
int user_len = strlen(finfo.usr->pw_name);
if (user_len > info->max_usr) info->max_usr = user_len;
int group_len = strlen(finfo.grp->gr_name);
if (group_len > info->max_grp) info->max_grp = group_len;
int name_len = strlen(file->d_name);
if (name_len > info->max_name) info->max_name = name_len;
int size_len = strlen(finfo.size);
if (size_len > info->max_size) info->max_size = size_len;
int link_len = num_places(finfo.links);
if (link_len > info->max_link) info->max_link = link_len;
info->total_len += name_len + 2;
(*files)[*size] = finfo;
(*size)++;
}
static void recurse_directory(char* path, struct Flags* flags) { static void recurse_directory(char* path, struct Flags* flags) {
DIR* d; DIR* d;
struct dirent* file; struct dirent* file;
@ -167,6 +275,12 @@ static void recurse_directory(char* path, struct Flags* flags) {
d = get_directory(path); d = get_directory(path);
if (d == NULL) return; if (d == NULL) return;
int capacity = 8;
int size = 0;
struct FileInfo* files = malloc(sizeof(struct FileInfo) * capacity);
struct FileListInfo info;
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) continue;
if (!flags->hidden && prefix(".", file->d_name)) continue; if (!flags->hidden && prefix(".", file->d_name)) continue;
@ -175,9 +289,12 @@ static void recurse_directory(char* path, struct Flags* flags) {
printf("\n%s%s:%s\n", DIR_COLOR, path, FILE_COLOR); printf("\n%s%s:%s\n", DIR_COLOR, path, FILE_COLOR);
first = false; first = false;
} }
list_file(file, flags, path); push_file(&files, &info, &size, &capacity, file, path);
} }
list_files(files, size, info, flags, path);
free(files);
if (!flags->more_info) printf("\n"); if (!flags->more_info) printf("\n");
closedir(d); closedir(d);
@ -206,14 +323,22 @@ static void list_directory(char* path, struct Flags* flags) {
DIR* d = get_directory(path); DIR* d = get_directory(path);
if (d == NULL) return; if (d == NULL) return;
struct dirent* file; int capacity = 8;
int size = 0;
struct FileInfo* files = malloc(sizeof(struct FileInfo) * capacity);
struct FileListInfo info;
memset(&info, 0, sizeof(struct FileListInfo));
struct dirent* file;
while ((file = readdir(d)) != NULL) { while ((file = readdir(d)) != NULL) {
if (!flags->hidden && prefix(".", file->d_name)) continue; if (!flags->hidden && prefix(".", file->d_name)) continue;
if (flags->hide_dot && is_dot_dir(file->d_name)) continue; if (flags->hide_dot && is_dot_dir(file->d_name)) continue;
list_file(file, flags, path); push_file(&files, &info, &size, &capacity, file, path);
} }
list_files(files, size, info, flags, path);
free(files);
closedir(d); closedir(d);
} }

View file

@ -47,7 +47,7 @@ bool prefix(const char* pre, const char* str) {
} }
static char fs_types[5] = {'K','M','G','T','P'}; static char fs_types[5] = {'K','M','G','T','P'};
void print_file_size(size_t bytes) { void print_file_size(size_t bytes, char buf[5]) {
int index = 0; int index = 0;
float next = bytes; float next = bytes;
while (true) { while (true) {
@ -62,24 +62,25 @@ void print_file_size(size_t bytes) {
index++; index++;
} }
if (next/100 < 1) putchar(' '); int n = snprintf(buf, 4, "%u", (int)(next+.5));
if (next/10 < 1) putchar(' ');
if (index == 0) putchar(' ');
printf("%u", (int)(next+.5));
if (index > 0) { if (index > 0) {
putchar(fs_types[index - 1]); buf[n] = (fs_types[index - 1]);
n++;
} }
putchar(' '); buf[n] = '\0';
} }
static char* months[12] = static char* months[12] =
{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
void print_date_time(time_t mills) { void print_date_time(time_t mills, char buf[13]) {
struct tm* info; struct tm* info;
info = localtime(&mills); info = localtime(&mills);
printf("%s ", months[info->tm_mon]); int n = snprintf(buf, 5, "%s ", months[info->tm_mon]);
if (info->tm_mday < 10)
printf(" "); if (info->tm_mday < 10) {
printf("%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec); buf[n] = ' ';
n++;
}
snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec);
} }

View file

@ -13,5 +13,5 @@ long int get_number(const char* text);
bool streql(const char* a, const char* b); bool streql(const char* a, const char* b);
bool prefix(const char* pre, const char* str); bool prefix(const char* pre, const char* str);
void print_file_size(size_t bytes); void print_file_size(size_t bytes, char buf[5]);
void print_date_time(time_t mills); void print_date_time(time_t mills, char buf[13]);