#include #include #include #include #include #include #include #include #include #include "util/versie.h" static int max_depth = -1; static bool show_hidden = false; static bool dirs_only = false; static void usage(FILE *f) { fprintf(f, "Gebruik: boom [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) { int opt; while ((opt = getopt(argc, argv, "amd:hV")) != -1) { switch (opt) { case 'a': show_hidden = true; break; case 'm': dirs_only = true; break; case 'd': max_depth = atoi(optarg); if (max_depth <= 0) { fprintf(stderr, "boom: maximale diepte moet groter zijn dan 0\n"); exit(1); } break; case 'h': usage(stdout); exit(0); case 'V': drukkedoos_print_versie(stdout); exit(0); case '?': usage(stderr); exit(1); } } return argv + optind; } 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; 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 = parentdirfd == -1 ? readlink(list[i]->d_name, linknamebuf, PATH_MAX - 1) : 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; }