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;
}
|