#include #include #include #include #include #include #include #include #include #include #include "util/versie.h" #include "util/option.h" static int max_depth = -1; static bool show_hidden = false; static bool dirs_only = false; static const char *usage_string = "Gebruik: %s [OPTIES]... [STARTPUNTEN]...\n" "\n" "Geef inhoud van mapjes weer in een boom-achtig formaat.\n" "\n" " -a Alle bestanden worden weergegeven, ook al beginnen ze met een puntje\n" " -m Geef alleen mapjes weer\n" " -d DIEPTE Maximale diepte\n" " -h Toon deze hulptekst\n" " -V Toon versienummer\n"; // Returns pointer to argument array containing the starting points static char** parse_options(int argc, char **argv) { char *max_depth_str; const struct option_spec spec[] = { {'a', OPTION_SETBOOL(&show_hidden)}, {'m', OPTION_SETBOOL(&dirs_only)}, {'d', OPTION_WITHARG(&max_depth_str)}, {'h', OPTION_HELPUSAGE(usage_string)}, {'V', OPTION_VERSION()}, OPTION_SPEC_END }; char **rest = option_parse(argc, argv, spec); if (max_depth_str != NULL) { errno = 0; max_depth = strtol(max_depth_str, NULL, 10); if (errno != 0) { fprintf(stderr, "boom: maximale diepte moet een getal zijn\n"); exit(1); } if (max_depth <= 0) { fprintf(stderr, "boom: maximale diepte moet groter zijn dan 0\n"); exit(1); } } return rest; } static int scandir_filterfunc(const struct dirent *ent) { if (dirs_only && ent->d_type != DT_DIR) return 0; if (!show_hidden && ent->d_name[0] == '.') return 0; if (strcmp(ent->d_name, ".") == 0) return 0; if (strcmp(ent->d_name, "..") == 0) return 0; return 1; } static const char *branches[] = { "├── ", "└── ", "│   ", " " }; // Contains indices into 'branches' static int8_t prefix[PATH_MAX / 2 + 1]; // Buffer to store the target of a symlink in static char linknamebuf[PATH_MAX]; static size_t total_dirs = 0; static size_t total_files = 0; static bool encountered_error = false; static void print_prefix(int depth) { for (int i = 0; i < depth; i++) { printf("%s", branches[prefix[i]]); } } // Pass -1 as parentdirfd if dirname is to be resolved relative to $PWD or is absolute. static void boom(int parentdirfd, const char *dirname, unsigned depth) { total_dirs++; if (depth > (unsigned)max_depth) return; struct dirent **list; const int nitems = parentdirfd == -1 ? scandir(dirname, &list, scandir_filterfunc, alphasort) : scandirat(parentdirfd, dirname, &list, scandir_filterfunc, alphasort); if (nitems == -1) { printf("%s [kon mapje niet openen]\n", dirname); total_dirs--; // not a directory after all encountered_error = true; return; } const int dirfd = parentdirfd == -1 ? open(dirname, O_DIRECTORY) : openat(parentdirfd, dirname, O_DIRECTORY); if (dirfd == -1) { printf("%s [kon mapje niet open(2)en]\n", dirname); total_dirs--; // not a directory after all encountered_error = true; return; } if (parentdirfd == -1) { printf("%s\n", dirname); } for (int i = 0; i < nitems; i++) { if (i < nitems - 1) prefix[depth-1] = 0; else prefix[depth-1] = 1; print_prefix(depth); if (list[i]->d_type == DT_LNK) { ssize_t len = readlinkat(dirfd, list[i]->d_name, linknamebuf, PATH_MAX - 1); if (len == -1) { printf("%s -> [kon niet leeslinken]\n", list[i]->d_name); } else { linknamebuf[len] = '\0'; printf("%s -> %s\n", list[i]->d_name, linknamebuf); } } else printf("%s\n", list[i]->d_name); if (list[i]->d_type == DT_DIR) { if (i < nitems - 1) prefix[depth-1] = 2; else prefix[depth-1] = 3; boom(dirfd, list[i]->d_name, depth + 1); } else { total_files++; } free(list[i]); } close(dirfd); free(list); } int entry_boom(int argc, char **argv) { char **args = parse_options(argc, argv); if (*args == NULL) { boom(-1, ".", 1); } else for (int i = 0; args[i]; i++) { boom(-1, args[i], 1); } putchar('\n'); printf("%zu map%s", total_dirs, total_dirs == 1 ? "" : "jes"); if (!dirs_only) printf(", %zu bestand%s", total_files, total_files == 1 ? "" : "en"); putchar('\n'); return encountered_error; }