aboutsummaryrefslogtreecommitdiff
path: root/sys/libn
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2020-06-18 19:46:25 -0700
committerNicholas Noll <nbnoll@eml.cc>2020-06-18 19:46:25 -0700
commitd62e5c1df92ee159d0203b75adebb50faf45badb (patch)
tree35536a0da777d56e4233c30b2f66658a6f5b46fc /sys/libn
parent425ef692da7e74112f88f0b368f3286dba84f846 (diff)
feat: small filesystem walker added
Diffstat (limited to 'sys/libn')
-rw-r--r--sys/libn/arg.c1
-rw-r--r--sys/libn/fs.c202
2 files changed, 203 insertions, 0 deletions
diff --git a/sys/libn/arg.c b/sys/libn/arg.c
new file mode 100644
index 0000000..64e4dd6
--- /dev/null
+++ b/sys/libn/arg.c
@@ -0,0 +1 @@
+char *argv0;
diff --git a/sys/libn/fs.c b/sys/libn/fs.c
new file mode 100644
index 0000000..5d06997
--- /dev/null
+++ b/sys/libn/fs.c
@@ -0,0 +1,202 @@
+#include <u.h>
+#include <libn.h>
+#include <libn/macro/map.h>
+#include <dirent.h>
+
+/*
+ * path history
+ */
+
+struct Key
+{
+ ino_t ino;
+ dev_t dev;
+};
+
+static
+void*
+xalloc(void *_, int n, uintptr size)
+{
+ return malloc(n*size);
+}
+
+static
+void
+xfree(void *_, void *ptr)
+{
+ return free(ptr);
+}
+
+#define hash(k) ((int32)k.ino ^ (int32)k.dev)
+#define equal(k1, k2) (k1.ino == k2.ino && k1.dev == k2.dev)
+
+struct fs·History
+{
+ SET_STRUCT_BODY(struct Key);
+};
+
+static
+int
+morehistory(fs·History *h, int n)
+{
+ SET_GROW(h, struct Key, n, hash, xalloc, xfree, nil);
+}
+
+static
+int
+addentry(fs·History *h, struct Key key, int *err)
+{
+ SET_PUT(h, key, hash, equal, morehistory, err);
+}
+
+static
+void
+forget(fs·History *h)
+{
+ if (!h)
+ return;
+
+ SET_RESET(h);
+}
+
+static
+void
+delete(fs·History *h)
+{
+ SET_FREE(h, xfree, nil);
+}
+
+#undef hash
+#undef equal
+
+static
+char *
+strcpyn(char *dst, char *src, int n)
+{
+ while(*src && n-- > 0)
+ *dst++ = *src++;
+
+ *dst = 0;
+ return dst;
+}
+
+/*
+ * main functions
+ */
+
+int
+fs·init(fs·Walker *fs, char *path)
+{
+ fs->base = fs->end = fs->path;
+
+ if (!path || !path[0]) {
+ path = getcwd(fs->path, arrlen(fs->path));
+ if (!path)
+ return 1;
+ fs->end += strlen(path);
+ } else
+ fs->end = strcpyn(fs->base, path, arrlen(fs->path));
+
+ if (fs->path[0] != '/')
+ fs->fd = AT_FDCWD;
+
+ if (!fs->hist && !(fs->flags & fs·nolinks))
+ fs->hist = calloc(1, sizeof(*fs->hist));
+
+ return 0;
+}
+
+void
+fs·fini(fs·Walker *fs)
+{
+ if (fs->hist) {
+ delete(fs->hist);
+ free(fs->hist);
+ }
+}
+
+void
+fs·walk(fs·Walker *fs)
+{
+ char *e, *b;
+ DIR *dir;
+ int new, fd, ofd, flags;
+ fs·History *h;
+ struct dirent *d;
+ io·Stat cwd;
+ struct fs·Entry *it;
+
+ flags = 0;
+ if(fs->flags & fs·nolinks)
+ flags |= AT_SYMLINK_NOFOLLOW;
+
+ /* get info for base relative to current fd */
+ if(fstatat(fs->fd, fs->base, &cwd, flags) < 0){
+ if(fs->flags & fs·verbose)
+ errorf("stat: %s", fs->path);
+ return;
+ }
+
+ /* if we hit a file, finish! */
+ if(!S_ISDIR(cwd.st_mode)) {
+ fs->func(fs->data, fs->base, fs->path, &cwd);
+ return;
+ }
+
+ /* have we been here before? (cycle detection) */
+ /* if not, add to our path history */
+ if (!(fs->flags & fs·nolinks)) {
+ addentry(fs->hist, (struct Key){.dev=cwd.st_dev, .ino=cwd.st_ino}, &new);
+ if (!new)
+ return;
+ }
+
+ /* operate on directory if preorder traversal */
+ if (fs->flags & fs·preorder)
+ fs->func(fs->data, fs->base, fs->path, &cwd);
+
+ /* open directory */
+ if(!fs->max || fs->lev + 1 < fs->max) {
+ fd = openat(fs->fd, fs->base, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
+ if (fd < 0)
+ errorf("open %s:", fs->path);
+
+ if (!(dir=fdopendir(fd))) {
+ if(fs->flags & fs·verbose)
+ errorf("fdopendir: %s", fs->path);
+ return;
+ }
+
+ ofd = fs->fd, fs->fd = fd;
+
+ /* traverse children */
+ e = fs->end, b = fs->base;
+ if (fs->end[-1] != '/')
+ *fs->end++ = '/';
+
+ fs->base = fs->end;
+ while((d = readdir(dir))) {
+ if (*d->d_name == '.')
+ if (d->d_name[1] == 0 || /* . */
+ (d->d_name[1] == '.' && d->d_name[2] == 0)) /* .. */
+ continue;
+
+ fs->end = strcpyn(fs->base, d->d_name, arrend(fs->path) - fs->base);
+
+ fs->lev++;
+ fs·walk(fs);
+ fs->lev--;
+ }
+ *e = 0;
+ fs->fd = ofd;
+ fs->end = e, fs->base = b;
+ closedir(dir);
+ }
+
+ /* operate on directory if postorder (default) traversal */
+ if (!(fs->flags & fs·preorder))
+ fs->func(fs->data, fs->base, fs->path, &cwd);
+
+ if (!fs->lev)
+ forget(fs->hist);
+}