Serenity Operating System
at master 136 lines 4.1 kB view raw
1/* 2 * Copyright (c) 2020, Stijn De Ridder <stijn.deridder@hotmail.com> 3 * Copyright (c) 2022, the SerenityOS developers. 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/DeprecatedString.h> 9#include <AK/LexicalPath.h> 10#include <AK/QuickSort.h> 11#include <AK/StringBuilder.h> 12#include <AK/Vector.h> 13#include <LibCore/ArgsParser.h> 14#include <LibCore/DirIterator.h> 15#include <LibCore/System.h> 16#include <LibMain/Main.h> 17#include <errno.h> 18#include <limits.h> 19#include <stdio.h> 20#include <sys/stat.h> 21#include <unistd.h> 22 23static bool flag_show_hidden_files = false; 24static bool flag_show_only_directories = false; 25static int max_depth = INT_MAX; 26 27static int g_directories_seen = 0; 28static int g_files_seen = 0; 29 30static void print_directory_tree(DeprecatedString const& root_path, int depth, DeprecatedString const& indent_string) 31{ 32 if (depth > 0) { 33 DeprecatedString root_indent_string; 34 if (depth > 1) { 35 root_indent_string = indent_string.substring(0, (depth - 1) * 4); 36 } else { 37 root_indent_string = ""; 38 } 39 out("{}|-- ", root_indent_string); 40 } 41 42 DeprecatedString root_dir_name = LexicalPath::basename(root_path); 43 out("\033[34;1m{}\033[0m\n", root_dir_name); 44 45 if (depth >= max_depth) { 46 return; 47 } 48 49 Core::DirIterator di(root_path, flag_show_hidden_files ? Core::DirIterator::SkipParentAndBaseDir : Core::DirIterator::SkipDots); 50 if (di.has_error()) { 51 warnln("{}: {}", root_path, di.error()); 52 return; 53 } 54 55 Vector<DeprecatedString> names; 56 while (di.has_next()) { 57 DeprecatedString name = di.next_path(); 58 if (di.has_error()) { 59 warnln("{}: {}", root_path, di.error()); 60 continue; 61 } 62 names.append(name); 63 } 64 65 quick_sort(names); 66 67 for (size_t i = 0; i < names.size(); i++) { 68 DeprecatedString name = names[i]; 69 70 StringBuilder builder; 71 builder.append(root_path); 72 if (!root_path.ends_with('/')) { 73 builder.append('/'); 74 } 75 builder.append(name); 76 auto full_path = builder.to_deprecated_string(); 77 78 struct stat st; 79 int rc = lstat(full_path.characters(), &st); 80 if (rc == -1) { 81 warnln("lstat({}) failed: {}", full_path, strerror(errno)); 82 continue; 83 } 84 85 if (S_ISDIR(st.st_mode)) { 86 g_directories_seen++; 87 88 bool at_last_entry = i == names.size() - 1; 89 DeprecatedString new_indent_string; 90 if (at_last_entry) { 91 new_indent_string = DeprecatedString::formatted("{} ", indent_string); 92 } else { 93 new_indent_string = DeprecatedString::formatted("{}| ", indent_string); 94 } 95 96 print_directory_tree(full_path.characters(), depth + 1, new_indent_string); 97 } else if (!flag_show_only_directories) { 98 g_files_seen++; 99 100 outln("{}|-- {}", indent_string, name); 101 } 102 } 103} 104 105ErrorOr<int> serenity_main(Main::Arguments arguments) 106{ 107 TRY(Core::System::pledge("stdio rpath tty")); 108 109 Vector<DeprecatedString> directories; 110 111 Core::ArgsParser args_parser; 112 args_parser.add_option(flag_show_hidden_files, "Show hidden files", "all", 'a'); 113 args_parser.add_option(flag_show_only_directories, "Show only directories", "only-directories", 'd'); 114 args_parser.add_option(max_depth, "Maximum depth of the tree", "maximum-depth", 'L', "level"); 115 args_parser.add_positional_argument(directories, "Directories to print", "directories", Core::ArgsParser::Required::No); 116 args_parser.parse(arguments); 117 118 if (max_depth < 1) { 119 warnln("{}: Invalid level, must be greater than 0.", arguments.argv[0]); 120 return 1; 121 } 122 123 if (directories.is_empty()) { 124 print_directory_tree(".", 0, ""); 125 puts(""); 126 } else { 127 for (auto const& directory : directories) { 128 print_directory_tree(directory, 0, ""); 129 puts(""); 130 } 131 } 132 133 outln("{} directories, {} files", g_directories_seen, g_files_seen); 134 135 return 0; 136}