diff options
Diffstat (limited to 'miscutils/tree.c')
-rw-r--r-- | miscutils/tree.c | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/miscutils/tree.c b/miscutils/tree.c new file mode 100644 index 000000000..fa55696c6 --- /dev/null +++ b/miscutils/tree.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Copyright (C) 2022 Roger Knecht <rknecht@pm.me> | ||
4 | * | ||
5 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
6 | */ | ||
7 | //config:config TREE | ||
8 | //config: bool "tree (0.6 kb)" | ||
9 | //config: default y | ||
10 | //config: help | ||
11 | //config: List files and directories in a tree structure. | ||
12 | |||
13 | //applet:IF_TREE(APPLET(tree, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
14 | |||
15 | //kbuild:lib-$(CONFIG_TREE) += tree.o | ||
16 | |||
17 | //usage:#define tree_trivial_usage NOUSAGE_STR | ||
18 | //usage:#define tree_full_usage "" | ||
19 | |||
20 | #include "libbb.h" | ||
21 | #include "common_bufsiz.h" | ||
22 | #include "unicode.h" | ||
23 | |||
24 | #define prefix_buf bb_common_bufsiz1 | ||
25 | |||
26 | static void tree_print(unsigned count[2], const char* directory_name, char* prefix_pos) | ||
27 | { | ||
28 | struct dirent **entries; | ||
29 | int index, size; | ||
30 | const char *bar = "| "; | ||
31 | const char *mid = "|-- "; | ||
32 | const char *end = "`-- "; | ||
33 | |||
34 | #if ENABLE_UNICODE_SUPPORT | ||
35 | if (unicode_status == UNICODE_ON) { | ||
36 | bar = "│ "; | ||
37 | mid = "├── "; | ||
38 | end = "└── "; | ||
39 | } | ||
40 | #endif | ||
41 | |||
42 | // read directory entries | ||
43 | size = scandir(directory_name, &entries, NULL, alphasort); | ||
44 | |||
45 | if (size < 0) { | ||
46 | fputs_stdout(directory_name); | ||
47 | puts(" [error opening dir]"); | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | // print directory name | ||
52 | puts(directory_name); | ||
53 | |||
54 | // switch to sub directory | ||
55 | xchdir(directory_name); | ||
56 | |||
57 | // print all directory entries | ||
58 | for (index = 0; index < size;) { | ||
59 | struct dirent *dirent = entries[index++]; | ||
60 | |||
61 | // filter hidden files and directories | ||
62 | if (dirent->d_name[0] != '.') { | ||
63 | int status; | ||
64 | struct stat statBuf; | ||
65 | |||
66 | //TODO: when -l is implemented, use stat, not lstat, if -l | ||
67 | status = lstat(dirent->d_name, &statBuf); | ||
68 | |||
69 | if (index == size) { | ||
70 | strcpy(prefix_pos, end); | ||
71 | } else { | ||
72 | strcpy(prefix_pos, mid); | ||
73 | } | ||
74 | fputs_stdout(prefix_buf); | ||
75 | |||
76 | if (status == 0 && S_ISLNK(statBuf.st_mode)) { | ||
77 | // handle symlink | ||
78 | char* symlink_path = xmalloc_readlink(dirent->d_name); | ||
79 | printf("%s -> %s\n", dirent->d_name, symlink_path); | ||
80 | free(symlink_path); | ||
81 | count[1]++; | ||
82 | } else if (status == 0 && S_ISDIR(statBuf.st_mode) | ||
83 | && (prefix_pos - prefix_buf) < (COMMON_BUFSIZE - 16) | ||
84 | ) { | ||
85 | // handle directory | ||
86 | char* pos; | ||
87 | if (index == size) { | ||
88 | pos = stpcpy(prefix_pos, " "); | ||
89 | } else { | ||
90 | pos = stpcpy(prefix_pos, bar); | ||
91 | } | ||
92 | tree_print(count, dirent->d_name, pos); | ||
93 | count[0]++; | ||
94 | } else { | ||
95 | // handle file | ||
96 | puts(dirent->d_name); | ||
97 | count[1]++; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | // release directory entry | ||
102 | free(dirent); | ||
103 | } | ||
104 | |||
105 | // release directory array | ||
106 | free(entries); | ||
107 | |||
108 | // switch to parent directory | ||
109 | xchdir(".."); | ||
110 | } | ||
111 | |||
112 | int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
113 | int tree_main(int argc UNUSED_PARAM, char **argv) | ||
114 | { | ||
115 | unsigned count[2] = { 0, 0 }; | ||
116 | |||
117 | setup_common_bufsiz(); | ||
118 | init_unicode(); | ||
119 | |||
120 | if (!argv[1]) | ||
121 | *argv-- = (char*)"."; | ||
122 | |||
123 | // list directories given as command line arguments | ||
124 | while (*(++argv)) | ||
125 | tree_print(count, *argv, prefix_buf); | ||
126 | |||
127 | // print statistic | ||
128 | printf("\n%u directories, %u files\n", count[0], count[1]); | ||
129 | |||
130 | return EXIT_SUCCESS; | ||
131 | } | ||