From 9fc48c236bd0344e2a6b59f1c92a54c18736d11a Mon Sep 17 00:00:00 2001 From: Yuta Imazu Date: Sat, 22 Jun 2024 20:00:31 +0900 Subject: [PATCH] userland(ls): add -l option --- userland/ls.c | 190 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 63 deletions(-) diff --git a/userland/ls.c b/userland/ls.c index ad5086f2..3d16b849 100644 --- a/userland/ls.c +++ b/userland/ls.c @@ -1,78 +1,78 @@ #include #include -#include #include #include #include +#include +#include #include +#include #include -static int get_format(const struct dirent* dent, const char** out_format, - size_t* out_len) { - struct stat stat_buf; - if (stat(dent->d_name, &stat_buf) < 0) { - perror("stat"); - return -1; - } - switch (stat_buf.st_mode & S_IFMT) { +static const char* get_format(const char* name, mode_t mode, size_t* out_len) { + switch (mode & S_IFMT) { case S_IFDIR: - *out_format = "\x1b[01;34m%s\x1b[m/"; - *out_len = strlen(dent->d_name) + 1; - break; + *out_len = strlen(name) + 1; + return "\x1b[01;34m%s\x1b[m/"; case S_IFCHR: case S_IFBLK: - *out_format = "\x1b[01;33m%s\x1b[m"; - *out_len = strlen(dent->d_name); - break; + *out_len = strlen(name); + return "\x1b[01;33m%s\x1b[m"; case S_IFIFO: - *out_format = "\x1b[33m%s\x1b[m|"; - *out_len = strlen(dent->d_name) + 1; - break; + *out_len = strlen(name) + 1; + return "\x1b[33m%s\x1b[m|"; case S_IFLNK: - *out_format = "\x1b[01;36m%s\x1b[m@"; - *out_len = strlen(dent->d_name) + 1; - break; + *out_len = strlen(name) + 1; + return "\x1b[01;36m%s\x1b[m@"; case S_IFSOCK: - *out_format = "\x1b[01;35m%s\x1b[m="; - *out_len = strlen(dent->d_name) + 1; - break; + *out_len = strlen(name) + 1; + return "\x1b[01;35m%s\x1b[m="; case S_IFREG: default: - if (stat_buf.st_mode & S_IXUSR) { - *out_format = "\x1b[01;32m%s\x1b[m*"; - *out_len = strlen(dent->d_name) + 1; + if (mode & S_IXUSR) { + *out_len = strlen(name) + 1; + return "\x1b[01;32m%s\x1b[m*"; } else { - *out_format = "%s"; - *out_len = strlen(dent->d_name); + *out_len = strlen(name); + return "%s"; } - break; } - return 0; +} + +static char get_file_type_char(mode_t mode) { + switch (mode & S_IFMT) { + case S_IFDIR: + return 'd'; + case S_IFCHR: + return 'c'; + case S_IFBLK: + return 'b'; + case S_IFIFO: + return 'p'; + case S_IFLNK: + return 'l'; + case S_IFSOCK: + return 's'; + case S_IFREG: + default: + return '-'; + } } #define TAB_STOP 8 -int main(int argc, char* argv[]) { - const char* path; - if (argc < 2) { - static char path_buf[1024]; - getcwd(path_buf, 1024); - path = path_buf; - } else if (argc == 2) { - path = argv[1]; - } else { - dprintf(STDERR_FILENO, "Usage: ls [DIRECTORY]\n"); - return EXIT_FAILURE; - } +static int list_dir(const char* path, size_t terminal_width, bool long_format) { + int ret = -1; + DIR* dirp = NULL; - DIR* dirp = opendir(path); + dirp = opendir(path); if (!dirp) { perror("opendir"); - return EXIT_FAILURE; + goto fail; } - chdir(path); - size_t width = 0; + size_t path_len = strlen(path); + size_t x_pos = 0; for (;;) { errno = 0; struct dirent* dent = readdir(dirp); @@ -80,29 +80,93 @@ int main(int argc, char* argv[]) { if (errno == 0) break; perror("readdir"); - closedir(dirp); - return EXIT_FAILURE; + goto fail; } - const char* format; - size_t len; - if (get_format(dent, &format, &len) < 0) { - closedir(dirp); - return EXIT_FAILURE; + size_t name_len = strlen(dent->d_name); + char* full_path = malloc(path_len + 1 + name_len + 1); + if (!full_path) { + perror("malloc"); + goto fail; + } + strcpy(full_path, path); + full_path[path_len] = '/'; + strcpy(full_path + path_len + 1, dent->d_name); + + struct stat st; + ret = stat(full_path, &st); + free(full_path); + if (ret < 0) { + perror("stat"); + goto fail; } - size_t next_pos = round_up(width + len + 1, TAB_STOP); - if (next_pos >= 80) { - width = round_up(len + 1, TAB_STOP); + + size_t len; + const char* format = get_format(dent->d_name, st.st_mode, &len); + if (long_format) { + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) { + printf("%c %3d,%6d ", get_file_type_char(st.st_mode), + major(st.st_rdev), minor(st.st_rdev)); + } else { + printf("%c %10u ", get_file_type_char(st.st_mode), st.st_size); + } + printf(format, dent->d_name); putchar('\n'); } else { - width = next_pos; + size_t next_pos = round_up(x_pos + len + 1, TAB_STOP); + if (next_pos >= terminal_width) { + x_pos = round_up(len + 1, TAB_STOP); + putchar('\n'); + } else { + x_pos = next_pos; + } + printf(format, dent->d_name); + putchar('\t'); } - printf(format, dent->d_name); - putchar('\t'); } - if (width > 0) + if (x_pos > 0) putchar('\n'); - closedir(dirp); - return EXIT_SUCCESS; + ret = 0; +fail: + putchar('\n'); + if (dirp) + closedir(dirp); + return ret; +} + +int main(int argc, char* argv[]) { + bool long_format = false; + size_t num_dirs = 0; + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-l")) + long_format = true; + else + ++num_dirs; + } + + size_t terminal_width = 80; + struct winsize winsize; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) >= 0) + terminal_width = winsize.ws_col; + + if (num_dirs == 0) { + static char path_buf[PATH_MAX]; + getcwd(path_buf, PATH_MAX); + if (list_dir(path_buf, terminal_width, long_format) < 0) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + + int ret = EXIT_SUCCESS; + for (int i = 1; i < argc; i++) { + const char* arg = argv[i]; + if (!strcmp(arg, "-l")) + continue; + if (num_dirs > 1) + printf("%s:\n", arg); + if (list_dir(arg, terminal_width, long_format) < 0) + ret = EXIT_FAILURE; + } + return ret; }