summaryrefslogtreecommitdiff
path: root/src/boom.c
diff options
context:
space:
mode:
authorTom Smeding <tom@tomsmeding.com>2024-09-08 18:25:40 +0200
committerTom Smeding <tom@tomsmeding.com>2024-09-08 18:26:03 +0200
commit43d5a40971b825cb78ce9ff56211c0c021109d07 (patch)
tree9a6e3dcceb41d40f28dd1e7deb42301dd01493e2 /src/boom.c
parentac5cad753c7dbac31756a2fffd85625b1431eaa5 (diff)
boom: unicode takjes
Diffstat (limited to 'src/boom.c')
-rw-r--r--src/boom.c143
1 files changed, 94 insertions, 49 deletions
diff --git a/src/boom.c b/src/boom.c
index c1bd164..d4829ef 100644
--- a/src/boom.c
+++ b/src/boom.c
@@ -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;
}