diff options
-rw-r--r-- | src/boom.c | 143 |
1 files changed, 94 insertions, 49 deletions
@@ -1,13 +1,11 @@ -#include <assert.h> -#include <ftw.h> +#include <dirent.h> +#include <fcntl.h> #include <getopt.h> #include <limits.h> -#include <pwd.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> #include <unistd.h> #include "util/versie.h" @@ -23,9 +21,9 @@ static void usage(FILE *f) { "\n" "Geef inhoud van mapjes weer in een boom-achtig formaat.\n" "\n" - " -a Alle bestanden worden weergegeven, ookal beginnen ze met een puntje\n" + " -a Alle bestanden worden weergegeven, ook al beginnen ze met een puntje\n" " -m Geef alleen mapjes weer\n" - " -d Maximale diepte\n" + " -d DIEPTE Maximale diepte\n" " -h Toon deze hulptekst\n" " -V Toon versienummer\n"); } @@ -68,69 +66,116 @@ static char** parse_options(int argc, char **argv) { return argv + optind; } -static int prev_level = -1; -static int skip_level = -1; +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 int f(const char *fpath, const struct stat *, int typeflag, struct FTW *ftwbuf) { - int level = ftwbuf->level; - bool is_dir - = typeflag == FTW_D - || typeflag == FTW_DNR - || typeflag == FTW_DP; +static size_t total_dirs = 0; +static size_t total_files = 0; - if (level < skip_level) { - skip_level = -1; - } else if (skip_level != -1 && level >= skip_level) { - return 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++; - // check max depth - if (max_depth >= 0 && level >= (max_depth + 1)) return 0; + if (depth > (unsigned)max_depth) return; - const char *fname = basename(fpath); + struct dirent **list; + const int nitems = + parentdirfd == -1 + ? scandir(dirname, &list, scandir_filterfunc, alphasort) + : scandirat(parentdirfd, dirname, &list, scandir_filterfunc, alphasort); - // don't show hidden files - if (!show_hidden && fname[0] == '.' && strlen(fpath) > 2 && level != 0) { - skip_level = level + 1; - goto done; + if (nitems == -1) { + printf("%s [kon mapje niet openen]\n", dirname); + total_dirs--; // not a directory after all + encountered_error = true; + return; } - // don't show files in dirs only mode - if (dirs_only && !is_dir) return 0; - for (int i = 1; i < level; i++) { - printf("| "); + 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 (level != 0) { - printf("|-- "); + if (parentdirfd == -1) { + printf("%s\n", dirname); } - printf("%s", fname); - if (typeflag == FTW_SL) { - char real_name[PATH_MAX] = {0}; - if (readlink(fpath, real_name, PATH_MAX-1) != -1) - printf(" -> %s", real_name); + + 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]); } - printf("\n"); -done: - prev_level = level; - return 0; + close(dirfd); + + free(list); } int entry_boom(int argc, char **argv) { char **args = parse_options(argc, argv); - int flags = 0; - flags |= FTW_PHYS; - if (*args == NULL) { - nftw(".", f, 4096, flags); - return 0; + boom(-1, ".", 1); + } else for (int i = 0; args[i]; i++) { + boom(-1, args[i], 1); } - for (int i = 0; args[i]; i++) { - nftw(args[i], f, 4096, flags); - } + 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 0; + return encountered_error; } |