summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTyler Murphy <tylerm@tylerm.dev>2023-04-28 12:31:49 -0400
committerTyler Murphy <tylerm@tylerm.dev>2023-04-28 12:31:49 -0400
commit3b53b4c96d05cfadcf275d24f021159471343048 (patch)
tree275af254cd3402fccfa214d8f0f7c08353e7869f
parentremove __ on uid and gid for alpine (diff)
downloadlazysphere-3b53b4c96d05cfadcf275d24f021159471343048.tar.gz
lazysphere-3b53b4c96d05cfadcf275d24f021159471343048.tar.bz2
lazysphere-3b53b4c96d05cfadcf275d24f021159471343048.zip
improve ls command
-rw-r--r--src/commands/ls.c301
-rw-r--r--src/shared.c29
-rw-r--r--src/shared.h4
3 files changed, 230 insertions, 104 deletions
diff --git a/src/commands/ls.c b/src/commands/ls.c
index bcc042c..e9fec8c 100644
--- a/src/commands/ls.c
+++ b/src/commands/ls.c
@@ -6,6 +6,7 @@
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/ioctl.h>
#include <unistd.h>
#include <dirent.h>
#include <ftw.h>
@@ -26,6 +27,28 @@ struct Flags {
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) {
DIR* d = opendir(path);
if (d == NULL) {
@@ -44,114 +67,152 @@ 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);
}
-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;
+ uid_t uid = getuid();
+ gid_t gid = getgid();
- 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();
- gid_t gid = getgid();
-
- char buf[PATH_MAX];
- append_path(buf, dir_path, file);
+ char buf[PATH_MAX];
+ append_path(buf, dir_path, file);
- struct stat s;
- if (stat(buf, &s) < 0) {
- printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno));
- return;
- }
+ struct stat s;
+ if (stat(buf, &s) < 0) {
+ printf("\x1b[0merror: failed to read file '%s': %s\n", buf, strerror(errno));
+ return false;
+ }
- if (file->d_type == DT_DIR) putchar('d');
- else if (file->d_type == DT_LNK) putchar('l');
- else putchar('-');
+ info->set_uid = false;
+ info->set_gid = false;
+ info->exec = false;
- bool group_sticky = false;
- bool user_sticky = false;
- bool exectuable = false;
+ if (file->d_type == DT_DIR)
+ info->mode[0] = 'd';
+ else if (file->d_type == DT_LNK)
+ info->mode[0] = 'l';
+ else
+ info->mode[0] = '-';
- putchar((s.st_mode & S_IRUSR) ? 'r' : '-');
- putchar((s.st_mode & S_IWUSR) ? 'w' : '-');
- if (s.st_mode & S_IXUSR) {
- if (putchar(s.st_mode & S_ISUID)) {
- putchar('s');
- user_sticky = true;
- } else {
- putchar('x');
- }
- if (!exectuable) exectuable = s.st_uid == uid;
- } else {
- putchar('-');
- }
-
- putchar((s.st_mode & S_IRGRP) ? 'r' : '-');
- putchar((s.st_mode & S_IWGRP) ? 'w' : '-');
- if (s.st_mode & S_IXGRP) {
- if (putchar(s.st_mode & S_ISGID)) {
- putchar('s');
- group_sticky = true;
- } else {
- putchar('x');
- }
- if (!exectuable) exectuable = s.st_gid == gid;
+ info->mode[1] = (s.st_mode & S_IRUSR) ? 'r' : '-';
+ info->mode[2] = (s.st_mode & S_IWUSR) ? 'w' : '-';
+ if (s.st_mode & S_IXUSR) {
+ if (s.st_mode & S_ISUID) {
+ info->mode[3] = 's';
+ info->set_uid = true;
} else {
- putchar('-');
+ info->mode[3] = 'x';
}
+ if (!info->exec) info->exec = s.st_uid == uid;
+ } else {
+ info->mode[3] = '-';
+ }
- putchar((s.st_mode & S_IROTH) ? 'r' : '-');
- putchar((s.st_mode & S_IWOTH) ? 'w' : '-');
- if (s.st_mode & S_IXOTH) {
- putchar('x');
- exectuable = true;
+ info->mode[4] = (s.st_mode & S_IRGRP) ? 'r' : '-';
+ info->mode[5] = (s.st_mode & S_IWGRP) ? 'w' : '-';
+ if (s.st_mode & S_IXGRP) {
+ if (s.st_mode & S_ISGID) {
+ info->mode[6] = 's';
+ info->set_gid = true;
} else {
- putchar('-');
+ info->mode[6] = 'x';
}
+ if (!info->exec) info->exec = s.st_gid == gid;
+ } else {
+ info->mode[6] = '-';
+ }
- printf(" %ld\t", s.st_nlink);
+ info->mode[7] = (s.st_mode & S_IROTH) ? 'r' : '-';
+ info->mode[8] = (s.st_mode & S_IWOTH) ? 'w' : '-';
+ if (s.st_mode & S_IXOTH) {
+ info->mode[9] = 'x';
+ info->exec = true;
+ } else {
+ info->mode[9] = '-';
+ }
- struct passwd* passwd = getpwuid(s.st_uid);
- struct group* group = getgrgid(s.st_gid);
+ info->mode[10] = '\0';
- printf("%s ", passwd->pw_name);
- printf("%s ", group->gr_name);
+ info->usr = getpwuid(s.st_uid);
+ info->grp = getgrgid(s.st_gid);
+ info->file = file;
+ info->links = s.st_nlink;
- print_file_size(s.st_size);
- print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000);
+ print_file_size(s.st_size, info->size);
+ print_date_time(s.st_mtim.tv_sec + s.st_mtim.tv_nsec / 1000000, info->date);
+ return true;
+}
- if (file->d_type == DT_REG) {
- if (user_sticky) {
- color = SET_UID_COLOR;
- } else if (group_sticky) {
- color = SET_GID_COLOR;
- } else if (exectuable) {
- color = EXEC_COLOR;
- }
+static char* get_file_color(struct FileInfo* info) {
+ char* color;
+ if (info->file->d_type == DT_DIR) {
+ color = DIR_COLOR;
+ } else if (info->file->d_type == DT_LNK) {
+ color = LINK_COLOR;
+ } else {
+ if (info->set_uid) {
+ color = SET_UID_COLOR;
+ } else if (info->set_gid) {
+ color = SET_GID_COLOR;
+ } else if (info->exec) {
+ color = EXEC_COLOR;
+ } else {
+ 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;
- printf("%s%s\x1b[0m", color, file->d_name);
+ 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);
- if (file->d_type == DT_LNK) {
- char link[PATH_MAX];
- if (readlink(buf, link, PATH_MAX) < 0) {
- printf("\n");
+ char lnk[PATH_MAX];
+ if (readlink(path, lnk, PATH_MAX) < 0) {
+ putchar('\n');
+ } else {
+ printf(" -> %s\n", lnk);
+ }
+
} else {
- printf(" -> %s\n", link);
+ putchar('\n');
}
+ } else if (flags->one_column) {
+ printf("%s%s\x1b[0m\n", color, finfo.file->d_name);
} else {
- printf("\n");
+ 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);
+ }
}
-
- } else {
- printf("%s%s", color, file->d_name);
- flags->one_column ? putchar('\n') : putchar(' ');
}
}
@@ -159,6 +220,53 @@ static bool is_dot_dir(const char* 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) {
DIR* d;
struct dirent* file;
@@ -167,6 +275,12 @@ static void recurse_directory(char* path, struct Flags* flags) {
d = get_directory(path);
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) {
if (file->d_type == DT_DIR) continue;
if (!flags->hidden && prefix(".", file->d_name)) continue;
@@ -175,8 +289,11 @@ static void recurse_directory(char* path, struct Flags* flags) {
printf("\n%s%s:%s\n", DIR_COLOR, path, FILE_COLOR);
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");
@@ -206,14 +323,22 @@ static void list_directory(char* path, struct Flags* flags) {
DIR* d = get_directory(path);
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));
+
struct dirent* file;
-
while ((file = readdir(d)) != NULL) {
if (!flags->hidden && prefix(".", 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);
}
diff --git a/src/shared.c b/src/shared.c
index a8837c4..b167e6f 100644
--- a/src/shared.c
+++ b/src/shared.c
@@ -47,7 +47,7 @@ bool prefix(const char* pre, const char* str) {
}
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;
float next = bytes;
while (true) {
@@ -61,25 +61,26 @@ void print_file_size(size_t bytes) {
next /= 1024;
index++;
}
-
- if (next/100 < 1) putchar(' ');
- if (next/10 < 1) putchar(' ');
- if (index == 0) putchar(' ');
-
- printf("%u", (int)(next+.5));
+
+ int n = snprintf(buf, 4, "%u", (int)(next+.5));
if (index > 0) {
- putchar(fs_types[index - 1]);
+ buf[n] = (fs_types[index - 1]);
+ n++;
}
- putchar(' ');
+ buf[n] = '\0';
}
static char* months[12] =
{"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;
info = localtime(&mills);
- printf("%s ", months[info->tm_mon]);
- if (info->tm_mday < 10)
- printf(" ");
- printf("%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec);
+ int n = snprintf(buf, 5, "%s ", months[info->tm_mon]);
+
+ if (info->tm_mday < 10) {
+ buf[n] = ' ';
+ n++;
+ }
+
+ snprintf(buf + n, 13 - n, "%d %02d:%02d ", info->tm_mday, info->tm_hour, info->tm_sec);
}
diff --git a/src/shared.h b/src/shared.h
index 56f0a70..4556d5d 100644
--- a/src/shared.h
+++ b/src/shared.h
@@ -13,5 +13,5 @@ long int get_number(const char* text);
bool streql(const char* a, const char* b);
bool prefix(const char* pre, const char* str);
-void print_file_size(size_t bytes);
-void print_date_time(time_t mills);
+void print_file_size(size_t bytes, char buf[5]);
+void print_date_time(time_t mills, char buf[13]);