diff --git a/userspace/CMakeLists.txt b/userspace/CMakeLists.txt index 65ee48c6..16d090a5 100644 --- a/userspace/CMakeLists.txt +++ b/userspace/CMakeLists.txt @@ -3,9 +3,10 @@ cmake_minimum_required(VERSION 3.26) project(userspace CXX) set(USERSPACE_PROJECTS - Shell cat echo + ls + Shell test yes ) diff --git a/userspace/ls/CMakeLists.txt b/userspace/ls/CMakeLists.txt new file mode 100644 index 00000000..df6d503d --- /dev/null +++ b/userspace/ls/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.26) + +project(ls CXX) + +set(SOURCES + main.cpp +) + +add_executable(ls ${SOURCES}) +target_compile_options(ls PUBLIC -O2 -g) +target_link_libraries(ls PUBLIC libc) + +add_custom_target(ls-install + COMMAND cp ${CMAKE_CURRENT_BINARY_DIR}/ls ${BANAN_BIN}/ + DEPENDS ls +) diff --git a/userspace/ls/main.cpp b/userspace/ls/main.cpp new file mode 100644 index 00000000..db113e6d --- /dev/null +++ b/userspace/ls/main.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include + +bool all { false }; +bool list { false }; + +const char* mode_string(mode_t mode) +{ + static char buffer[11]; + buffer[0] = (mode & 0770000) == S_IFLNK ? 'l' : + (mode & 0770000) == S_IFDIR ? 'd' : + (mode & 0770000) == S_IFBLK ? 'b' : + (mode & 0770000) == S_IFCHR ? 'c' : + '-'; + buffer[1] = mode & S_IRUSR ? 'r' : '-'; + buffer[2] = mode & S_IWUSR ? 'w' : '-'; + buffer[3] = mode & S_IXUSR ? 'x' : '-'; + buffer[4] = mode & S_IRGRP ? 'r' : '-'; + buffer[5] = mode & S_IWGRP ? 'w' : '-'; + buffer[6] = mode & S_IXGRP ? 'x' : '-'; + buffer[7] = mode & S_IROTH ? 'r' : '-'; + buffer[8] = mode & S_IWOTH ? 'w' : '-'; + buffer[9] = mode & S_IXOTH ? 'x' : '-'; + buffer[10] = '\0'; + + return buffer; +} + +void list_directory(const char* path) +{ + DIR* dirp = opendir(path); + if (dirp == nullptr) + return perror("opendir"); + + errno = 0; + + bool first = true; + while (auto* dirent = readdir(dirp)) + { + if (!all && dirent->d_name[0] == '.') + continue; + + if (list) + { + if (!first) + printf("\n"); + + struct stat st; + if (fstatat(dirfd(dirp), dirent->d_name, &st, AT_SYMLINK_NOFOLLOW) == -1) + { + perror("stat"); + printf("?????????? ???? %s", dirent->d_name); + } + else + { + printf("%s %d %s", mode_string(st.st_mode), st.st_size, dirent->d_name); + } + } + else + { + if (!first) + printf(" "); + printf("%s", dirent->d_name); + } + + first = false; + } + + if (errno != 0) + perror("readdir"); + + printf("\n"); + + closedir(dirp); +} + +void print_usage() +{ + printf("Usage: ls [OPTION]... [FILE]...\n"); + printf(" -a, --all show all files\n"); + printf(" -l, --list print files on separate lines\n"); +} + +int main(int argc, char** argv) +{ + int arg = 1; + + while (arg < argc && argv[arg][0] == '-') + { + if (strcmp(argv[arg], "--all") == 0) + all = true; + else if (strcmp(argv[arg], "--list") == 0) + list = true; + else if (strcmp(argv[arg], "--help") == 0) + { + print_usage(); + return 0; + } + else + { + for (int i = 1; argv[arg][i]; i++) + { + char c = argv[arg][i]; + if (c == 'a') + all = true; + else if (c == 'l') + list = true; + else + { + print_usage(); + return 1; + } + } + } + + arg++; + } + + if (arg == argc) + { + list_directory("."); + } + else if (arg + 1 == argc) + { + list_directory(argv[arg]); + } + else + { + for (int i = arg; i < argc; i++) + { + if (i > arg) + printf("\n"); + printf("%s:\n", argv[i]); + list_directory(argv[i]); + } + } + + return 0; +}