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