aboutsummaryrefslogtreecommitdiff
path: root/miscutils/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'miscutils/tree.c')
-rw-r--r--miscutils/tree.c131
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
26static 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
112int tree_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
113int 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}