/* vi: set sw=4 ts=4: */ /* * Copyright (C) 2022 Roger Knecht * * Licensed under GPLv2, see file LICENSE in this source tree. */ //config:config TREE //config: bool "tree (0.6 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" #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; // 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, "└── "); } else { strcpy(prefix_pos, "├── "); } 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, "│   "); } 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(); 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; }